有了磁悬浮陀螺的念头,我没有直接开始做下推式,而是先做一个上拉式的练练手。这件事的经历说明:对一个新手来说,可以采用由浅入深,先易后难的策略;虽然总时间可能会更长一点,但是对增强信心,掌握经验很有帮助。(好吧,水木的Joker们看到这句话一定会想歪,色情猥琐男们请自觉面壁)
Contents
1 上拉式磁悬浮
上拉式磁悬浮原理图
view plaincopy to clipboardprint? 01. int readPin = 2; //用来连接输入 02. int i1Pin = 36; //连接电机驱动板的I1接口 03. int i2Pin = 37; //连接电机驱动板的I2接口 04. int powerPin = 8; //连接电机驱动板的EA接口 05. int adjustPin = 6; 06. 07. boolean flag = true; 08. int power = 0; 09. int readValue = 0; 10. int adjustValue = 0; 11. 12. void GetPowerValue() 13. { 14. power = readValue - adjustValue; 15. if(power 50) power = 50; 17. power = power * 16 / 10; 18. } 19. 20. void setup() 21. { 22. pinMode(i1Pin, OUTPUT); //I1和I2都是数字信号 23. pinMode(i2Pin, OUTPUT); //通过设置I1和I2来控制电流方向 24. pinMode(powerPin, OUTPUT); //按占空比方式输出的模拟信号 25. digitalWrite(i1Pin, !flag); 26. digitalWrite(i2Pin, flag); 27. //Serial.begin(9600); //设置波特率 28. } 29. 30. void loop() 31. { 32. //读取电位器和传感器的读数 33. readValue = analogRead(readPin); 34. //传感器的电压范围是220~580,所以调节电位器的范围可以稍作调整 35. adjustValue = analogRead(adjustPin) / 3 + 220; 36. GetPowerValue(); 37. //Serial.println(readValue); 38. //Serial.println(adjustValue); 39. //Serial.println(power); 40. 41. analogWrite(powerPin, power); 42. //delay(2000); 43. //delay(1); 44. }
power = power * 16 / 10;
1. 参数很重要!如果你的磁悬浮上下跳动的厉害,恭喜你,其实你已经接近成功了。
2. 这个试验中的参数有效范围非常窄,跟程序也有关系,后面会介绍一种PID算法,可以扩大范围,更容易调节。
3. 坚持,太累的时候放松一下,然后换个思路想想。
2 下推式磁悬浮电路
view plaincopy to clipboardprint? 01. int readValue1 = analogRead(read1Pin);
view plaincopy to clipboardprint? 01. analogWrite(power1Pin, Pid1.power);
view plaincopy to clipboardprint? 01. pinMode(Pin1, OUTPUT); //设置为输出管脚 02. pinMode(Pin2, INPUT); //设置为输入管脚 03. digitalWrite(Pin1, HIGH); //输出高电压 04. int v = digitalRead(Pin2); //读取Pin2的电压,返回结果是0或1
view plaincopy to clipboardprint? 01. int adjust1Pin = 1; //用来调节A方向的电位器 02. int adjust2Pin = 2; //用来调节B方向的电位器 03. int read1Pin = 4; //用来连接输入A传感器 04. int read2Pin = 3; //用来连接输入B传感器 05. int i1Pin = 36; //连接电机驱动板的I1接口 06. int i2Pin = 37; //连接电机驱动板的I2接口 07. int i3Pin = 39; //连接电机驱动板的I3接口 08. int i4Pin = 38; //连接电机驱动板的I4接口 09. int power1Pin = 5; //连接电机驱动板的EA接口 10. int power2Pin = 6; //连接电机驱动板的EB接口
view plaincopy to clipboardprint? 01. I1=0;I2=1; //输出正电压,EA范围0~255时,输出电压对应为0~+20V 02. I1=1;I2=0; //输出负电压,EA范围0~255时,输出电压对应为0~-20V 03. I1=0;I2=0; //输出电压均为0 04. I1=1;I2=1; //输出电压均为0
3 PID平衡算法
现在介绍提了多次的PID平衡算法。先从网上摘抄一段:
当今的自动控制技术都是基于反馈的概念。反馈理论的要素包括三个部分:测量、比较和执行。测量关心的变量,与期望值相比较,用这个误差纠正调节控制系统的响应。
这个理论和应用自动控制的关键是,做出正确的测量和比较后,如何才能更好地纠正系统。
在工程实际中,应用最为广泛的调节器控制规律为比例、积分、微分控制,简称PID控制,又称PID调节。它以其结构简单、稳定性好、工作可靠、调整方便而成为工业控制的主要技术之一。当被控对象的结构和参数不能完全掌握,或得不到精确的数学模型时,控制理论的其它技术难以采用时,系统控制器的结构和参数必须依靠经验和现场调试来确定,这时应用PID控制技术最为方便。即当我们不完全了解一个系统和被控对象,或不能通过有效的测量手段来获得系统参数时,最适合用PID控制技术。PID控制,实际中也有PI和PD控制。PID控制器就是根据系统的误差,利用比例、积分、微分计算出控制量进行控制的。
比例(P)控制
比例控制是一种最简单的控制方式。其控制器的输出与输入误差信号成比例关系。当仅有比例控制时系统输出存在稳态误差(Steady-state error)。
积分(I)控制
在积分控制中,控制器的输出与输入误差信号的积分成正比关系。对一个自动控制系统,如果在进入稳态后存在稳态误差,则称这个控制系统是有稳态误差的或简称有差系统(System with Steady-state Error)。为了消除稳态误差,在控制器中必须引入“积分项”。积分项对误差取决于时间的积分,随着时间的增加,积分项会增大。这样,即便误差很小,积分项也会随着时间的增加而加大,它推动控制器的输出增大使稳态误差进一步减小,直到等于零。因此,比例+积分(PI)控制器,可以使系统在进入稳态后无稳态误差。
微分(D)控制
在微分控制中,控制器的输出与输入误差信号的微分(即误差的变化率)成正比关系。自动控制系统在克服误差的调节过程中可能会出现振荡甚至失稳。其原因是由于存在有较大惯性组件(环节)或有滞后(delay)组件,具有抑制误差的作用,其变化总是落后于误差的变化。解决的办法是使抑制误差的作用的变化“超前”,即在误差接近零时,抑制误差的作用就应该是零。这就是说,在控制器中仅引入 “比例”项往往是不够的,比例项的作用仅是放大误差的幅值,而目前需要增加的是“微分项”,它能预测误差变化的趋势,这样,具有比例+微分的控制器,就能够提前使抑制误差的控制作用等于零,甚至为负值,从而避免了被控量的严重超调。所以对有较大惯性或滞后的被控对象,比例+微分(PD)控制器能改善系统在调节过程中的动态特性。
view plaincopy to clipboardprint? 01. //PINs setting 02. int adjust1Pin = 1; //用来调节A的电位器 03. int adjust2Pin = 2; //用来调节B的电位器 04. int read1Pin = 4; //用来连接输入A传感器 05. int read2Pin = 3; //用来连接输入B传感器 06. int i1Pin = 36; //连接电机驱动板的I1接口 07. int i2Pin = 37; //连接电机驱动板的I2接口 08. int i3Pin = 39; //连接电机驱动板的I3接口 09. int i4Pin = 38; //连接电机驱动板的I4接口 10. int power1Pin = 5; //连接电机驱动板的EA接口 11. int power2Pin = 6; //连接电机驱动板的EB接口 12. int rotatePin = 3; //用来控制磁场旋转的PMW接口 13. 14. boolean debug = false; 15. boolean writeLog = false; 16. double setKd1 = 0.55; 17. double setKd2 = 0.55; 18. double setKp = 22; 19. int offset = 70; 20. int delayMs = 1; 21. int tick = 0; 22. int myLog[3500]; 23. 24. //PID structure 25. typedef struct { 26. double target; 27. double aver; 28. double Kp; 29. double Kd; 30. int preError; 31. int power; 32. boolean flag; 33. double v; 34. } PID; 35. 36. PID Pid1, Pid2; 37. 38. void setup() 39. { 40. pinMode(i1Pin, OUTPUT); //I1和I2都是数字信号 41. pinMode(i2Pin, OUTPUT); //通过设置I1和I2来控制电流方向 42. pinMode(i3Pin, OUTPUT); //I1和I2都是数字信号 43. pinMode(i4Pin, OUTPUT); //通过设置I1和I2来控制电流方向 44. pinMode(power1Pin, OUTPUT); //按占空比方式输出的模拟信号 45. pinMode(power2Pin, OUTPUT); //按占空比方式输出的模拟信号 46. pinMode(rotatePin, OUTPUT); //按占空比方式输出的模拟信号 47. 48. //analogWrite(rotatePin, 128); 49. 50. Serial.begin(9600); //设置波特率 51. TCCR0B = 0x01; // Timer 0: PWM 5 & 6 @ 16 kHz 52. TCCR1B = 0x01; // Timer 1: PWM 9 & 10 @ 32 kHz 53. TCCR2B = 0x01; // Timer 2: PWM 3 & 11 @ 32 kHz 54. Pid1.Kp = setKp; 55. Pid1.preError = 0; 56. Pid1.Kd = setKd1; 57. Pid1.power = 0; 58. Pid1.flag = true; 59. Pid1.target = 300; 60. Pid1.aver = 0; 61. Pid1.v = 0; 62. Pid2.Kp = setKp; 63. Pid2.preError = 0; 64. Pid2.Kd = setKd2; 65. Pid2.power = 0; 66. Pid2.flag = true; 67. Pid2.target = 300; 68. Pid2.aver = 0; 69. Pid2.v = 0; 70. tick = 0; 71. } 72. 73. int tick2 = 0; 74. //boolean rotateFlag = true; 75. void loop() 76. { 77. //digitalWrite(rotatePin, rotateFlag); 78. //rotateFlag = ! rotateFlag; 79. //delay(16000); 80. //return; 81. 82. if(debug) tick = 0; 83. tick++; 84. if(tick==500) 85. { 86. tick2++; 87. if(tick2500) 116. { 117. tick = 0; 118. //delay(990000); 119. return; 120. }; 121. 122. //=======第一组电位器和传感器======== 123. int readValue1 = 0; 124. for(int i = 0; i >= 2; 126. //readValue1 += (Pid1.flag ? 1 : -1) * Pid1.power / 17; 127. int adjustValue1 = analogRead(adjust1Pin); //410 analogRead(adjust1Pin); 128. Pid1.aver = Pid1.aver * 0.9995 + readValue1 * 0.0005; 129. Pid1.target = Pid1.target + (Pid1.target - Pid1.aver) / 100.0; 130. Pid1.target = max(0, max(adjustValue1 - offset, Pid1.target)); 131. Pid1.target = min(755, min(adjustValue1 + offset, Pid1.target)); 132. 133. //=======第二组电位器和传感器======= 134. int readValue2 = 0; 135. for(int i = 0; i >= 2; 137. //readValue2 += (Pid2.flag ? 1 : -1) * Pid2.power / 6; 138. int adjustValue2 = analogRead(adjust2Pin); //240 analogRead(adjust2Pin); 139. Pid2.aver = Pid2.aver * 0.9995 + readValue2 * 0.0005; 140. Pid2.target = Pid2.target + (Pid2.target - Pid2.aver) / 1000.0; 141. Pid2.target = max(0, max(adjustValue2 - offset, Pid2.target)); 142. Pid2.target = min(755, min(adjustValue2 + offset, Pid2.target)); 143. 144. if(debug) 145. { 146. Serial.println(adjustValue1); 147. Serial.println(adjustValue2); 148. Serial.println(readValue1); 149. Serial.println(readValue2); 150. Pid1.flag = adjustValue1 > 512; 151. Pid1.power = abs(adjustValue1 - 512) / 2; 152. if(Pid1.power > 255) Pid1.power = 255; 153. digitalWrite(i1Pin, Pid1.flag); 154. digitalWrite(i2Pin, !Pid1.flag); 155. analogWrite(power1Pin, Pid1.power); 156. Pid2.flag = adjustValue2 > 512; 157. Pid2.power = abs(adjustValue2 - 512) / 2; 158. if(Pid2.power > 255) Pid2.power = 255; 159. digitalWrite(i3Pin, Pid2.flag); 160. digitalWrite(i4Pin, !Pid2.flag); 161. analogWrite(power2Pin, Pid2.power); 162. delay(32000); 163. return; 164. } 165. 166. //Calculate power values 167. double v, error; 168. error = readValue1 - Pid1.target; 169. v = error - Pid1.preError; 170. Pid1.v = (Pid1.v * 6 + v) / 7; 171. Pid1.power = (int)error * Pid1.Kd + Pid1.v * Pid1.Kp; 172. Pid1.flag = Pid1.power > 0; 173. Pid1.power = abs(Pid1.power); 174. if(Pid1.power>255) Pid1.power = 255; 175. Pid1.preError = error; 176. 177. error = readValue2 - Pid2.target; 178. v = error - Pid2.preError; 179. Pid2.v = (Pid2.v * 6 + v) / 7; 180. Pid2.power = (int)error * Pid2.Kd + Pid2.v * Pid2.Kp; 181. Pid2.flag = Pid2.power 255) Pid2.power = 255; 184. Pid2.preError = error; 185. 186. //Write PMW to control the floa 187. digitalWrite(i1Pin, Pid1.flag); 188. digitalWrite(i2Pin, !Pid1.flag); 189. analogWrite(power1Pin, Pid1.power); 190. 191. digitalWrite(i3Pin, Pid2.flag); 192. digitalWrite(i4Pin, !Pid2.flag); 193. analogWrite(power2Pin, Pid2.power); 194. 195. myLog[tick * 7 + 0] = tick; 196. myLog[tick * 7 + 1] = (int)Pid1.target; 197. myLog[tick * 7 + 2] = readValue1; 198. myLog[tick * 7 + 3] = Pid1.power; 199. myLog[tick * 7 + 4] = (int)Pid2.target; 200. myLog[tick * 7 + 5] = readValue2; 201. myLog[tick * 7 + 6] = Pid2.power; 202. 203. /* 204. for(int i=0;i(未完待续...)
本文转自 动力老男孩的博客
原文地址:
http://www.diy-robots.com/?p=745