热度 25
2016-6-1 17:57
1323 次阅读|
0 个评论
数字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失去了调节能力。不论指令电压给了多少,输出电压都几乎等于输入电压。而加大负载之后,输出电压会调节到指令电压位置。