## 盗梦陀螺攻略5- PID平衡算法

```//PINs setting
int i1Pin = 36;        //连接电机驱动板的I1接口
int i2Pin = 37;        //连接电机驱动板的I2接口
int i3Pin = 39;        //连接电机驱动板的I3接口
int i4Pin = 38;        //连接电机驱动板的I4接口
int power1Pin = 5;     //连接电机驱动板的EA接口
int power2Pin = 6;     //连接电机驱动板的EB接口
int rotatePin = 3;     //用来控制磁场旋转的PMW接口

boolean debug = false;
boolean writeLog = false;
double setKd1 = 0.55;
double setKd2 = 0.55;
double setKp = 22;
int offset = 70;
int delayMs = 1;
int tick = 0;
int myLog[3500];

//PID structure
typedef struct {
double target;
double aver;
double Kp;
double Kd;
int preError;
int power;
boolean flag;
double v;
} PID;

PID Pid1, Pid2;

void setup()
{
pinMode(i1Pin, OUTPUT);     //I1和I2都是数字信号
pinMode(i2Pin, OUTPUT);     //通过设置I1和I2来控制电流方向
pinMode(i3Pin, OUTPUT);     //I1和I2都是数字信号
pinMode(i4Pin, OUTPUT);     //通过设置I1和I2来控制电流方向
pinMode(power1Pin, OUTPUT);  //按占空比方式输出的模拟信号
pinMode(power2Pin, OUTPUT);  //按占空比方式输出的模拟信号
pinMode(rotatePin, OUTPUT);  //按占空比方式输出的模拟信号

//analogWrite(rotatePin, 128);

Serial.begin(9600);          //设置波特率
TCCR0B = 0x01;   // Timer 0: PWM 5 &  6 @ 16 kHz
TCCR1B = 0x01;   // Timer 1: PWM 9 & 10 @ 32 kHz
TCCR2B = 0x01;   // Timer 2: PWM 3 & 11 @ 32 kHz
Pid1.Kp = setKp;
Pid1.preError = 0;
Pid1.Kd = setKd1;
Pid1.power = 0;
Pid1.flag = true;
Pid1.target = 300;
Pid1.aver = 0;
Pid1.v = 0;
Pid2.Kp = setKp;
Pid2.preError = 0;
Pid2.Kd = setKd2;
Pid2.power = 0;
Pid2.flag = true;
Pid2.target = 300;
Pid2.aver = 0;
Pid2.v = 0;
tick = 0;
}

int tick2 = 0;
//boolean rotateFlag = true;
void loop()
{
//digitalWrite(rotatePin, rotateFlag);
//rotateFlag = ! rotateFlag;
//delay(16000);
//return;

if(debug) tick = 0;
tick++;
if(tick==500)
{
tick2++;
if(tick2<50) {tick = 0;return;}
tick2 = 0;
if(writeLog)
{
for(int i=0;i<500;i++)
{
Serial.print(myLog[i*7 + 0]);
Serial.print("  ");
Serial.print(myLog[i*7 + 1]);
Serial.print("  ");
Serial.print(myLog[i*7 + 2]);
Serial.print("  ");
Serial.print(myLog[i*7 + 3]);
Serial.print("  ");
Serial.print(myLog[i*7 + 4]);
Serial.print("  ");
Serial.print(myLog[i*7 + 5]);
Serial.print("  ");
Serial.print(myLog[i*7 + 6]);
Serial.println("  ");
}
Serial.println(Pid1.target);
Serial.println(Pid1.preError);
Serial.println(Pid2.target);
Serial.println(Pid2.preError);
}
return;
}
else if(tick>500)
{
tick = 0;
//delay(990000);
return;
};

//=======第一组电位器和传感器========
//readValue1 += (Pid1.flag ? 1 : -1) * Pid1.power / 17;
Pid1.aver = Pid1.aver * 0.9995 + readValue1 * 0.0005;
Pid1.target = Pid1.target + (Pid1.target - Pid1.aver) / 100.0;
Pid1.target = max(0, max(adjustValue1 - offset, Pid1.target));
Pid1.target = min(755, min(adjustValue1 + offset, Pid1.target));

//=======第二组电位器和传感器=======
//readValue2 += (Pid2.flag ? 1 : -1) * Pid2.power / 6;
Pid2.aver = Pid2.aver * 0.9995 + readValue2 * 0.0005;
Pid2.target = Pid2.target + (Pid2.target - Pid2.aver) / 1000.0;
Pid2.target = max(0, max(adjustValue2 - offset, Pid2.target));
Pid2.target = min(755, min(adjustValue2 + offset, Pid2.target));

if(debug)
{
Pid1.power = abs(adjustValue1 - 512) / 2;
if(Pid1.power > 255) Pid1.power = 255;
digitalWrite(i1Pin, Pid1.flag);
digitalWrite(i2Pin, !Pid1.flag);
analogWrite(power1Pin, Pid1.power);
Pid2.power = abs(adjustValue2 - 512) / 2;
if(Pid2.power > 255) Pid2.power = 255;
digitalWrite(i3Pin, Pid2.flag);
digitalWrite(i4Pin, !Pid2.flag);
analogWrite(power2Pin, Pid2.power);
delay(32000);
return;
}

//Calculate power values
double v, error;
v = error - Pid1.preError;
Pid1.v = (Pid1.v * 6 + v) / 7;
Pid1.power = (int)error * Pid1.Kd + Pid1.v * Pid1.Kp;
Pid1.flag = Pid1.power > 0;
Pid1.power = abs(Pid1.power);
if(Pid1.power>255) Pid1.power = 255;
Pid1.preError = error;

v = error - Pid2.preError;
Pid2.v = (Pid2.v * 6 + v) / 7;
Pid2.power = (int)error * Pid2.Kd + Pid2.v * Pid2.Kp;
Pid2.flag = Pid2.power < 0;
Pid2.power = abs(Pid2.power);
if(Pid2.power>255) Pid2.power = 255;
Pid2.preError = error;

//Write PMW to control the floa
digitalWrite(i1Pin, Pid1.flag);
digitalWrite(i2Pin, !Pid1.flag);
analogWrite(power1Pin, Pid1.power);

digitalWrite(i3Pin, Pid2.flag);
digitalWrite(i4Pin, !Pid2.flag);
analogWrite(power2Pin, Pid2.power);

myLog[tick * 7 + 0] = tick;
myLog[tick * 7 + 1] = (int)Pid1.target;
myLog[tick * 7 + 2] = readValue1;
myLog[tick * 7 + 3] = Pid1.power;
myLog[tick * 7 + 4] = (int)Pid2.target;
myLog[tick * 7 + 5] = readValue2;
myLog[tick * 7 + 6] = Pid2.power;

/*
for(int i=0;i<8;i++)
{
digitalWrite(rotatePins[i] , 0);
digitalWrite(rotatePins[(i + 1) % 8] ,1);
delay(1);
}
digitalWrite(rotatePins[0] , 0);
*/

delay(delayMs);
}
```
﻿

### 对 “盗梦陀螺攻略5- PID平衡算法” 的 153 条 评论

1. John 说：

哇哈~这个赞~都用到PID控制了！学习学习！

2. 玩平衡用PID，价格便宜量又足

3. 胡雪岩 说：

是个好东东,我还不太会啊

4. 魔王 说：

那个 segway 小车的PID 曾经测试了N^N遍..还是没有成功

• 哦？改天我也试试看
你可以跟galiu同学探讨一下

• YZL_HN 说：

我想请问，你加的两个电位器是干嘛用的？

5. z 说：

用什么软件、分析接收串口发来的数据？

• 用一个叫Origin的软件，分析数据专用的

6. propolis 说：

虽然不太懂，学习一下。呵呵

7. 老白兔 说：

大哥！！！佩服的五体投地啊！！！
我是学计算机的，我们那个单片机啊什么的都没学，稍微会点编程，
好后悔以前电子电路打酱油了啊……
对你的这些东西都感到震撼！！！
头一回有这么强烈的想好好学点东西的冲动……
能不能跟我说下，要学习些哪些方面的知识啊……
谢谢，膜拜啊……

• 哈哈，爱好跟专业是两回事，我的专业是航空发动机
需要涉及到的知识点主要包括：电子电路，嵌入式，C或者Java编程
电子电路我也不熟，所以用的是Arduino的开发板，不用做板子了

8. galiu 说：

被点名了哇？呵呵。
带着女儿玩乐高的好处之一是，申请经费非常便利啊…
最近刚借着要遥控乐高为名，把手机换成了G7，努力搞Android中…

• cool！我最近也刚入手了G7，嘿嘿，也是准备用来遥控用
blog如果你自己有空间的话，就用wordpress
没有空间的话，用新浪微薄

9. galiu 说：

请教下楼主，blog有哪个比较推荐啊？

10. 海云飞 说：

老大研究下‘四轴’飞行器吧，那个更爽！看看ar.drone

11. itiancai 说：

不是吧，我说自控的都没你用的熟。悲剧，自己已经被无情的就业大军推倒自动控制行业了。电子和机器人竟成业余爱好了。我可怜的IFAN还躺在工具箱里。

12. jjhigh24 说：

好强啊，第一次进入到这个网站，我琢磨到半夜才恋恋不舍的关电脑，太佩服了，作为电子专业的我，深感羞愧，C语言我们才刚刚起步学习，不想您老人家都已经出神入化了。我的好几个同学对你的网站都爱疯了，最近我们几个人组织三人的小队伍，打算将您的陀螺战役再做一遍，以表敬意！中途遇到什问题还望您多多指点，顺便问一下，这样的制作，成本会很高吗？

• 欢迎欢迎！
这个制作成本不高，少吃几顿饭就出来了
如果用老顽童的电路方案，会更便宜
我的方案用了单片机，那个arduino的板子可能会贵一点，一百多吧
另外，磁铁和线圈也得各几十元

• jjhigh24 说：

好嘞，说干就干，下周末我们就去上海买材料去~~~

• 哇，还得去上海买？路费就不便宜了吧？
我那个有个清单，先看看
到时候一次全搞定，省的老跑
实在不够的可以淘宝

• jjhigh24 说：

没有啊，我学校离那儿近，半个小时的路就OK了，不过说的对，有些东西却是要去淘宝的，但更想去真正的电子市场看看，体验一下…感觉会不错~哈哈

13. 李宙恒 说：

代码好难看懂 能否多加一点注释啊

14. 李宙恒 说：

或者有一个流程图也行 这样看起来会比较方便吧。。。不然那么多 符号数字。。头都大了。。。

15. 李宙恒 说：

动力哥！！！！有米有比较完整的原理图啊 就是你做的这个。。有的话麻烦一下~~~~~发到我邮箱吧 453569822@qq.com 感谢了 !!!!!!!!最近一直在弄这个。。。很悲催啊啊啊

• 呵呵，看帖不仔细吧，原理图我不是发过了吗？
http://www.diy-robots.com/Resources/meglev/diagram.pdf
我就是照这个做的，原理完全一样
至于流程图和我这个Arduino版本的原理图…..

我要是有时间做早发出来了，况且也不会做，Protel不会用

• 李宙恒 说：

这个图看了好多遍啊 就是没看多懂。。。。。
主要是对霍尔器件的用法不是很懂
网上搜了没搜到
于是看了半天数据手册 。。。还没看明白

• 月月鸟 说：

用fritzing做原理图比较快。

• 多谢指点，呵呵，下载一个看看
这世界还是好人多啊

16. 李宙恒 说：

还有 霍尔元件放在那个地方 是用来监测谁的磁通的变化？想来想去 4个螺线管的磁场方向应该是平行霍尔元件的表面的吧。那霍尔器件就不能监测到了。。
说白了 我还是对这个系统如何反馈。如何控制有点迷惑。。。
好了 虽然这个问题有点白痴。。请表拍我~~~~~~

• 你说对了，4个线圈的磁场方向是平行霍尔表面的，目的就是线圈电流对霍尔没有影响
注意注意，霍尔元件是用来判断陀螺位置的，跟线圈没有关系
哈哈，你都知道自己问的问题不合适了，自己拍几下吧

• 李宙恒 说：

哦 soga!那程序呢？？？有没有详细的注释哈。。不然太难看了。。

• 我帖程序的时候，显然不会故意把注释删了再贴
你看上面那几行英文就是我的注释了
其实就是PID算法
我建议你也先做一个上拉式的，只有一个线圈
以此了解PID，然后就能看懂代码了

这段代码，你把debug，tick相关的都删掉，然后把所有的Pid1,Pid2这样的删掉一半，就会发现其实没几行了

• 李宙恒 说：

嗯 好的 上拉式的貌似简单的。。还有一个问题。debug和 tick是做啥的啊？？？

17. 李宙恒 说：

终于快要看懂了 。这个是用的位置式PID算法吧？（别又错了） 。
关于PID的计算过程那里已经懂了
但还是有几个地方不太明白
1.target = 300 ;为什么是300 300对应的电压应该是300/1024*5 吧
而霍尔传感器的输出（无磁通时）为2.5V啊 这之间应该有运放的作用吧？
2.//=======第一组电位器和传感器======== 这个过程不是太明白在做些什么。。能稍微讲一下么。大概意思就行。。

18. lslsls 说：

这个采样用到了什么滤波算法？

• 这里采用了最先进，快速，高效的算术平均滤波算法…..
好吧，专家们一般都这样用术语吓唬人
其实就是取几个值平均一下嘛，代码看这里：

• DaisyHUnter 说：

• Prince z 说：

>>2表示位向右移两位，二进制所以相当于得到的值除以4（比如十进制1000右移一位表示0100，变成10分之1），即取平均，相当于滤波。

• DaisyHUnte 说：

谢过~

19. 没错，就是PID，呵呵
target你没看懂太正常了，因为我刚才看了半天，想了半天才想起来这是干啥用的
大概说说当初加这个参数的原因
我们调节一个平衡位置的时候，需要有个目标值，它表示陀螺正好处于平衡位置时传感器的读数。这个目标值是多少我们其实并不知道，而且它会随着温度，环境磁场而变化，这就是我外接ajustPin的原因，既然你不知道目标值是多少，那么调一调试试，拧电位器让它能平衡为止

所以我最初的程序是这么写的：

请注意，这样写就已经可以实现平衡了！所以你看代码的时候，可以先把Pid1.aver,offset这些东西都删掉，代码就容易看懂了

结果问题又来了，当我刚把陀螺放上去的时候，这个aver是从0突然变大的，最初的一秒钟很不准确，这个算法会误认为陀螺现在位置偏移非常大，就猛加电流，结果真的就不平衡了。所以我又加了一个offset参数，表示修正的范围，超过这个范围，我就不在修正了。

这段代码的结果是，陀螺刚放上去的时候，会稍微抖动一下，然后会慢慢的越来越平稳。

总结：这上面都是浮云，其实我监测的电流值只下降了一点点，至于平稳，你慢慢的调电位器，一样可以调到一个非常平稳的位置。不过我也懒得把程序改回去了，这就是你看到的结果。建议你只留这么一句即可：

• 比利丸 说：

请教一下第128行 Pid1.aver = Pid1.aver * 0.9995 + readValue1 * 0.0005;在setup()就已定义Pi1.aver=0,那么*0.9995有什么意义呢?

20. 李宙恒 说：

昨天终于把电路搭好了 看着一堆一堆杂乱的线 放上小磁铁试了试。
当用手托着小磁铁靠近线圈上方时，感觉到有点震动，感觉也快要平衡了（自我安慰一下），但一松开手 小磁铁就被吸到大磁铁上去了。。

有一个问题 这个adjust1Pin 这个电位器的解法是这样的吗？？电位器两端分别接地 和 VCC ，中间的接arduino 的1管脚 ？？？（）
然后调平衡用这个来调？？
始终感觉不大对劲啊。可以调节与LM358相连的电位器，使得输出运放输出电压在2.5V 那么可以设置target=511啊 。这样不是更方便吗？

• 你理解的很正确
但是与LM358相连的电位器是被放大几十倍的，太灵敏了，稍微一动就变几十，想把它调到准确的2.5V有点困难（还会温漂，每次读数都变）
而且那个电位器比较小，是焊在板子上的，你做好以后封到盒子里，怎么调节它呢？
所以我再引出来一个大电位器，这个是不放大的，调起来方便，而且可以把它安装在盒子的面板上，看上去比较酷

21. 李宙恒 说：

与LM358项链的电位器这个调节会改变运放同相端的电压。这个电压是直流的 不会被放大。运放放大的交流信号吧，也是就传感器测得的电压的波动（传感器的输出电压是2.5V基准电压+波动），运放会放大这个由磁场变化所导致的传感器的输出信号的波动吧 。。

昨天我就调节的这两个电位器（全是裸露在外边的。。。。），电压变化连续，也不太灵敏，貌似转了10几圈才从0.8V转到2.5v 。。

还有一个地方不太明白，就是如何根据error来判断通电的顺序，Pid1.flag = Pid1.power > 0; 就是这里了，由于昨天弄得很仓促，线接的一团糟。。。这个地方要考虑线圈的接法 和 线圈与 L298N的接法 吧？？如果发现磁铁往左偏，经过程序运算后，我还把磁铁往左推。。。。。。。

没时间了 上课去了。。
感谢动力哥早上的指导！！！

• 老顽童 说：

你把线圈两线头对调,就倒相啦.

• 李宙恒 说：

嗯 是啊！
如果悬浮不起来，有可能是这个原因，可以把线圈两头对调 ，再尝试一下。

• 是啊，同学，你都已经发现反相了，换个线头，或者在程序里面把变量来个 a=!a 不就搞定了:D

22. 李宙恒 说：

老顽童，，请教一个问题哈
我把LM358 的正相电压调到了2.5V 发现这个芯片的2个输出 1和7脚 电压始终在3.8V。而且调节正相的电位器 几乎是没有变化。。是不是就可以确定这个芯片坏掉了。。

• 老顽童 说：

两个正相都在2.5V.

两个负相也要上2.5V.

输出才会是2.5V.

• 李宙恒 说：

用的负反馈接法 闭环增益50。
但是无论如何电阻 为啥运放的值就不变呢？

• 老顽童 说：

正相在2.5V.

负相也要在2.5V. 输入才为零.

输出才不会被50倍把小误差放大到饱和区. 才可能也在2.5V附近.

• 没错，需要看到你的电路示意图才能分析
不然只能猜测

• 老顽童 说：

没见到图,就不具体.
口述的会各说各的,想不到一起去.

变动电阻时,要看电阻处在什么地方.
如能大幅变动电压的. 输出就应该有反应.

如果仅仅只是变动阻值,也许回路阻抗很大. 这点变动没反应也正常.

• 老顽童 说：

还有就是要控制好闭环增益.(多级时,尽量小环,不要大环).
在够用的条件下,放大量尽量取低点.比如100倍以下.

要不然,开环增益太高,容易自激,
量到的就是一乱七八糟,和莫明奇妙的信息,会把分析引入岐途.

• 李宙恒 说：

额 换了一个LM358 莫名奇妙的好了。。
老顽童，问一下哈 ，有没有这样的器件啊。。有两个输出口，当输入电压高于某个值时2个输出均为高，当输入电压低于某个值时输出电压均为低，当输入电压在这两者之间时一个输出高 一个输出低。。。

• 这种器件组合一下就可以吧，把输入分成两股，然后用两个比较器，一个high，一个low
各自输出各自的不就好了嘛

• 李宙恒 说：

嗯 是的 谢谢~~~~帮同学问的。

• 老顽童 说：

还不知道有没有现成的三电平器件, 有市场需求的话,应该也不难生产.

只知道近些年在DC变换中,开始流行三电平变换器,很巧妙的,适用于高电压,大功率场合.
把一个方波分割成2个,分2次切换,减轻了对开关管的应力. 除了赞叹外,本人还没有实践过. 人类,真的是创意无穷.

• 李宙恒 说：

嗯 创意无穷，我还在一个网上看到别人用电子元器件做的各种动物造型。。真的很有趣。
运放是放大交流信号啊。？怎么成直流放大器了？

• 老顽童 说：

就是特意来纠正你的.

运放是正宗的,专门的直流放大器.

• 老顽童 说：

运放是直流放大器.

23. 李宙恒 说：

好像找到一点原因了 我一旦打开实验室的直流稳压电源 霍尔的输出变化很大。难道实验室的直流稳压源对霍尔有影响？

• 如果是这样的话，可能是你的直流稳压电源容量不够，初始电流太大的时候撑不住
对霍尔的影响可能是“副产品”

• 李宙恒 说：

进一步实验排查发现 没那么简单。。应该不是直流稳压源的影响 而是线圈们的影响。。因为用一个9V电池接在线圈两端 发现霍尔输出变化了。。。
应该和霍尔的位置有关。
但是洞洞板这个东西，霍尔器件不太好调整位置啊。。要调整就只能调整线圈的位置了。。

• 对，安装霍尔有注意事项的，平面一定要竖直，另外高度上尽量接近线圈高度的中心
即使这样，也只是尽量减少线圈对霍尔的影响而已，肯定多少有一点磁通量的

• 李宙恒 说：

嗯 是的啊 最近一直在调试 发现霍尔的安装要很精确。。。

24. 李宙恒 说：

现在真的发现和每次做实验 就算与运放相连的电位器不变 但是arduino读出来的readvalue变化 每次实验都不一样 而且变化比较大。。有时候是300多 有时候500多。。
您说的是调整到浮子平衡为止，但是调到哪能平衡呢 总不能一个一个值去试吧。。

一般来说，我的那个磁悬浮隔一段时间以后，放上去就不能平衡了
这时候用手大概扶一下，电位器拧一圈，你能明显感觉到手上不吃劲，那就是到平衡位置了

• 李宙恒 说：

嗯 有两个电位器啊。。同时调？？？

25. 李宙恒 说：

还有就是 与线圈相连的L298的接线方向要不要特别注意的？？万一通电时候产生一个 通电的方向反了 也完蛋了 。。。

• 线圈一定要同级性对接，不然的话这个悬浮永远也成功不了
跟298n的连接倒是关系不大

• 李宙恒 说：

嗯 是同极性对接的 连接的时候测过。。

• 邪恶的眼神啊，我开始扫了一眼，以为是个关于同性恋的垃圾评论

• 李宙恒 说：

话说北航还是有不少同性恋的。。

• 看来你是业内人士啊，这都知道

26. 李宙恒 说：

还有。。。
参数应该怎么调 ，，，我现在把浮子放上去能感到很快的抖动了。。是程序的参数出问题了，还是其他的？？？？崩溃了。

• 小盆友，以我的经验看，浮子抖动就是快成功了！
先恭喜一下，基本上应该就是参数的问题了。
咱们的磁铁，线圈，浮子，霍尔位置都不一样，参数可能会差比较多，多试试吧

• 李宙恒 说：

浮子还是往下面的那个大磁铁那里飞！！！！
就是我把浮子往旁边推一点，试一试线圈的斥力，看能不能把它推回来。。结果线圈好像对浮子只有抖动似的。。浮子一下就飞到大磁铁上去了。。
我把PWM输出上限调到1023了 才有感觉 。。线圈不给力啊。。

• 线圈不给力就加电压，我用的20V的，哈哈

27. 李宙恒 说：

震动感很强 但是老震飞 把浮子放到中间地方也给震到旁边去了。。。是不是PID设置参数的问题 。。那这个参数应该咋样设置啊。。
还有，您那个坐标图里边的红点是啥？黑点是target吧？

• 参数….慢慢试吧，没啥好办法

• 李宙恒 说：

• 你的电源电压是多少？
线圈铜丝直径？线圈电阻多少？
感觉真的是线圈不给力

• 李宙恒 说：

今天弄好了 电流变大了 3A多。。。线圈的磁力还是蛮大的。。再就是控制上的问题了 。。。

28. 李宙恒 说：

我可不是“业内”，只是看见厕所上有很多XXXX

29. 老顽童 说：

有一个概念必须明确,运放就是专门的直流放大器.
这一点,可以去找书本核实.也可以剖析内部电原理.
········
整个磁悬浮电路中,就没有交流, 只有被换向的直流. 顶多是被PWM的直流.

如何分辨出是交流,还是被换向的直流呢?
交流总是会表现出有周期的特征.那怕是变周期的.

直流的按需换向就难有周期特征. 那怕是出现了机械振动的表象.

关键是看频率当家,还是位移作主.

• 李宙恒 说：

哦 是的。。我隐隐约约记起来了老师说的话。。。。看来我得好好补习基础知识了。。

• 看来确实是专业科班出身 ：）
不过我觉得PWM可以认为是一个稳定的直流叠加一个交流分量
老顽童说的周期特征来区分交流直流，我感觉不太合理
PWM的周期性就非常明显

• 李宙恒 说：

怎么就是不稳定呢不稳定呢。。。调啊调啊 调得我心都碎了。。。。
怎么这磁悬浮就不能给个面子呢。。？？

• 老顽童 说：

PWM是直流脉动,只变占空比不变方向,顶多算脉动周期,
还是直流性质 ,何来交流呢?

霍尔指导换向,不是周期指导换向,
所以,换向了,也不是交流.

比如,交流感应电机是频率主宰旋转.属交流电机.

永磁无刷电机由霍尔指导换向,尽管流向都变了,
却不是交流电机.因为这换向只和位移有关,与频率无关.

• 我明白你的意思，呵呵，必须是周期性+电流换向才是交流
我以前学力学的时候，做过波的叠加和分解
所以习惯性的把PWM这种直流脉冲分解为一个平均的直流分量，叠加一个交流分量
当然我这种观点在电学专业上看可能太不专业了，主要是术语用的不精确
受教了，多谢:)

• 老顽童 说：

不是啊. 咱现在真正计较的是,在黄二的那类电路上,就是无法捉到稳定的波形. 就是因为它根本就没有周期.

不似你那个5V的PWM,尽管只是脉动,总还能见到,对吧.

那么老头他们那时在讨论的频率,应该就只是自激了.

30. vvisper 说：

digitalWrite(i1Pin, Pid1.flag);
digitalWrite(i2Pin, !Pid1.flag);
analogWrite(power1Pin, Pid1.power);
这几行是控制线圈电流方向的吗？ 我怎么看不没明白呢 就像之前小电机实验里的这几行
//设置转动方向，I1和I2值相反时，分别对应两种不同的转向；I1和 I2值相同时停止转动
digitalWrite(i1Pin, buttonPressed);
digitalWrite(i2Pin, !buttonPressed);
我的意思是 比如一组水平放置的线圈 陀螺在旋转过程中水平向左偏移了 那么就应该控制左边的线圈产生斥力 此时右边的是吸引力 把它牵引像平衡位置 这是如何通过这几行代码实现的呢 我弄不明白了 。
另外 是不是说 陀螺偏移的越大 传感器感应电压就越大 放大后传入单片机的电压就大 就应该控制输出pwm增大线圈中的电流？ 也就是说除了给线圈供电的20v及其相应的电流外 EA端口收到的pwm信号会增大线圈中的电流？
我有点乱啊。。。

• 哈，你一点都没乱，最后一段说的全部正确
L298N你可以理解为一个放大器，EA=0的时候，线圈两端电压也是0，可以用万用表量一下检验。
输出PWM的时候，线圈两端的电压是方波，算平均电压的话就是在变化了

31. vvisper 说：

拜托你给我讲一下 控制线圈电流方向的那几个代码 我不明白他们是怎么控制的 就像我上面问到的问题 难道线圈中的电流是不断交替变化的吗 这样岂不是很不稳定。。

• 你把思路搞错了吧？
线圈里的电流就是不断交替变换的，不断变化的磁场才能维持动态平衡。

• vvisper 说：

对 我思路错了 晕。。

32. wlreg 说：

这个算法没有看懂啊，囧。没有学过PID算法，不知道有没有参考书籍推荐呢，老男孩大神 T T，先谢谢。

• 这个好像没有参考书，都是网上的文章一篇一篇的，拿出骗mm的决心，死缠烂打，其实很快就看明白了

• wlreg 说：

诶，我想的MM都不理我了，现在专心搞专业的说
话说
—————————————————————
TCCR0B = 0×01; // Timer 0: PWM 5 & 6 @ 16 kHz
TCCR1B = 0×01; // Timer 1: PWM 9 & 10 @ 32 kHz
TCCR2B = 0×01; // Timer 2: PWM 3 & 11 @ 32 kHz
—————————————————————
这段设置的是什么呢，看不懂……原本板子今天可以拿到的，但是因为某些原因和快递错失了，失望ing

• 这位同学，mm比专业重要，赶紧回去泡妞！
btw：这段的作用是加快PWM的频率，看这里：http://www.diy-robots.com/?p=852

• wlreg 说：

如果生命中出现过那个人，那别人就只能是将就。一直都是不将就自己走过来的，其他mm什么的一点兴趣都没有啊，和专业比起来她们都弱爆了。还真是很羡慕老男孩和圈圈妈啊。
原来老男孩筒子还有其他关于这个开发板的博文的呀，我还以为都在小爱的那个项目里哩。虽然还是看不懂，但是会努力看懂的。话说在成功弄出来之前我想都会打搅到老男孩筒子诶，由于是菜鸟希望老男孩筒子可以体谅一下哈！

• DaisyHUnte 说：

跨越时空的陈年狗粮

• wlreg 说：

那个“Arduino系列教程之 – PWM的秘密（下）”，我猜我大概是看懂了大部分吧，但是还是没有看懂
—————————————————————
TCCR0B = 0×01; // Timer 0: PWM 5 & 6 @ 16 kHz
TCCR1B = 0×01; // Timer 1: PWM 9 & 10 @ 32 kHz
TCCR2B = 0×01; // Timer 2: PWM 3 & 11 @ 32 kHz
—————————————————————
这个诶。教程上说TCCRnB是用来设置时钟的计数位数的，上面的程序上看，难道它们都是8位的吗。
不过貌似加快PWM频率的设置就只有这一段吧？这段设置完后下面的程序都不是关于PWM频率设置的是吧，吧？

33. wlreg 说：

今天特意把程序打印了出来一条条研究了，程序方面还是有那么一些多地方不太懂诶，或许要先从整个磁悬浮的是怎么运行入手开始理解吧。
1、看了攻略和上面的评论，整个装置通上电后通过电位器先将接运放的传感器的输出调节为2.5V，然后再放浮子上去。霍尔传感器感应的气势是浮子的磁通吗？然后通过传感器的输出变化反应浮子的倾斜程度？（错了请指正呀，感谢的说。）
今天在图书馆居然被我找到一本关于PID控制器的书，对照了一下上面的符号和程序上的符号，大概理解了一些之前不懂的变量的意义，但是程序上还是有地方不懂，希望老男孩筒子指导一下吧。
2、一开始的端口设置里，int rotatePin=3，rotatePin这个变量貌似一直都没有用过吧（只在上面程序注释里看见出现过）……
3、Kp是比例系数，Kd是微分系数，这个程序采用的是增进式PID？那么setKd，和setKp这些系数都是实验慢慢调试出来的吗？如果我做的有些部件(磁铁的大小之类的）的和老男孩筒子列表里的有出入，那么这些值都要自己找了？
4、程序第82行，if(debug) tick=0;
这里debug不是设定为false吗？后面也貌似没有看见有更改过这个值，那么这一句实质上是永远不会运行的吧？
5、程序第87行，if(tick2>=2;
这个是右移两位的意思吗？就是相当于除以4了，这个的意义何在呢？
7、程序144行，if(debug)下面的括起来的真的会运行吗？
8、程序第162行，delay(32000)
32000这个其实不用纠结的吧？但是总觉得很特殊的样子，为什么是这个设置……
9、程序第131行，Pid1.target = min(755, min(adjustValue1 + offset, Pid1.target));
755这个值是什么样来的呢？不是很明白的样子……
我知道老男孩筒子有工作平时也比较忙的，可能问题有些繁琐，会让人觉得烦，希望老男孩筒子有心情的时候指导一下吧。这里万分感谢了。

• wlreg 说：

又看了遍程序，发现第6个问题的原因是因为上面有一个for语句连续读了4次电位器的值，然后这里右移两位是为了取平均值的原因吧？

• YZL_HN 说：

我想请问，你加地两个电位器干嘛用的？

• DaisyHUnte 说：

我想骂人。。。。

• DaisyHUnte 说：

电位器是调节传感器标准电压用的，比如调节到2.5V，那么传感器数据就在2.5V为起点波动

• wlreg 说：

又看了遍程序，发现第6个问题的原因是因为上面有一个for语句连续读了4次传感器的值，然后这里右移两位是为了取平均值的原因吧？

• wlreg 说：

额，之前那个问题也问错了，程序的PID应该是位置型的……
不过按照那个位置型PID的公式，貌似下面的这段就有点不解了
——————————————————
v = error – Pid1.preError;
Pid1.v = (Pid1.v * 6 + v) / 7;
Pid1.power = (int)error * Pid1.Kd + Pid1.v * Pid1.Kp;
——————————————————
按照公式，这里应该是
Pid1.power =Pid1.Kd*v+Pid1.Kp*(int)error
不是吗？

• wlreg 说：

那个，又有了一个新问题。仔细看了一下程序
第87行
———————————————–
if(tick2<50) {tick = 0;return;}
————————————————
按照我学的C语言的理解（只是入门级别，可能有错），这里有一个return语句，就是执行到这个return的话就会跳出函数体了，而这里正在执行的函数式loop函数，也就是说执行到这里，就跳出loop函数不往下执行了，按照开发板的运作，loop这个函数又会被从头开始执行。而程序的113行和119行也有一个return语句……
其实我想问的是，按我的理解这段if(tick==500)这段程序是用来检测端口的值用的……其实特意弄tick和tick2这两个变量以及return语句这样来检测端口的用途是什么呢……这段有些弄不懂意图……

• vvisper 说：

wlreg 我这几天也在做这个陀螺 我的qq是397133023 如果你在北京就太好了 咱们当面谈谈 动力哥经常不在 我电路搭好了 最严重的是 我怎么测的不带陀螺的时候 传感器输出是2.57v啊 这么大 而通过放大器后的电压竟然不随磁场变化而变化 而且保持3.8v左右。。。。
晕死

• 传感器输出2.57V很正常，你需要把电位器的输出也调节到2.57V附近，就可以发现放大器后的电压有变化了。不然的话，这东西放大很多倍，稍微差一点就到最大电压了。

• vvisper 说：

我调了一下电位器 现在上电后在不放转子的情况下 从358出来的电压大小是0.05左右，两个都是 我是直接用万用表测的 放上浮子后变化范围是从0.xx到3.xx 总之没超过过4v 然后转子放上面总是飞出去 也有震动 但是线圈没多久就变得很烫 我就得断电 如果再继续 恐怕就着火了 。。。。 各种无语中 我的转子就是四个小钕铁硼 两小块加上两个大一点的薄片形的 上面还粘着个一元硬币。。

• 这个页面帖子太长了，我早上发了一个集中回答的页面，你们可以在那里讨论，然后我把讨论结果统一添到页面里。

• 你量过线圈的电阻吗？感觉特别烫的话，可能是铜丝比较粗吧？

• wlreg 说：

我的电路还在搭呢，主要是有些材料还没有到……导线、电位器和电阻什么的本来想在实验室拿的，但是还没有拿到手…………

• vvisper 说：

我是非常新手的新手 主要就是这段代码的有些地方看不懂 还说问问你呢 你赶紧加我qq吧 十万火急啊 呵呵

• wlreg 说：

QQ等我回宿舍才行。
问一下，你20V电源方面是怎样解决的呀？我发现L298N的接20V电源的接口是那个标注VCC的吧。那种接口的20V电源可以买到么

• 我是塞了一根电线在电源中心的孔里，然后缠了一段在外面的插头上，最后用黑胶布捆好，土法挺管用。

• vvisper 说：

呵呵 我是买的20v 3.25A的电源 本来说把那个接头剪掉劈成两半的 后来看到卖电源的地方都有那样的接头的（就跟arduino开发板那个9v电源一样的接头） 用线接上就可以了 正极vcc 负极接GND 咱们去新开的帖子讨论吧 这个太长。。。

34. vvisper 说：

ok 悬浮成功

• 赞一个，呵呵，你实现的速度真快，年轻人果然战斗力超强

• wlreg 说：

求交流呀。我想和你对一下每一个步骤。估计星期二电路就能搭好了。

35. weihan 说：

請問這PID的原理可應用在四軸飛行器上嗎??

origin串口分析具体使用谁会，各位推荐个靠谱的教程也行。
做磁悬浮分析分析，东西都买齐了，连板子选的都是和oldboy一样的，做电机实验L208一不小心烧坏了，现在在路上，你要为我负责啊！！我可被你骗上道了。。

37. 关于积分调节，我的理解是这样的：
还拿小球在光滑坡面上为例，你认为积分项修正在坡面运动时才需要用到，而我觉得是这样的：
物理平衡位置是x=L，如果我现在就像让小球平衡在x=L处。首先，微分调节我们是没有争议的；比例调节这里也没有争议，如果x>L则施加向左的力，如果x<L则施加向右的力，x=L则仅仅根据当前速度进行调节（微分调节）；积分调节貌似确实没用。
但如果我想要的平衡位置不是x=L处，那么在静止平衡时显然也是有一个恒力作用在小球上的，这个力不是比例调节带来的（Δx=0），也不是微分调节带来的（v=0），这是一个恒定的偏移量，如果平衡位置是x=L则这个偏移量恰好是0。我感觉这个偏移量是由积分调节积累出来的。
我看你在程序代码中也没有用到积分调节，那么当你想让悬浮物悬浮在非物理平衡位置时，实际平衡位置应该更远离物理平衡位置。因为当物体恰好处于设定的平衡位置时，比例调节为0，如果速度也为0的话线圈就不工作，而此时又不是在物理平衡位置，所以物体要在更远离物理平衡位置处达到平衡。
这是我对积分调节的理解，所谓消除静差（稳态误差）应该指的就是控制量的恒定的偏差吧？
在大神面前说话要小心啊，要是不小心错了也不要笑我啊~嘻嘻

• 老薛 说：

哈，我仔细阅读并理解了这一段文字，确实是正确无误的！

• 赞，总结的真好！不敢称大神啊，我要多向大家学习

38. homeboy 说：

你好，我想问问代码中：
int power1Pin = 5; //连接电机驱动板的EA接口
int power2Pin = 6; //连接电机驱动板的EB接口
int rotatePin = 3; //用来控制磁场旋转的PMW接口
在原理图当中EA和EB不是接了+5V了吗？另外PWM口又是哪个呢？

• DaisyHUnte 说：

EA和EB是接得信号端。。

39. rotate是本来设想的一种方案，让磁场旋转来诱导浮子旋转，后来发现没有效果，这段代码是无效的

40. 可乐 说：

你好，我的硬件都做好了，开始调试PID参数。打算从Kp开始调起，Kd为0。我用手放松地拿着磁浮放在正中间感受，请问当Kp接近合适的范围是磁浮应该是什么样地表现？
我从0.1开始试，刚开始的时候磁浮要很靠近电磁铁才会感受到斥力。我就把Kp直接增加到8，磁浮在平衡位置就能感受到两边一定的拉扯力，并且手拿着磁浮开始被吸引的摇晃。然后我以0.1为增量一路加到20，磁铁在平衡位置能感受到比较强的力，但是随之Kp增大，我手拿着磁浮开始越来越大幅度地摇晃，并不像我想象中那样，当Kp接近合适值时磁铁会被控制在平衡位置并比较高频地震动。到目前为止，我的磁浮一放上去立马就飞。

• 所以你现在只调节了 Kp 对吧？只有Kp的话，就会像你说的那样，左右的抖动，而且幅度会越来越大。这个是对的，你可以开始加上一些Kd，Kd偏小的时候表现就是震荡，Kd偏大的时候表现是无法稳定，从侧面滑走。
震荡的时候就是接近成功了，加油！

• 可乐 说：

那请问调Kp的时候调到什么程度可以着手开始调Kd呢？目前为止我说的抖动都是在我轻轻拿着磁浮的时候才会发生，如果我只是把磁浮往平衡位置一放，磁浮只会一下就飞到边上去了，没有看到有任何先震动然后幅度增大失去平衡的过程。我比较困惑的是不知道现在Kp是太大了还是太小了。有可能是太小了控制不住磁浮一下飞出去，也有可能是太大了一下过激了飞出去了。请问动力哥当时在调试的时候是怎么确定Kp是过大了还是过小了？非常感谢

• 可乐 说：

成功了！原来是我为了调试写的几行serial.print严重拖慢了arduino的频率，删了以后直接就好了！不过感谢动力哥的文章和解答

• 恭喜恭喜！
9600波特率下，发一个字节好像正好是1ms，果然很慢啊
以后就有经验了

另外，我有个请求，能否把你做的磁悬浮整理一下照片，发到创酷网上？ http://www.chuangkoo.com
这是我和几个朋友一起做的创客平台，希望许许多多和咱们一样的爱动手的人，有个交流分享的地方。
我希望做成中国的instructables，同时也是硬科技范儿的果壳

• 流云 说：

动力老男孩，您好！我最近也在尝试做磁悬浮，遇到了两个问题想跟大师您请教一下，谢谢了！
1）两个方向的hall元件平衡的target值是不是一样的？设置为多少合适呢？
2）要间隔多少时间进行一次PID调整呢？目前我设置的时间是大概5ms读取一次hall元件的值，然后根据当前的偏移方向进行PID调整，这个时间是否合适呢？您当时是多长时间调整一次呢？

• 如果安装的垂直的话，target应该一样，但是一般手动装不了那么准，会有一点儿误差。至于霍尔值是多少，跟你的磁铁有关，每个人都不一样
5ms测量一次够用的，每秒钟调节200次呢

• 流云 说：

老男孩，谢谢您及时回复，我目前将两个方向的hall元件输入到mcu的值通过电位器调整到2v左右，然后将target值就设置为2v，这样可以吗？接着去调Kp和Kd的参数。当kd为零时，调整kp，发现当值差不多是4的时候，刚放上浮子就会剧烈摆动，然后为了解决剧烈摆动的问题，我开始从0.1调整Kd，每次步进0.1，发现浮子会慢慢由剧烈摆动变为剧烈抖动，当调整到1.2时，基本上不会抖动了，但是浮子仍然会飞出去。我再加大kd的值，如果直接将其设置为30就会感觉到浮子在剧烈的抖动，然后就会飞出去。我想要知道kd设置到1.2时和30两种情况时，哪一种才是接近悬浮的感觉？再次表示感谢，谢谢！

• 你试试kd用1.2，把Kp降一点儿，到3左右

• 流云 说：

老男孩，您好！现在已经可以悬浮2s左右，但是2s后，浮子就会抖动然后被拉飞到环形磁铁上，这种情况下，需要调整Kp还是Kd？另外我发现虽然能悬浮但是电流很大，如何降低电流呢？谢谢了！

• 这种情况下，两个参数都需要微调。PID是个头疼的事情，最后还是需要费点儿劲尝试。
听说有自动匹配PID的办法，但是我还不会。
电流大的话，把上方的磁铁换大一点儿，也就是说浮子的重力主要由磁铁来承担，就可以减少耗电。
最完美的状态应该是线圈可以支持双向电流（可以吸也可以排斥），浮子的平衡位置基本在磁铁和重力的平衡点，那样的话基本不费电

• 流云 说：

谢谢动力老男孩的指点，可不可以这样理解，电流比较大主要还是找的平衡点并不是最优的平衡点，如果保持浮子不变，我可以尝试去重新调整平衡目标值，尽量让电磁铁只是去平衡水平位置，而不承担在垂直方向对浮子产生的力，即在垂直方向只让永磁铁产生的力等于浮子的重力。

• 没错！

41. 小生 说：

用串口我可以把数据传到电脑，但是你那个分析的图片是怎么做的呢

• 我用的是一个叫 Origin的软件，根据坐标点来生成图片

42. 东东东 说：

图里面由黑色的点线和红色的点线得出什么结论？需要定的目标值？不太明白啊~
还有串口输出的数据，你是自己一个一个写进去制图的吗，Origin软件已经下了~