数字PID方案(二)
数字控制方案(一)算法简单,实现直观,而且在100us的调节周期下,已经能够获得很平稳的输出了。可是现在还是想让调节更智能一些。因为使用方案(一),即使MCU知道当前采集到的输出值并不正确,但是它没有途径去更改,因为它走的是一条单行道。指令给的是多少,反馈引脚加的就是多少,执行方案(一)的MCU会想,反正我按照指令去执行了,即使结果不正确,也不关我的事了。
如果要根据输出来调整输入,就需要引入数字PID方案。这就引出了数字控制方案(二),它采用了增量式的数字PID算法。增量式数字PID算法的用途非常广泛,几乎能想到的控制,都可以用它来实现,最常见的如直流电机的速度闭环。
对于LM2677的电路来说,设定值就是用户输入的理想输出电压,输出值就是LM2677的实际输出电压,控制量就是加在LM2677反馈引脚上的DAC输出值。LM2677的MCU程序需要实现的功能就是,采集LM2677的实际输出电压,和用户输入的理想输出电压比较,根据比较出来的偏差,调整LM2677反馈引脚上的DAC输出值,直到使实际输出电压等于理想输出电压。
100us的调节时间,用定时器TIM2的溢出中断来实现,但是PID调节的计算并不在中断服务程序中。TIM2_IRQHandler仅仅做了一件事情,就是启动一次ADC扫描。扫描完成之后,ADC的数据会经由DMA通道传输到指定地址的数组中,DMA1_Channel1_IRQHandler是DMA中断,当需要扫描的ADC通道都转换完成之后,DMA中断被触发,在这里进行PID运算。
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) != RESET) {
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
auu.dmaFlag = TRUE;
adc_startScan();
}
}
void DMA1_Channel1_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC1) != RESET) {
DMA_ClearITPendingBit(DMA1_IT_TC1);
if (auu.dmaFlag == TRUE) {
auu.dmaFlag = FALSE;
regular_pid();
}
}
}
PID为数字增量式,buu.vol_set是用户给定的输出电压值,buu.vol_out为实际采回来的输出电压值,auu.voltageOut为ADC的运算结果,auu.voltageFeed为DAC的给定结果。
struct __pid {
int32_t vol_set;
int32_t vol_out;
int32_t vol_feed;
int32_t vol_preError;
int32_t vol_preDError;
int32_t vol_P;
int32_t vol_I;
int32_t vol_D;
int32_t deadline;
};
struct __pid buu;
uint8_t regular_pid(void)
{
int32_t i = 0;
int32_t error_t, derror_t, dderror_t;
for (auu.voltageOut = 0, i = 2; i < ADC_NUM; i++)
auu.voltageOut += adcResult;
auu.voltageOut /= (ADC_NUM - 2);
auu.voltageOut = (auu.voltageOut * (VOUT_RES_H + VOUT_RES_L) / VOUT_RES_L)
* (3300 / 4) / (4096 / 4);
buu.vol_set = auu.voltageSet;
buu.vol_out = auu.voltageOut;
error_t = buu.vol_set - buu.vol_out;
derror_t = error_t - buu.vol_preError;
dderror_t = derror_t - buu.vol_preDError;
buu.vol_preError = error_t;
buu.vol_preDError = derror_t;
if (!((error_t < buu.deadline) && (error_t > -buu.deadline)))
buu.vol_feed += buu.vol_P * derror_t + buu.vol_I * error_t / 100
+ buu.vol_D * dderror_t / 100;
auu.voltageFeed = buu.vol_feed / 1000;
auu.voltageFeed = 3114 - auu.voltageFeed;
if (auu.voltageFeed > 3114 + 980)
auu.voltageFeed = 3114 + 980;
if (auu.voltageFeed < 3114 - 980)
auu.voltageFeed = 3114 - 980;
DAC_SetChannel1(auu.voltageFeed);
return 0;
}
这是一个很典型的增量式数字PID算法,error_t是误差,derror_t是误差之差,dderror_t是误差之差的差。通过PID运算buu.vol_feed之后,乘以相应的比例,给出DAC的值。正常的情况下,输出电压会以一个缓慢的趋势到达指令值。实际情况是,这“一个缓慢的趋势”与负载大小有关系,负载大,则趋势快;负载小,则趋势慢。所以在不同的负载电流下,需要不一样的PID参数值。
(调节PID参数,是一件吃力不讨好的事情,枯燥无趣,而且很难掌握一个绝对的规律。所以才会有那么多人去研究更高级的PID算法,比如神经网络、模糊数学、马尔可夫算法等等~~~尼玛用好控制理论真是一门很不简单的工作~~~~)
三个PID参数分别为buu.vol_P、buu.vol_I、buu.vol_D。不同的参数会获得不同特性的输出。经过调试,发现如果使用下面的参数表,会获得相当不错的波形。
这几张张表中都没有0A负载电流的情况,是因为负载太小时PID失去了调节能力。不论指令电压给了多少,输出电压都几乎等于输入电压。而加大负载之后,输出电压会调节到指令电压位置。
文章评论(0条评论)
登录后参与讨论