小车用的是ARDUINO UNO+MPU6050+L289,目前再平衡状态,总是不停地抖动,下图是平衡状态角度波形图,javascript:;求大神们指点~
- #include <Wire.h>
- #include <i2cdev.h>
- #include <MPU6050.h>
- #include <PID_v1.h>
- #define Left1 10 //定义左直流电机输出
- #define Left2 11 //定义左直流电机输出
- #define Right1 5 //定义右直流电机输出
- #define Right2 6 //定义右直流电机输出
- #define drift 20 //小车角度调整范围
- #define Gry_offset -72 //陀螺仪X轴的静态飘移。
- #define Gyr_Gain -0.00763 //由陀螺仪X轴读数转换为角速度值(1/131)
- //向前方向与X轴为反方向,故乘以-1
- #define ACC_Gain 0.000061 //由读数转换为加速度值(1/16384)
- #define pi 3.1415926 //定义PI值
- //#define K 0.715 //一阶互补滤波权重取值,不知由来,可更改。
- #define Q_angle 0.01 //角度数据置信度
- #define Q_omega 0.0003 //角速度数据置信度
- #define R_angle 0.01 //方差噪声
- unsigned long preTime = 0;//定义积分初始时间
- float angleG = 0;
- double Klm_angle;
- float bias = 0;
- float P_00 = 0, P_01 = 0, P_10 = 0, P_11 = 0;
- double Setpoint, Input, Output;
- double temp_kp,temp_ki,temp_kd;
- MPU6050 accelgyro; //MPU6050初始化
- int16_t ax, ay, az;
- int16_t gx, gy, gz;
- PID myPID(&Input,&Output,&Setpoint,10,0.05,0,DIRECT);
- void car_stop() //小车停止函数
- {
- analogWrite(Left1,0);
- analogWrite(Right1,0);
- analogWrite(Left2,0);
- analogWrite(Right2,0);
- }
- void setup() //主程序初始化
- {
- Wire.begin();
- Serial.begin(9600);
- accelgyro.initialize(); //MPU6050初始化。
- pinMode(Left1,OUTPUT);
- pinMode(Left2,OUTPUT);
- pinMode(Right1,OUTPUT);
- pinMode(Right2,OUTPUT);
- Wire.begin(); //I2C初始化
- accelgyro.testConnection();
- Setpoint=-2.00; //设置平衡角
- myPID.SetSampleTime(10);
- myPID.SetMode(AUTOMATIC);
- }
- void loop()
- {
- unsigned long now = millis(); //当前时间(ms)
- float dt = (now - preTime) / 500.0; //微分时间(s)
- preTime = now; //上一次采样时间(ms)
- accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);//读取六轴数值
- float Y_Accelerometer = ay * ACC_Gain; //转换为向前的加速度(g),为负值
- float Z_Accelerometer = az * ACC_Gain; //转换为向下的加速度(g)
- float angleA= atan(Y_Accelerometer/ Z_Accelerometer)* (-180)/ pi;//加速度仪,反正切获得弧度值,乘以180度/pi
- //获得角度值,乘以-1得正值
- float gx_revised = gx + Gry_offset; //陀螺仪x轴静态时修正后的角速度读数,向前为负值
- float omega= Gyr_Gain* gx_revised; //陀螺仪,转换为向前的角速度(o/s),Gyr_Gain取负,故负负得正
-
- //卡尔曼滤波
- Klm_angle += (omega - bias) * dt; // 先验估计
- P_00 += -(P_10 + P_01) * dt + Q_angle *dt;
- P_01 += -P_11 * dt;
- P_10 += -P_11 * dt;
- P_11 += +Q_omega * dt; // 先验估计误差协方差
- float K_0 = P_00 / (P_00 + R_angle);
- float K_1 = P_10 / (P_00 + R_angle);
- bias += K_1 * (angleA - Klm_angle);
- Klm_angle += K_0 * (angleA - Klm_angle); // 后验估计
- P_00 -= K_0 * P_00;
- P_01 -= K_0 * P_01;
- P_10 -= K_1 * P_00;
- P_11 -= K_1 * P_01; // 后验估计误差协方差
- Serial.print(angleA);
- Serial.print(",");
- Serial.println(Klm_angle);
- //PID算法结合PWM输出
- if((Klm_angle>(Setpoint-drift))&&(Klm_angle<(Setpoint+drift)))
- {
- if((Klm_angle>Setpoint)&&(Klm_angle<(Setpoint+drift)))
- {
- myPID.SetControllerDirection(REVERSE);
- Input=Klm_angle;
- myPID.Compute();
- analogWrite(Left2,min(Output+150,255));
- analogWrite(Right2,min(Output+150,255));
- analogWrite(Left1,0);
- analogWrite(Right1,0);
- }
- if((Klm_angle<Setpoint)&&(Klm_angle>(Setpoint-drift)))
- {
- myPID.SetControllerDirection(DIRECT);
- Input=Klm_angle;
- myPID.Compute();
- analogWrite(Left1,min(Output+100,255));
- analogWrite(Right1,min(Output+100,255));
- analogWrite(Left2,0);
- analogWrite(Right2,0);
- }
- }
- else
- {
- car_stop();
- }
- }
复制代码 |