极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 22294|回复: 10

自平衡小车,无论如何立不起来,可能是什么原因?修改,已经立起来了,谢谢pz_cloud

[复制链接]
发表于 2014-9-27 11:32:39 | 显示全部楼层 |阅读模式
本帖最后由 liangquan 于 2014-9-28 16:15 编辑

配置:
Arduino UNO
L298N
MPU6050
直流电机12V,无反馈,150r/min

采用PID_v1库对直流电机进行速度控制,希望小车不倒,可是无论怎样调整参数,最好的效果是小车大幅震荡,无法直立。

代码
  1. #include<Wire.h>
  2. #include "gyro_accel.h"
  3. #include "PID_v1.h"

  4. // Defining constants
  5. #define dt 1            // time difference in milli seconds
  6. #define rad2degree 57.3  // radian to degree conversion
  7. #define Filter_gain 0.95  // e.g. angle = angle_gyro*Filter_gain + angle_accel*(1-Filter_gain)

  8. // Global Variables
  9. unsigned long t = 0;      // Time Variables
  10. float angle_x_gyro=0, angle_y_gyro=0, angle_z_gyro=0, angle_x_accel=0, angle_y_accel=0, angle_z_accel=0, angle_x=0, angle_y=0, angle_z=0;

  11. // DC motor driver with L298N
  12. const int motor1PWMPin = 5; // PWM Pin of Motor 1
  13. const int motor1Polarity1 = 4; // Polarity 1 Pin of Motor 1
  14. const int motor1Polarity2 = 7;  // Polarity 2 Pin of Motor 1
  15. const int motor2PWMPin = 6;  // PWM Pin of Motor 2
  16. const int motor2Polarity1 = 8; // Polarity 1 Pin of Motor 2
  17. const int motor2Polarity2 = 12;  // Polarity 2 Pin of Motor 2

  18. int ValM1 = 255;  // Initial Value for PWM Motor 1
  19. int ValM2 = 255;  // Initial Value for PWM Motor 2

  20. double Setpoint, Input, Output;

  21. PID myPID(&Input, &Output, &Setpoint, 10, 0, 1, DIRECT);

  22. void setup()
  23. {
  24.   // MPU-6050
  25.   Serial.begin(115200);
  26.   Wire.begin();
  27.   MPU6050_ResetWake();
  28.   MPU6050_SetGains(0,1);  // Setting the lows scale
  29.   MPU6050_SetDLPF(0);    // Setting the DLPF to inf Bandwidth for calibration
  30.   MPU6050_OffsetCal();  // very important
  31.   MPU6050_SetDLPF(6);  // Setting the DLPF to lowest Bandwidth
  32.   
  33.   t = millis();
  34.   
  35.   // DC motor
  36.   pinMode(motor1PWMPin, OUTPUT);
  37.   pinMode(motor1Polarity1, OUTPUT);
  38.   pinMode(motor1Polarity2, OUTPUT);
  39.   
  40.   pinMode(motor2PWMPin, OUTPUT);
  41.   pinMode(motor2Polarity1, OUTPUT);
  42.   pinMode(motor2Polarity2, OUTPUT);
  43.   
  44.   // set enablePin of motor 1 high so that motor 1 can turn on
  45.   digitalWrite(motor1PWMPin, HIGH);
  46.   digitalWrite(motor1Polarity1, HIGH);
  47.   digitalWrite(motor1Polarity2, LOW);

  48.   // set enablePin of motor 2 high so that motor 2 can turn on  
  49.   digitalWrite(motor2PWMPin, HIGH);
  50.   digitalWrite(motor2Polarity1, HIGH);
  51.   digitalWrite(motor2Polarity2, LOW);
  52.   
  53.   Input = 0.0;
  54.   Setpoint = 0.0;
  55.   
  56.   myPID.SetMode(AUTOMATIC);
  57.   myPID.SetSampleTime(1);
  58. }

  59. void loop()
  60. {
  61.   t = millis();
  62.   
  63.   MPU6050_ReadData();
  64.   
  65.   angle_x_gyro = (gyro_x_scalled*((float)dt/1000)+angle_x);
  66.   angle_y_gyro = (gyro_y_scalled*((float)dt/1000)+angle_y);
  67.   angle_z_gyro = (gyro_z_scalled*((float)dt/1000)+angle_z);

  68.   angle_z_accel = atan(accel_z_scalled/(sqrt(accel_y_scalled*accel_y_scalled+accel_x_scalled*accel_x_scalled)))*(float)rad2degree;
  69.   angle_y_accel = -atan(accel_x_scalled/(sqrt(accel_y_scalled*accel_y_scalled+accel_z_scalled*accel_z_scalled)))*(float)rad2degree;
  70.   angle_x_accel = atan(accel_y_scalled/(sqrt(accel_x_scalled*accel_x_scalled+accel_z_scalled*accel_z_scalled)))*(float)rad2degree;

  71.   angle_x = Filter_gain*angle_x_gyro+(1-Filter_gain)*angle_x_accel;
  72.   angle_y = Filter_gain*angle_y_gyro+(1-Filter_gain)*angle_y_accel;
  73.   angle_z = Filter_gain*angle_z_gyro+(1-Filter_gain)*angle_z_accel;
  74.   
  75.   digitalWrite(motor1PWMPin, HIGH);
  76.   digitalWrite(motor2PWMPin, HIGH);
  77.   
  78.   Serial.print(angle_x);
  79.   Serial.print("\t");
  80.   
  81.   // change direction is very important
  82.   if(angle_x>0)
  83.   {
  84.     myPID.SetControllerDirection(REVERSE);
  85.    
  86.     // set enablePin of motor 1 high so that motor 1 can turn on
  87.     digitalWrite(motor1Polarity1, HIGH);
  88.     digitalWrite(motor1Polarity2, LOW);

  89.     // set enablePin of motor 2 high so that motor 2 can turn on  
  90.     digitalWrite(motor2Polarity1, HIGH);
  91.     digitalWrite(motor2Polarity2, LOW);
  92.   }
  93.   else
  94.   {
  95.     myPID.SetControllerDirection(DIRECT);
  96.     // set enablePin of motor 1 high so that motor 1 can turn on
  97.     digitalWrite(motor1Polarity1, LOW);
  98.     digitalWrite(motor1Polarity2, HIGH);

  99.     // set enablePin of motor 2 high so that motor 2 can turn on  
  100.     digitalWrite(motor2Polarity1, LOW);
  101.     digitalWrite(motor2Polarity2, HIGH);
  102.   }
  103.   
  104.   Input = angle_x;
  105.   Serial.print(Input);
  106.   Serial.print("\t");
  107.   
  108.   myPID.Compute();
  109.   Serial.print(Output);
  110.   Serial.print("\n");
  111.   
  112.   analogWrite(motor1PWMPin, Output);
  113.   analogWrite(motor2PWMPin, Output);
  114.   
  115.   while((millis()-t) < dt)  // Maing sure the cycle time is equal to dt
  116.   {
  117.     // Do nothing
  118.   }
  119. }
复制代码



视频中的试验是通过USB线为Arduino供电,USB线起到了辅助支撑的作用,如果撤掉USB线,小车将震荡的更加厉害,最终无法稳定直立。

可能是什么原因呢?

1. MPU6050放置的不平?

2. 小车的电机最高转速太慢或太快?

3. PID参数还不对?

视频

回复

使用道具 举报

 楼主| 发表于 2014-9-28 16:00:15 | 显示全部楼层
本帖最后由 liangquan 于 2014-9-28 16:11 编辑
pz_cloud 发表于 2014-9-28 14:04
跟重心一样,加速度计也是越贴近轮轴越好,放高了会发现噪声非常严重,这是我试验过的

因为车子不是 ...


一语惊醒梦中人。
我将MPU6050的高度降低,只用一个参数P,小车就立起来了。

您的一句话避免了我走许多弯路,谢谢!!!


回复 支持 1 反对 0

使用道具 举报

发表于 2014-9-27 12:30:32 | 显示全部楼层
虽然我没有玩过平衡小车,但是从你的硬件配置及软件来看,没有反馈,就需要用模糊理论控制,也就是必须选择一个正确的常量,决定这个常量的因素有很多,电机转速、电压、负载、车轮大小、6050的安装位置等等,这些因素中,有一个变化,相应的常量值就需要改变,所以,小车立不起来是正常的,建议你在电路中加入电机反馈控制,调整起来会相对容易一点,否则,只能靠运气了!另外,电机的电压也是很关键的因素,电压决定转速!除非你用步进电机。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-9-27 19:32:17 | 显示全部楼层
zzp1383 发表于 2014-9-27 12:30
虽然我没有玩过平衡小车,但是从你的硬件配置及软件来看,没有反馈,就需要用模糊理论控制,也就是必须选择 ...

非常感谢您中肯的回答,我尝试用您说的方法改进一下。

我想到的几点:
我打算提高小车的重心,生活的经验告诉我,用手来平衡一个短棍保持直立,比用手平衡一个长棍保持直立,要难一些,我打算提高小车的重心试试。

加反馈需要在电机后面加编码器吧?我现在买的电机后端无轴伸,恐怕加编码器要困难,看来立不起来,要买新的电机了
回复 支持 反对

使用道具 举报

发表于 2014-9-28 12:03:58 | 显示全部楼层
    提高重心应该是一个不错的做法,我看到你的帖子,一时手痒,也准备仿一个,正在准备材料过程中,当然是借用你的程序,谢谢!也建议你在手头没有编码器电机的情况下,是否考虑步进电机?
    传感器加高,带来的数据变化应该是角度范围扩大,PID常量也需要对应加大,成熟、完整的自平衡小车,还需要考虑负载因素。我会关注你的帖子,并向你持续学习,再次感谢!!!!!!!!!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-9-28 13:12:57 | 显示全部楼层
zzp1383 发表于 2014-9-28 12:03
提高重心应该是一个不错的做法,我看到你的帖子,一时手痒,也准备仿一个,正在准备材料过程中,当然是 ...

互相学习,如果您解决了我的问题,也请告诉我!谢谢!
回复 支持 反对

使用道具 举报

发表于 2014-9-28 13:54:26 | 显示全部楼层
本帖最后由 pz_cloud 于 2014-9-28 13:58 编辑
liangquan 发表于 2014-9-27 19:32
非常感谢您中肯的回答,我尝试用您说的方法改进一下。

我想到的几点:


1.pid采样时间太短了,可能导致角度获取不准确,一般5ms完全足够了,对arduino的速度来说,10ms比较合适

2.很明确地告诉你,重心越低越好,一个极限情况是,当低至轮轴,就不需要调节也能平衡了,我们做飞思卡尔的时候都是尽量把重心降低甚至不惜增加重量改变重心

3.影响车子的参数太多了,一步一步排查,从角度获取开始,把融合的角度串口输出来,看有没有漂移,延时怎么样,这是稳定的基础,然后看你的描述应该参数有点大了,先调p,另两个参数置0,大概可以前后震荡着平衡了就够了,再一点点加d,最后看有没必要加i,这个跟你电机有关,电机响应好的话不需要i

4.需要注意的是,参数不一定成比例的,一个参数可能比另一个大一个甚至好几个数量级,不要弄错标度,比如一开始就给d加1,可能d的合适参数是0.01

5.可以看一下我发的那个帖子里的直立车的指导教程,会很有帮助
回复 支持 反对

使用道具 举报

发表于 2014-9-28 14:04:41 | 显示全部楼层
zzp1383 发表于 2014-9-28 12:03
提高重心应该是一个不错的做法,我看到你的帖子,一时手痒,也准备仿一个,正在准备材料过程中,当然是 ...


跟重心一样,加速度计也是越贴近轮轴越好,放高了会发现噪声非常严重,这是我试验过的

因为车子不是刚体,而加速度计是噪声敏感原件,陀螺仪影响不大
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-9-28 14:32:12 | 显示全部楼层
pz_cloud 发表于 2014-9-28 14:04
跟重心一样,加速度计也是越贴近轮轴越好,放高了会发现噪声非常严重,这是我试验过的

因为车子不是 ...

我的MPU-6050是加速度计和陀螺仪的组合,我试试把它放的低一点
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-9-28 14:32:33 | 显示全部楼层
pz_cloud 发表于 2014-9-28 13:54
1.pid采样时间太短了,可能导致角度获取不准确,一般5ms完全足够了,对arduino的速度来说,10ms比较合适 ...

谢谢您的回答这么详细,我试试!
回复 支持 反对

使用道具 举报

发表于 2014-9-28 16:40:51 | 显示全部楼层
不客气,新手善用搜索可以避免很多麻烦,论坛里平衡车的资料很多了

http://geek-workshop.com/thread-5688-1-1.html

http://geek-workshop.com/thread-9683-1-1.html

这是我以前发的方案,可以参考一下
回复 支持 反对

使用道具 举报

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

本版积分规则

Archiver|联系我们|极客工坊

GMT+8, 2026-6-15 17:28 , Processed in 0.039831 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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