极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 182178|回复: 72

我的自平衡小车D4——PID控制器——第一次的直立行走

  [复制链接]
发表于 2012-3-28 12:18:27 | 显示全部楼层 |阅读模式
本帖最后由 黑马 于 2012-3-29 10:15 编辑

依旧先上代码
  1. /***************************
  2. 自平衡系统 for Arduino
  3. by 黑马
  4. 数据采集    2012-03-21
  5. 滤波        2012-03-23
  6. PID控制     2012-03-27
  7. PID整定
  8. 运动控制
  9. ***************************/

  10. #include <Wire.h>
  11. #include <Servo.h>

  12. /************ 传感器参数 ***********/
  13. #define Acc 0x1D           // ADXL345地址
  14. #define Gyr 0x69           // L3G4200D地址
  15. #define Mag 0x1E           // HMC5883L地址
  16. #define Gry_offset -13     // 陀螺仪偏移量
  17. #define Gyr_Gain 0.07      // 满量程2000dps时灵敏度(dps/digital)
  18. #define pi 3.14159

  19. /********** 互补滤波器参数 *********/
  20. unsigned long preTime = 0; // 采样时间
  21. float f_angle = 0.0;       // 滤波处理后的角度值

  22. /*********** PID控制器参数 *********/
  23. unsigned long lastTime;           // 前次时间
  24. float ITerm, lastInput;    // 积分项、前次输入
  25. float Output = 0.0;        // PID输出值

  26. /*********** 马达控制参数 **********/
  27. Servo servoL;              // 定义左驱
  28. Servo servoR;              // 定义右驱
  29. # define servoL_offset 90  // 左驱偏置
  30. # define servoR_offset 90  // 右驱偏置

  31. /************ 程序初始化 ***********/
  32. void setup() {
  33.     sensor_init();        // 配置传感器
  34.     Serial.begin(19200);  // 开启串口以便监视数据
  35.     servoL.attach(30);    // PIN30输出到左驱
  36.     servoR.attach(32);    // PIN32输出到右驱
  37.     delay(1000);
  38.   }
  39. /************** 主程序 *************/
  40. void loop() {
  41.     unsigned long now = millis();                           // 当前时间(ms)
  42.     float dt = (now - preTime) / 1000.0;                    // 微分时间(s)
  43.     preTime = now;                                          // 记录本次时间(ms)

  44. /********** 读取姿态传感器 *********/
  45.     float Y_Acc = gDat(Acc, 1);                             // 获取向前的加速度(digite)
  46.     float Z_Acc = gDat(Acc, 2);                             // 获取向下的加速度(digite)
  47.     float angleA = atan(Y_Acc / Z_Acc) * 180 / pi;          // 根据加速度分量得到的角度(degree)
  48.     float omega =  Gyr_Gain * (gDat(Gyr, 0) +  Gry_offset); // 当前角速度(degree/s)

  49. /*********** 一阶互补滤波 **********/
  50.     float K = 0.8;                                          // 取值权重
  51.     float A = K / (K + dt);                                 // 加权系数
  52.     f_angle = A * (f_angle + omega * dt) + (1-A) * angleA;  // 互补滤波算法
  53. /************ PID控制器 ***********/
  54.     now = millis();                                         // 当前时间(ms)
  55.     float TimeCh = (now - lastTime) / 1000.0;               // 采样时间(s)
  56.     float Kp = 10.0, Ki = 0.0, Kd = 0.0;                    // 比例系数、积分系数、微分系数
  57.     float SampleTime = 0.1;                                 // 采样时间(s)
  58.     float Setpoint = -3.8;                                  // 设定目标值(degree)
  59.     float outMin = -80.0, outMax = +80.0;                   // 输出上限、输出下限
  60.     if(TimeCh >= SampleTime) {                              // 到达预定采样时间时
  61.         float Input = f_angle;                              // 输入赋值
  62.         float error = Setpoint - Input;                     // 偏差值
  63.         ITerm+= (Ki * error * TimeCh);                      // 计算积分项
  64.         ITerm = constrain(ITerm, outMin, outMax);           // 限定值域
  65.         float DTerm = Kd * (Input - lastInput) / TimeCh;    // 计算微分项
  66.         Output = Kp * error + ITerm - DTerm;                // 计算输出值
  67.         Output = constrain(Output, outMin, outMax);         // 限定值域
  68.         servoL.write(Output + servoL_offset);               // 控制左驱
  69.         servoR.write(Output + servoR_offset);               // 控制右驱
  70.         lastInput = Input;                                  // 记录输入值
  71.         lastTime = now;                                     // 记录本次时间
  72.     }
  73. /************ 参数上传 ***********/
  74.     Serial.print(now);          // 计算时间
  75.     Serial.print(",");
  76.     Serial.print(f_angle, 6);   // 偏离角度
  77.     Serial.print(",");
  78.     Serial.print(Output, 6);    // PID输出值
  79.     Serial.print(";");
  80. // 控制微分时间
  81.     delay(10);
  82. }

  83. /***************************************
  84. 九轴姿态传感器寄存器读取函数
  85. For Arduino, by 黑马
  86. ****************************************
  87.         调用参数表
  88. ****************************************
  89.   type    device      axis
  90.                    0   1   2
  91. ADXL345     Acc    x   y   z
  92. L3G4200D    Gyr    x   y   z
  93. HMC5883L    Mag    x   z   y
  94. ****************************************
  95. Example
  96. ****************************************
  97. 00 #include <Wire.h>
  98. 01 #define Acc 0x1D;
  99. 02 #define Gyr 0x69;
  100. 03 #define Mag 0x1E;
  101. 04
  102. 05  void setup() {
  103. 06    sensor_init();
  104. 07    delay(1000);
  105. 08  }
  106. 09
  107. 10  void loop() {
  108. 11    int Z-Gyroscope;
  109. 12    Z-Gyroscope = gDat(Gyr, 2);
  110. 13    delay(50);
  111. 14  }
  112. ***************************************/

  113. int gDat(int device, int axis) {
  114.     int v;
  115.     byte vL, vH, address;               // 存放byte数值
  116.     if (device == Acc) address = 0x32;  // ADXL345的读数地址
  117.     if (device == Gyr) address = 0xA8;  // L3G4200D的读数地址
  118.     if (device == Mag) address = 0x03;  // HMC5883L的读数地址
  119.     address = address + axis * 2;       // 数据偏移-坐标轴
  120.     Wire.beginTransmission(device);     // 开始传输数据
  121.     Wire.send(address);                 // 发送指针
  122.     Wire.requestFrom(device, 2);        // 请求2 byte数据
  123.     while(Wire.available() < 2);        // 成功获取前等待
  124.     vL = Wire.receive();
  125.     vH = Wire.receive();                // 读取数据
  126.     Wire.endTransmission();             // 结束传输
  127.     if (device == Mag) v = (vL << 8) | vH;
  128.     else v = (vH << 8) | vL;            // 将byte数据合并为Int
  129.     return v;                           // 返回读书值
  130. }

  131. /********************************************
  132. 配置九轴姿态传感器
  133. ********************************************/
  134. void sensor_init() {

  135. /************ 配置 ADXL345 ***********/
  136.     writeRegister(Acc, 0x2D, 0b00001000);    // 测量模式

  137. /************ 配置L3G4200D ***********/
  138.     writeRegister(Gyr, 0x20, 0b00001111);    // 设置睡眠模式、x, y, z轴使能
  139.     writeRegister(Gyr, 0x21, 0b00000000);    // 选择高通滤波模式和高通截止频率
  140.     writeRegister(Gyr, 0x22, 0b00000000);    // 设置中断模式
  141.     writeRegister(Gyr, 0x23, 0b00110000);    // 设置量程(2000dps)、自检状态、SPI模式
  142.     writeRegister(Gyr, 0x24, 0b00000000);    // FIFO & 高通滤波

  143. /************ 配置HMC5883L ***********/
  144.     writeRegister(Mag, 0x02, 0x00);          // 连续测量
  145. }

  146. /********************************************
  147. 寄存器写入函数
  148. ********************************************/
  149. void writeRegister(int device, byte address, byte val) {
  150.     Wire.beginTransmission(device);          // 写入的传感器
  151.     Wire.send(address);                      // 写入地址
  152.     Wire.send(val);                          // 写入值
  153.     Wire.endTransmission();                  // 结束传输
  154. }
复制代码
这应该是最基础的框架了,调试的时候肯定还需要加进不少杂七杂八的东西,估计今天下班就可以开始做整体的调试了。不知道程序上有没有这么错漏,各位DX多指点啊。

PID算法参考三水的帖子写的,一些估计用不到的就没加……反正慢慢调吧,遇到问题再说。灰常感谢三水的帮助。

再来张拍的照片吧。没加光耦试了下,没发现什么问题,不知道还有没有必要加,加的话是不是要加个上拉电阻?一般加多大的合适呢?

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

 楼主| 发表于 2012-3-28 22:36:08 | 显示全部楼层
taotao71 发表于 2012-3-28 22:02
恭喜,恭喜!总算站起来了

是啊,不过肯定还要慢慢调参数,现在只用了P控制,积分和微分都没加

不过我犯了一个严重的错误,接线插口实在不应该放在上边啊,干扰太严重了,看来还得考虑改结构
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2012-3-28 12:24:21 | 显示全部楼层
有个疑问,自平衡车如果加点负载物,重心和转动惯量肯定会变,网上的自平衡车是自整定了呢还是只是加的负载还不足以让系统失稳?
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2012-3-28 21:34:56 | 显示全部楼层
哈哈第一次试机竟然就站起来了,只是调节范围还比较小,视频……上载中,手机拍的……好暗
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-3-28 21:37:21 | 显示全部楼层
接下来就要慢慢整定数据了……哈哈
回复 支持 反对

使用道具 举报

发表于 2012-3-28 21:58:27 | 显示全部楼层
{:soso_e142:} 注解好详细~~
回复 支持 反对

使用道具 举报

发表于 2012-3-28 22:02:57 | 显示全部楼层
恭喜,恭喜!总算站起来了
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-3-28 22:31:54 | 显示全部楼层
弘毅 发表于 2012-3-28 21:58
注解好详细~~

haha, 就当是学习笔记了,也方便DX们帮我挑挑毛病
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-3-28 22:56:07 | 显示全部楼层
有视频有真相……虽然拍得超级不给力

http://v.qq.com/boke/play/9VuIgjUYc6k.html
回复 支持 反对

使用道具 举报

发表于 2012-3-30 14:54:25 | 显示全部楼层
光耦就是为了隔离用,有时候脱机的瞬间电流可能会影响到单片机!你说的上拉是哪部份? 如果只是为了高电平的花,一般我们习惯用10k或者4.7k上拉电阻
回复 支持 反对

使用道具 举报

发表于 2012-3-30 16:22:39 | 显示全部楼层
{:soso_e102:},好帅的车车
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-3-30 16:41:32 | 显示全部楼层
开心就好 发表于 2012-3-30 14:54
光耦就是为了隔离用,有时候脱机的瞬间电流可能会影响到单片机!你说的上拉是哪部份? 如果只是为了高电平的 ...

谢谢~~我用的是一款连续舵机,现在看还没太大问题。4pin的光耦,三极管端要形成高低电平肯定得接上拉或者下拉的电阻吧
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-3-30 16:43:52 | 显示全部楼层
迷你强 发表于 2012-3-30 16:22
,好帅的车车

给点建议呗
回复 支持 反对

使用道具 举报

发表于 2012-3-30 20:17:53 | 显示全部楼层
本帖最后由 三水 于 2012-3-30 20:19 编辑

{:soso_e142:}哇!恭喜恭喜。一般网上的大多没用自整定,我看了几个开源的都是PI就够了,没有失稳的原因估计是重心还在可控范围内吧,因为他们设置不在可控范围内就停车了。

哈哈,你用的也是舵机来测试的?我之前也用舵机测试过,只要P调节就站住了。。现在也准备重新做做
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-3-31 09:15:16 | 显示全部楼层
三水 发表于 2012-3-30 20:17
哇!恭喜恭喜。一般网上的大多没用自整定,我看了几个开源的都是PI就够了,没有失稳的原因估计 ...

不过感觉转速不太给力,扭矩倒是很大,减速比太大。

最好玩的是有时候没调好,它会很执着的一下一下的撞桌腿,笑喷了{:soso__5448211862396791711_1:}
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-20 20:47 , Processed in 0.049858 second(s), 24 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表