全国大学生电子设计竞赛(三)--SPWM与PID

干货福利,第一时间送达!

前两天回学校拿了毕业证和学位证,有几天没有更新了所以今天继续。听说今年的电赛还是会举行所以这些肯定是有帮助了,SPWM和PID的资料在网上一搜一把大,正点原子和野火的例程里面也有关于这方面的程序代码。关于SPWM的知识大家可以在CSDN还有知乎上面看一看,而PID方面的知识在B站上有很多,最经典的就是育科技胥老师的pid视频教程了,讲解的非常详细。说归说理论知识还是少不了,说不定到时候你写技术报告时这些还具有一些参考价值呢对不对

1  SPWM波调制技术

  逆变电路的控制方式主要是采用SPWM(正弦脉宽调制技术),IR2104控制开关管的通断来实现正弦调制,SPWM的基本思路是将一个正弦波按等宽间距分成N等份,对于每一个波形以一个等面积的脉冲来对应,使脉冲的中点与相应正弦波部分的中点重合,如下图所示,由于此脉冲序列的面积分布满足正弦规率,根据面积等效原理,将这个脉冲序列输出至负载时,将使负载得到相当接近正弦的输出电压和电流。

SPWM示意图

  由于采用该方式输出的电压波形很接近正弦波,所以电压中的谐波成分较少,同时也可以提高功率因数。通过改变SPWM脉冲的宽度可以改变输出电压的幅值,调节电路的调制周期则可以改变输出电压的频率,方便对负载进行控制。

1.1  单极性正弦脉宽调制

  单极性正弦脉宽调制是以一个宽度正弦规律变化的正弦脉冲序列对应交流输出波形的正半周,再以一个宽度按正弦规率变换的负脉冲序列对应交流输出波形的负半周,这两个脉冲序列交替作用,控制开关器件产生近似于正弦波的输出电压波形。
  正弦脉宽调制的基本方法是将正弦波性的调制波与三角波形的载波进行比较,通过比较正弦波形各点的瞬时值确定该点对应的脉冲宽度。单极性正弦脉宽调制使用的三角波是单极性的,在正弦调制波为正半周时三角载波是正极性的;在正弦波为负半周时三角波载波为负极性。如下图所示。

单相桥式逆变电路
单相桥式逆变电路

1.2  双极性正弦脉宽调制

  双极性正弦脉宽调制是以一个宽度按正弦规率变化的正负双向脉冲序列对应交流输出波形的整个周期,通过正向脉冲与负向脉冲宽度的差产生出按正弦规率变换的正半周波和负半周波。
  双极性正弦脉宽调制使用的三角波是双极性的,其波形的形式如下图,通过正弦调制波与三角波比较,当正弦调制波值大于三角波时,输出正脉冲;而当正弦调制波的值小于三角载波时,输出负脉冲。如此得到的脉冲序列是正负交替的双向脉冲序列。

双极性正弦脉宽调制

1.3  三相正弦脉宽调制

  三相正弦脉宽调制可以使用三个相角彼此相差120°的单相调制电路来合成,但是这样会使调制电路结构比较复杂。较简单的方法是采用一个双极性的三角载波对三相正弦调制波进行调制,其调制的波形如下图。
  调制过程中,双极性三角载波为三个正弦调制波共用,分别进行比较后获取脉宽调制信号,方式与双极性正弦脉宽调制方法相同。从而得到三个双向的脉冲序列,分别对应A、B、C三相,使用此三个脉冲序列控制逆变电路的A、B、C三相开关元件,可以将直流电压逆变为正弦波的三相交流输出电压。

三相正弦脉宽调制电路
三相正弦脉宽调制信号的产生

  三相脉宽调制波的三角载波只能是双极性的,因为单极性三角载波需要根据正弦调制波的正、负半周更换载波的极性。采用双极性正弦脉宽调制,H桥上同一相的上、下两个桥臂导通与截止都是互补的,为防止上下两个桥臂直通而造成的短路,需要在给一个桥臂施加关断信号后延迟一段时间,从而在波形中引入了死区时间,死区将会给输出的SPWM波形带来高次谐波。

1.4  SPWM控制信号的生成

  早期的SPWM采用模拟控制方式来实现,通过信号发生器产生所需的信号,由比较器进行信号之间的比较,随着数字技术和微处理器在SPWM逆变技术中的应用,采用一定算法产生SPWM的数字控制方式越来越广泛。

1.4.1  模拟控制方式

  波形比较法,其基本的方法是由正弦信号发生器件产生正弦调制波,由三角波信号发发生器产生的三角载波,将正弦调制波与三角载波比较,通过比较器的判断而产生出对应的脉冲信号序列,对逆变电路进行控制从而得到所需的交流电压。

1.4.2  数字控制方式

(1)等效面积法
  其原理就是按面积等效原理构成与正弦波等效的一系列等幅但宽度按正弦规律变化的矩形脉冲。等效面积法适用于单极性控制,算法中计算的是正弦波形到横轴间的面积,这与模拟控制方式中的单极性正弦脉冲调制的方式一致。
(2)自然采样法与规则采样法
  自然采样法在算法上仿真模拟控制方式的双极性正弦脉冲调制,通过计算正弦调制波与三角载波的交点位置,确定调制的脉冲宽度。而规则采样法是对自然采样法的简化,其几何关系如下图。

  规则采样法适用于双极性控制,因为算法中采用的三角载波是双极性。等效面积法和规则采样法都是数字控制的算法,可以由微处理器实时计算SPWM脉冲的宽度和位置,实现对逆变电路的控制,也可以事先计算好每个脉冲中心位置和脉冲宽度存入微处理器中,以查表的方式实现对逆变电路的控制。
  此外除了使用微处理器生成SPWM以外,目前还有专门产生SPWM波形的大规模集成芯片,有些微处理器也继承有SPWM波形发生器,如STM32微处理器,其内部有三相互补SPWM波形发生器,可以直接输出6路SPWM波形信号。

1.5.SPWM软件生成

利用单片机输出PWM波,然后让占空比正弦规律变化。
实现步骤可以简单分为三步,以stm32为例:
(1)生成载波。比如要生成一个10KHZ的三角波,将计数器设置加减计数、周期设为1/10K就ok啦。这样生成的三角波的幅值是多少呀,3.3V?其实在单片机里面都是数字信号,三角波最高点的时候可以用一个计数值来表示,比如8400,最低点是0。不用管他的电压是多少。
(2)生成正弦波。这一步用软件生成一个正弦表即可。比如将正弦波取200个点,即将一个正弦分割成200份,每个点代表一个幅值。用离散的数字量表达正弦模拟量。
(3)将正弦波和三角波进行比较。
a.什么时候进行比较。设置计数值达到比较值产生动作。
b.比较完之后,需要改变比较值,用于下一个周期进行比较(比较值可以理解为占空比),比较值查正弦表获得,这样就生成了占空比正弦规律变化的SPWM。
C.调制度m。m=正弦表最大值/三角波最大计数值。如正弦表最大值4200,三角波最大计数值8400,m=4200/8400=0.5,此时spwm最大占空比为50%,设置m=1,spwm最大占空比为100%。

例如我们要生成一个开环的三相spwm。单相逆变需生成两组相差180度的spwm,三相即生成三组两两相差120度的spwm,且每组的spwm都是互补的。将生成的三组spwm分别加到1、2、3组桥臂,每组spwm互补防止了同一桥臂上下管同时导通会引起短路。

int const talab[250]=
{
 100 , 102 , 108 , 116 , 126 , 140 , 154 , 172 , 194 , 216 ,
 242 , 270 , 300 , 334 , 370 , 408 , 448 , 490 , 536 , 582 ,
 632 , 684 , 738 , 794 , 854 , 914 , 976 ,1040 ,1108 ,1176 ,
1246 ,1320 ,1394 ,1470 ,1548 ,1626 ,1708 ,1790 ,1874 ,1960 ,
2046 ,2136 ,2224 ,2316 ,2408 ,2502 ,2596 ,2690 ,2786 ,2884 ,
2982 ,3080 ,3180 ,3280 ,3382 ,3482 ,3584 ,3686 ,3788 ,3892 ,
3994 ,4096 ,4200 ,4304 ,4406 ,4508 ,4612 ,4714 ,4816 ,4918 ,
5018 ,5120 ,5220 ,5320 ,5418 ,5516 ,5614 ,5710 ,5804 ,5898 ,
5992 ,6084 ,6176 ,6264 ,6354 ,6440 ,6526 ,6610 ,6692 ,6774 ,
6852 ,6930 ,7006 ,7080 ,7154 ,7224 ,7292 ,7360 ,7424 ,7486 ,
7546 ,7606 ,7662 ,7716 ,7768 ,7818 ,7864 ,7910 ,7952 ,7992 ,
8030 ,8066 ,8100 ,8130 ,8158 ,8184 ,8206 ,8228 ,8246 ,8260 ,
8274 ,8284 ,8292 ,8298 ,8300 ,8300 ,8298 ,8292 ,8284 ,8274 ,
8260 ,8246 ,8228 ,8206 ,8184 ,8158 ,8130 ,8100 ,8066 ,8030 ,
7992 ,7952 ,7910 ,7864 ,7818 ,7768 ,7716 ,7662 ,7606 ,7546 ,
7486 ,7424 ,7360 ,7292 ,7224 ,7154 ,7080 ,7006 ,6930 ,6852 ,
6774 ,6692 ,6610 ,6526 ,6440 ,6354 ,6264 ,6176 ,6084 ,5992 ,
5898 ,5804 ,5710 ,5614 ,5516 ,5418 ,5320 ,5220 ,5120 ,5018 ,
4918 ,4816 ,4714 ,4612 ,4508 ,4406 ,4304 ,4200 ,4096 ,3994 ,
3892 ,3788 ,3686 ,3584 ,3482 ,3382 ,3280 ,3180 ,3080 ,2982 ,
2884 ,2786 ,2690 ,2596 ,2502 ,2408 ,2316 ,2224 ,2136 ,2046 ,
1960 ,1874 ,1790 ,1708 ,1626 ,1548 ,1470 ,1394 ,1320 ,1246 ,
1176 ,1108 ,1040 , 976 , 914 , 854 , 794 , 738 , 684 , 632 ,
 582 , 536 , 490 , 448 , 408 , 370 , 334 , 300 , 270 , 242 ,
 216 , 194 , 172 , 154 , 140 , 126 , 116 , 108 , 102 , 100 
};

uint16_t Counter_sine1 = 0; //A相
uint16_t Counter_sine2 = 83; //滞后A相120度
uint16_t Counter_sine3 = 166;//超前A相120度
/*TIM1GPIO初始化  
CH1--A8   CH2--A9   CH3--A10
CH1N-B13  CH2N-B14  CH3N-B15 
*/
void TIM1_GPIO_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8 | GPIO_Pin_9| GPIO_Pin_10;  
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;       //复用推挽输出              
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_13| GPIO_Pin_14 | GPIO_Pin_15;  
  GPIO_Init(GPIOB, &GPIO_InitStructure);                                                  
}
#define CKTIM       ((u32)72000000uL)  //主频
#define PWM_PRSC    ((u8)0)            //TIM1分频系数
#define PWM_FREQ    ((u16) 10000)      //PWM频率(Hz)
#define PWM_PERIOD  ((u16) (CKTIM / (u32)(2 * PWM_FREQ *(PWM_PRSC+1))))
#define MODULAT     (float)0.7           //调制度
void TIM1_Mode_Config(void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;
    TIM_BDTRInitTypeDef TIM1_BDTRInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD; //计数周期
    TIM_TimeBaseStructure.TIM_Prescaler = PWM_PRSC;//分频系数
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV2;       
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1; 
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //配置为PWM模式1
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  //使能CHx的PWM输出
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;//互补输出使能,使能CHxN的PWM输出
    TIM_OCInitStructure.TIM_Pulse = 0;                          
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;   
    TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;   
    TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;  
    TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set; 
    TIM_OC1Init(TIM1, &TIM_OCInitStructure);//配置CH1
TIM_OCInitStructure.TIM_Pulse = 0;                         
    TIM_OC2Init(TIM1, &TIM_OCInitStructure); //配置CH2
    TIM_OCInitStructure.TIM_Pulse = 0;                         
    TIM_OC3Init(TIM1, &TIM_OCInitStructure);//配置CH3

//死区时间
    TIM1_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable; 
    TIM1_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
    TIM1_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;   
    TIM1_BDTRInitStructure.TIM_DeadTime = 360; //设置死区时间
    TIM1_BDTRInitStructure.TIM_Break = TIM_Break_Disable;        
    TIM1_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;         
    TIM1_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable;  
    TIM_BDTRConfig(TIM1, &TIM1_BDTRInitStructure);

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //4个抢先级、4个子优先级    
    NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    TIM_ITConfig(TIM1,TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3,ENABLE);  //使能中断

TIM_CtrlPWMOutputs(TIM1, ENABLE);  //PWM输出使能    
    TIM_Cmd(TIM1, ENABLE); //使能TIM1
}
void TIM1_PWM_Init(void)
{
    TIM1_GPIO_Config();
    TIM1_Mode_Config();
}

//定时器1中断服务函数
void TIM1_CC_IRQHandler(void) 
{    
    if(Counter_sine1>=250)  { Counter_sine1 = 0; }
    if(Counter_sine2>=250)  { Counter_sine2 = 0; }
    if(Counter_sine3>=250)  { Counter_sine3 = 0; }
    if (TIM_GetITStatus(TIM1, TIM_IT_CC1)!=RESET)
    {
        TIM_SetCompare1(TIM1,(uint32_t)(talab[Counter_sine1])*MODULAT); //A相    
        Counter_sine1++;
        TIM_ClearITPendingBit(TIM1 , TIM_IT_CC1);
     }
    if (TIM_GetITStatus(TIM1, TIM_IT_CC2) !=RESET)
    {
        TIM_SetCompare2(TIM1,((uint32_t)talab[Counter_sine2])*MODULAT); //B相
        Counter_sine2++;
        TIM_ClearITPendingBit(TIM1 , TIM_IT_CC2);   
    }
    if (TIM_GetITStatus(TIM1, TIM_IT_CC3) !=RESET)
    {
        TIM_SetCompare3(TIM1,(uint32_t)(talab[Counter_sine3])*MODULAT); //C相
        Counter_sine3++;
        TIM_ClearITPendingBit(TIM1 , TIM_IT_CC3);   
    }
}

TIM1设置为中央计数模式,开启互补通道,设置死区时间,死区时间是多少个时钟计数周期,比如TIM1计数周期是72M,设置为72就是1000ns。spwm频率设置为10k,然后TIM1每个通道的比较值达到时更新比较值。调制度m范围为0~1,设为0.7。正弦调制波的频率是自己设置的,方法是f=载波频率/表中点数,这里设置的是载波频率10K,取250个点得到的正弦频率就是40HZ,改变载波频率为50*250就得到了50hz的正弦波输出。正弦表可以用取点工具。

2  PID算法

  在连续时间控制系统中,PID控制器应用非常广泛,其设计技术成熟,长期以来形成了典型的结构,参数整定方便,结构更改灵活。由于计算机程序的灵活性,数字PID控制比连续PID控制更为优越。

2.1  PID控制系统简介

  连续时间PID控制系统如下图所示,D(s)完成PID控制规律,称为PID控制器。PID控制器是一种线性控制器,用输出量y(t)和给定量r(t)之间的误差的时间函数e(t)=r(t)-y(t)的比例、积分和微分的线性组合构成控制量u(t),称为比例、积分、微分控制,简称PID控制。

连续时间PID控制系统

  PID控制组合了比例控制、积分控制和微分控制这三种基本控制规律,通过改变调节器参数来实现控制,其基本输入输出关系为:

2.2  PID参数控制效果分析

  PID控制的三基本参数为KP、KI、KD,分别对应比例,积分,微分三个模,经实验测试,可总结出这三项参数的实际控制作用为:
  比例调节参数(KP):按比例反映系统的偏差,系统一旦出现偏差,比例调节立即进行。比例调节是主要的控制部分,但过大的比例会使系统的稳定性下降。增大KP,系统的反应变灵敏、速度加快、稳态误差减小,但振荡次数也会加多、调节时间加长。
  积分调节参数(KI):消除系统静态(稳态)误差 ,提高系统的控制精度。积分调节会使系统的稳定性下降,动态响应变慢,超调加大。积分控制一般不单独作用,而是与P或者PD结合作用。
  微分调节参数(KD):反映系统偏差信号的变化率,可以预见偏差的变化趋势,产生超前控制作用,使偏差在未形成前已被消除。因此,微分控制可以提高系统的动态跟踪性能,减小超调量,但对噪声干扰有放大作用,过强的微分调节会使系统剧烈震荡,对抗干扰不利。
  常规的PID控制系统中,减少超调和提高控制精度难以两全其美。主要是积分作用有缺陷造成的。如果减少积分作用,静差不易消除,有扰动时,消除误差速度变慢;而加强积分作用时又难以避免超调,这也是常规PID控制中经常遇到的难题。

PID

2.3 通俗易懂的理解PID

  P就是比例的意思。它的作用最明显,原理也最简单。
  需要控制的量,比如水温,有它现在的『当前值』,也有我们期望的『目标值』。当两者差距不大时,就让加热器“轻轻地”加热一下。要是因为某些原因,温度降低了很多,就让加热器“稍稍用力”加热一下。要是当前温度比目标温度低得多,就让加热器“开足马力”加热,尽快让水温到达目标附近。
  这就是P的作用,跟开关控制方法相比,是不是“温文尔雅”了很多.实际写程序时,就让偏差(目标减去当前)与调节装置的“调节力度”,建立一个一次函数的关系,就可以实现最基本的“比例”控制了~
  KP越大,调节作用越激进,KP调小会让调节作用更保守。
  要是你正在制作一个平衡车,有了KP的作用,你会发现,平衡车在平衡角度附近来回“狂抖”,比较难稳住。如果已经到了这一步——恭喜你!离成功只差一小步了~

  D的作用更好理解一些,所以先说说D,最后说i。
  刚才我们有了P的作用。你不难发现,只有P好像不能让平衡车站起来,水温也控制得晃晃悠悠,好像整个系统不是特别稳定,总是在“抖动”。

  你心里设想一个弹簧:现在在平衡位置上。拉它一下,然后松手。这时它会震荡起来。因为阻力很小,它可能会震荡很长时间,才会重新停在平衡位置。
  请想象一下:要是把上图所示的系统浸没在水里,同样拉它一下 :这种情况下,重新停在平衡位置的时间就短得多。我们需要一个控制作用,让被控制的物理量的“变化速度”趋于0,即类似于“阻尼”的作用。
  因为,当比较接近目标时,P的控制作用就比较小了。越接近目标,P的作用越温柔。有很多内在的或者外部的因素,使控制量发生小范围的摆动。D的作用就是让物理量的速度趋于0,只要什么时候,这个量具有了速度,D就向相反的方向用力,尽力刹住这个变化。kD参数越大,向速度相反方向刹车的力道就越强。
  如果是平衡小车,加上P和D两种控制作用,如果参数调节合适,它应该可以站起来了~欢呼吧
  等等,PID三兄弟好想还有一位。看起来PD就可以让物理量保持稳定,那还要I干嘛?

  因为我们忽视了一种重要的情况:
  还是以热水为例。假如有个人把我们的加热装置带到了非常冷的地方,开始烧水了。需要烧到50℃。
  在P的作用下,水温慢慢升高。直到升高到45℃时,他发现了一个不好的事情:天气太冷,水散热的速度,和P控制的加热的速度相等了。这可怎么办?
  P兄这样想:我和目标已经很近了,只需要轻轻加热就可以了。
  D兄这样想:加热和散热相等,温度没有波动,我好像不用调整什么。
  于是,水温永远地停留在45℃,永远到不了50℃。
  作为一个人,根据常识,我们知道,应该进一步增加加热的功率。可是增加多少该如何计算呢?
  前辈科学家们想到的方法是真的巧妙。设置一个积分量。只要偏差存在,就不断地对偏差进行积分(累加),并反应在调节力度上。这样一来,即使45℃和50℃相差不太大,但是随着时间的推移,只要没达到目标温度,这个积分量就不断增加。
  系统就会慢慢意识到:还没有到达目标温度,该增加功率啦!到了目标温度后,假设温度没有波动,积分值就不会再变动。这时,加热功率仍然等于散热功率。但是,温度是稳稳的50℃。
  Ki的值越大,积分时乘的系数就越大,积分效果越明显。所以,I的作用就是,减小静态情况下的误差,让受控物理量尽可能接近目标值。
  I在使用时还有个问题:需要设定积分限制。防止在刚开始加热时,就把积分量积得太大,难以控制。

2.4  数字PID控制的实现

  数字PID控制是通过算法程序实现PID控制的。数字控制系统大多数是采样数据控制系统,进入系统的连续时间信号必须经过采样和量化后转换为数字量,方能进行相应的计算和处理,不论是积分还是微分,只能用数值计算去逼近。当采样周期相当短时,用求和代替积分,用差商代替微商,将描述连续时间PID算法的微分方程变为描述离散时间PID算法的差分方程。数字PID控制通常有以下两种实现:

2.4.1  位置式PID控制算法

  此式是数字PID算法的非递推形式,称全量算法,其中uo为控制量基值(n=0时的控制量);un为第n个采样时刻的控制量;Ts为采样周期。算法中为实现求和,必须存储系统偏差的全部值ei,得出的全量输出un是控制量的绝对数值,这种控制量确定了执行机构在控制系统中的位置(如阀门控制中,输出对应阀门的位置),因此将这种算法称为“位置算法”。

位置式PID控制算法实现框图

2.4.2  增量式PID控制算法

  当执行机构需要的不是控制量的绝对值,而是控制量的增量(例如驱动步进电动机)时,需要用PID的“增量算法”,其简化示意图如下图所示。

增量式PID控制算法简化示意图

  增量式PID算法的差分方程为

  此时已看不出P、I、D作用的直接关系,只能体现各次误差量对控制作用的影响,但处理时只需要贮存最近的三个误差采样值en en-1 en-2 。

(0)

相关推荐