pwm调光伤眼,都自己做了,何不做纯直流?
背景:
之前虎哥发过一篇文章,关于大幅提高室内照明亮度的设想 - 科创 (XXXXXXXXXXXX)。闲来无事测得书房打开台灯桌面的亮度不过300lux,可以说是昏暗了,看起书来费眼睛,遂萌发出自己搞个台灯,提升下桌面的亮度,使其稳定在一个令人舒适的值。
主要用到的材料:
stm32vct6开发板,电源,led灯带,手机懒人支架(夹持led,当做灯臂),铝散热座(废弃主板拆下),散热风扇,一堆导线,stm32开发板,BH1750光照传感器模块,L298N驱动模块,电源转接板等。
台灯制作:
把led灯带粘到铝散热座上,灯带正负极用导线连好。焊接技术一般般,散热太快了,锡难融化。本来想先粘到塑料膜上焊接完再挪过去的,发现塑料膜会糊掉,只能在散热座上强行焊接了(不知同志们对于这种情况有没有什么好方法?)。最后用手机支架(原计划3d打印个支架的,后来突然想到可以用手机支架做灯臂,够长(30cm-150cm都有),夹得也够稳)把整个灯头夹住。如下图所示(凌乱的线【笑哭】)
L298N驱动模块的接线:
因为驱动电压是24V所以必须使用外部5V供电,否则会烧坏芯片,拔掉跳线帽,5V端口接5V,还要注意要共地,不然可能会出bug。
BH1750接线:
按照模块上面标识的字符接线就行。SCL接PB6,SDA接PB7,ADDR接GND。
PID控制:
在过程控制中,按偏差的比例(P)、积分(I)和微分(D)进行控制的PID控制器(亦称PID调节器)是应用最为广泛的一种自动控制器。它具有原理简单,易于实现,适用面广,控制参数相互独立,参数的选定比较简单等优点;而且在理论上可以证明,对于过程控制的典型对象──“一阶滞后+纯滞后”与“二阶滞后+纯滞后”的控制对象,PID控制器是一种最优控制。PID调节规律是连续系统动态品质校正的一种有效方法,它的参数整定方式简便,结构改变灵活(PI、PD、…)[1]。总而言之,PID控制算法是一种相对简单易用的控制算法。
pid算法的公式:u(k)=Kp*error +∑Ki*error +Kd*[error-last_error]
这个公式我们可以转换成下面的几条式子。
pvalue = Kp*error; //(这里有点奇怪,不知为什么英文字体调不了大小,只能加粗了)
ivalue +=Ki*error; //(这个+=很关键,当时我想了一段时间才想出来)
dvalue = (error-last_error)*Kd;
pwmvalue += (int)(Kpvalue+ivalue+dvalue); //(这里用+=是为了符合实际情况)
error显然就是设定值和当前值之间的差(set_value-now_value)。
我们直接以实际例子来解释这三个参数的作用。设定台灯的亮度为1000lux。下面我用我的理解来分别解释参数P,I,D的作用。
参数P也称为比例常数。下图只有参数p起作用(ki=0,kd=0)时的图(如没有特殊说明,下文图表的纵轴代表亮度(单位:lux),横轴代表时间(单位:20ms),没有对ivalue进行限幅):
在kp=0.005时亮度永远也无法到1000lux,这是因为pwmvalue 是int类型的值,而pvalue 是浮点型的值,当error过小时即pvalue小于1时,于取整为0,pwmvalue就无法再增大。
所以,我们需要把Kp调大,随着kp的增大,亮度变为1000lux的时间越短,但是,如果kp过大,系统便会震荡。
如下图(kp=5,ki=0,kd=0),可以看出系统刚开始的时候震荡很剧烈。
当然,台灯如果亮度变化过快,感觉不舒服,我个人觉得kp=0.1时的体验比较好。这里就有个问题了,明明Kp(取0.05-1之间的值)就能很好解决台灯亮度的问题,还要其他两个参数干嘛。其实在我的这个pid台灯里面确实只用Kp就能解决问题了,系统的稳态误差可以忽略不计(影响很小),毕竟亮度提升太快肉眼也不舒服,亮度提升太慢体验也不好,不需要用(可以不用)到I,D这两个参数。当初申请基金的时候,还没有开始搞STM32,也没有接触PID,导致存在立项失误,这里要道个歉。
下面我们来看看ivalue += Ki*error;ki也称为积分时间常数。当kp过小(如kp=0.005,ki=0,kd=0)时,需要不断积分(ki=0.000001),最终使得对pwmvalue增大,趋于光强为1000lux的pwm值。如下图,蓝色线(Kp=0.005,Ki=0.000001,Kd=0)橙色线(Kp=0.005,Ki=0,Kd=0)。
ivalue会使系统更快达到所需的值,但会使系统震荡。在1000lux附近波动,最终趋于1000lux。Ki越大,波动越剧烈,最后趋于1000lux(稳定)的时间也越久。如图,ki越大,系统震荡越剧烈,稳定所需的时间越长,蓝线(Kp=0.1,Ki=0.1,Kd=0),橙色线(Kp=0.1,Ki=0.01,Kd=0)。
我们有两个办法能使系统震荡减弱,一个是积分限幅,另一个是微分抑制。我们先来说说积分限幅,如下图中橙色线(Kp=0.1,Ki=0.1,Kd=0)震荡很剧烈,在系统开始运行时,ivalue不断变大使得曲线十分陡峭,到达了设定值之后,由于ivalue是累加的,没有办法立刻等于0,所以pwmvalue仍然在不断增加,如下图,增加到1700lux附近才下降,然后就反复震荡,最终趋于设定值下图。我们可以对ivalue进行限幅,让ivalue只能在一个固定的范围内变动,如下图蓝线(Kp=0.1,Ki=0.1,Kd=0,ivalue限幅±100)。
限幅代码:
#define imax 100 //限幅
#define imin -100 //限幅
if(ivalue>imax) ivalue=imax; //如果ivalue>imax,使ivalue=imax
if(ivalue<imin) ivalue=imin; //如果ivalue<imin,使ivalue=imin
在对ivalue限幅之后,系统震荡明显减弱,如下图蓝线。
Kd是时间微分常数,用于减缓pwmvalue的增速(阻尼),当亮度不断增加趋于设定亮度时,error(t-1)>error(t),dvalue = (error-last_error)*Kd,式子中括号里面的项就是负值,乘上Kd微分时间常数,得到dvalue,dvalue能使pwmvalue的变化更平缓。如下图橙色线(kp=0.1,ki=0.1,kd=0)蓝色线(kp=0.1,ki=0.1,kd=1)
但是,当pwmvalue的增速很快(亮度不断增加并趋于设定亮度)时,|error-last_error|会很大导致|dvalue|也很大,如果Kd也偏大,显然在系统刚开始运行时,会震荡(pvalue+ivalue+dvalue<0,使得pwmvalue值变小,以至于亮度突然上升后突然下降,对外表现为闪烁,顿挫感,亮度变化不缓和),如下图(kp=0.1,ki=0,kd=4)。
最后我们应该把这个pid运算放在程序的中断里面(固定周期执行一次就行),假如放在while循环,pwmvalue 的值增速极快(每while一次,只要暂时没有读取到新的光照强度的新值(20ms读取一次)pvalue,dvalue 不变,ivalue快速增大(或许可以通过调小Kp,Ki,Kd(实测不太行,要调的很小,很难调节)来解决这个问题,毕竟while循环也算是一个周期)导致pwmvalue也快速增加,最后导致系统震荡)
蓝牙控制:
使用CH-08蓝牙模块,手机连接上蓝牙模块,发送所需的亮度就能自动调节,不需要每次调整亮度都要重新烧录。也可以做到无线开灯,无线关灯的效果。
整个pid调光台灯的全貌如下图:
程序:
结语:
感谢kc基金的支持,感谢kc论坛。在制作这个台灯的时候,遇到了不少困难,感谢同志们提出的建议,不过我觉得这个项目并没有真正完成(或许这个帖子还存在一些技术性的问题),还能加入更多的功能。希望这个帖子能给后面的同志们一点帮助。今后我还会继续完善这个项目,如果有问题和建议欢迎在下面留言。
[1] 刘教瑜,舒军主编;甘月红,谢长君副主编.单片机原理及应用(第2版):武汉理工大学出版社,2014.08