冷静一点后,觉得四轴飞行器一步到位不太现实,不如分三步曲去做
计划:
1. 用PID控制单维度(如角度),达到较好的自动化效果
2. 自平衡小车(实际上就是1加上轮子)
3. 四轴飞行器
第一步昨天做通了,在PID参数上有些体会,先上视频,效果是让陀螺仪保持45度水平角
再上代码
- #include <I2Cdev.h>
- #include <MPU6050.h>
- #include <Wire.h>//添加必须的库文件
- //====一下三个定义了陀螺仪的偏差===========
- #define Gx_offset 0.2
- #define Gy_offset 0.81
- #define Gz_offset -0.88
- //====================
- MPU6050 accelgyro;
-
- int16_t ax,ay,az;
- int16_t gx,gy,gz;//存储原始数据
- double aax,aay,aaz,ggx,ggy,ggz;//存储量化后的数据
- double tx,ty;
- double Ax,Ay,Az;//单位 g(9.8m/s^2)
- double Gx,Gy,Gz;//单位 °/s
- static double vari_estima_X= 0.1;
- static double vari_estima_Y= 0.1;
- static double vari_metron=0.0001;
- static double KG_X = 0.5;
- static double KG_Y = 0.5;
- static double metron_X =0;
- static double metron_Y =0;
- static double estima_X = 0.2;
- static double estima_Y = 0.2;
- static int time_span_micro =50;
- static float time_span =0.05;
- double now_error, last_error, error_diff, error_int;
- double fusion_angle =0;
- double Angel_accX,Angel_accY,Angel_accZ;//存储加速度计算出的角度
-
- long LastTime,NowTime,TimeSpan;//用来对角速度积分的
-
- void setup()//MPU6050的设置都采用了默认值,请参看库文件
- {
- Wire.begin();
-
- Serial.begin(38400);
-
- Serial.println("Initializing I2C device.....");
- accelgyro.initialize();
-
- Serial.println("Testing device connections...");
- Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful":"MPU6050 connection failure");
-
- }
-
- void loop()
- {
- accelgyro.getMotion6(&ax,&ay,&az,&gx,&gy,&gz);//获取三个轴的加速度和角速度
- // Serial.print(ax/16384.00);
- // Serial.print(gx/131.00);
- //======一下三行是对加速度进行量化,得出单位为g的加速度值
- Ax=ax/16384.00;
- Ay=ay/16384.00;
- Az=az/16384.00;
- //==========以下三行是用加速度计算三个轴和水平面坐标系之间的夹角
- //请参考:[url]http://www.geek-workshop.com/forum.php?mod=viewthread&tid=2328&page=1#pid27876[/url]
- //个人觉得原帖中case0算的不对,应该是z/sqrt(x*x+y*y),估计是江南楼主写错了
- Angel_accX=atan(Ax/sqrt(Az*Az+Ay*Ay))*180/3.14 -Gx_offset; //offset
- Angel_accY=atan(Ay/sqrt(Ax*Ax+Az*Az))*180/3.14 -Gy_offset; //offset
- Angel_accZ=atan(Az/sqrt(Ax*Ax+Ay*Ay))*180/3.14;
- //==========以下三行是对角速度做量化==========
-
- // ggx=gx/131.00;
- // ggy=gy/131.00;
- // ggz=gz/131.00;
-
- //===============以下是对角度进行积分处理================
- NowTime=millis();//获取当前程序运行的毫秒数
- TimeSpan=NowTime-LastTime;//积分时间这样算不是很严谨
- //下面三行就是通过对角速度积分实现各个轴的角度测量,当然假设各轴的起始角度都是0
-
- Gx=Gx+(ggx-Gx_offset)*TimeSpan/1000;
- Gy=Gy+(ggy-Gy_offset)*TimeSpan/1000;
- Gz=Gz+(ggz-Gz_offset)*TimeSpan/1000;
-
- automatic();
-
- LastTime=NowTime;
-
- // analogWrite(6, 0);
- // analogWrite(5, 80); //conter clockwise
-
- // KG_X = KG_cal(vari_estima_X, vari_metron) ;
- // KG_Y = KG_cal(vari_estima_Y, vari_metron) ;
- KG_X =0.1;
- KG_Y =0.1;
- estima_X = estima_cal(estima_X, Angel_accX, KG_X);
- estima_Y = estima_cal(estima_Y, Angel_accY, KG_Y);
- // vari_estima_X = vari_estima_cal(KG_X, vari_estima_X);
- // vari_estima_Y = vari_estima_cal(KG_Y, vari_estima_Y);
-
- //==============================
- // Serial.print(Angel_accX);
- // Serial.print(",");
- // Serial.println(Angel_accY);
-
- // Serial.print(estima_X);
- // Serial.print(",");
- // Serial.println(estima_Y);
-
- // Serial.print(Ax);
- // Serial.print(",");
- // Serial.println(Ay);
-
- //delay(time_span_micro);//这个用来控制采样速度
- }
- //void updateKalman (double my_metron_X, double my_metron_Y){
- // KG_X = KG_cal(vari_estima_X, vari_metron);
- // KG_Y = KG_cal(vari_estima_Y, vari_metron);
- // estima_X = estima_cal(estima_X, my_metron_X, KG_X);
- // estima_Y = estima_cal(estima_Y, my_metron_Y, KG_Y);
- // vari_estima_X = vari_estima_cal(KG_X, vari_estima_X);
- // vari_estima_Y = vari_estima_cal(KG_Y, vari_estima_Y);
- //}
- //------------------------------------------------------------------
- double KG_cal(double vari_estima, double vari_metron){
- double temp = vari_estima*vari_estima / (vari_estima*vari_estima+ vari_metron*vari_metron);
- double result =sqrt(temp);
- return result;
- }
- //------------------------------------------------------------------
- double estima_cal (double estima, double metron, double KG){
- double result1 = estima + KG*(metron - estima);
- return result1;
- }
- //------------------------------------------------------------------
- double vari_estima_cal (double KG, double vari_estima){
- double result2 = vari_estima * sqrt(1-KG);
- return result2;
- }
- double PID_control(double error, double error_diff, double error_int ){
- double P=2;
- double I=1;
- double D=1;
- double command = P*error + I*error_int + D*error_diff;
- return command;
- }
- void automatic(){
-
- // Serial.print("now_error=");
- // Serial.println(now_error);
- // Serial.print("error_diff=");
- // Serial.println(error_diff);
- double command = PID_control(now_error, error_diff, error_int);
- // Serial.print("command=");
- // Serial.println(command);
-
- int regu_pwm = int (command);
-
- //Serial.println(regu_pwm);
-
- if(regu_pwm >= 250){
- regu_pwm=250;
- analogWrite(6, 0);
- analogWrite(5, regu_pwm); //conter clockwise
- Serial.println("clock");
-
- }else if (regu_pwm > 0 & regu_pwm<250){
- analogWrite(6, 0);
- analogWrite(5, regu_pwm); //conter clockwise
- Serial.println("clock");
-
- }else if (regu_pwm <= 0 & regu_pwm > -250){
- analogWrite(5, 0);
- analogWrite(6, regu_pwm); // clockwise
- Serial.println("CONTER CLOCK");
- }else if (regu_pwm <=-250){
- regu_pwm=250;
- analogWrite(5, 0);
- analogWrite(6, regu_pwm); // clockwise
- Serial.println("CONTER CLOCK");
- }
- delay (time_span_micro);
- //fusion_angle = update_Estimat(fusion_angle, estima_Y, Gy );
- autoCorrection();
- Serial.print("estima_Y=");
- Serial.println(estima_Y);
-
- // uodate_error(time_span, 45, Angel_accY);
- uodate_error(time_span, 45, estima_Y);
- //uodate_error(time_span, 45, fusion_angle);
- }
- void uodate_error(double time_span, double consigne, double mesure){
- now_error = consigne - mesure;
- error_diff = (now_error - last_error)/time_span;
- error_int = error_int + now_error*time_span;
- last_error =now_error ;
- }
- double update_Estimat(double estimate, double metron_1, double metron_2){
- double temp = 0.4* estimate + 0.3*metron_1 + 0.3*metron_2;
- return temp;
- }
- void autoCorrection (){
- if ( Angel_accX > -0.2 & Angel_accX < 0.2 ){
- Gx=0;
- }
- }
复制代码
接下来是体会,有这么几点:
1。 P参数越大,回复速度越快,但同时越容易调整过头出现震荡,因此适合同时调大D参数,预计惯性累加量来及时减小电机转动输出(接近要到达位置时)。故曰:大P配大D,小P配小D
2 。 卡尔曼滤波函数 updateKalman 其实没有完全调通,主要是卡尔曼增益一下子就趋近于0了,导致角度响应变得非常慢。不过,如果一直将卡尔曼增益固定在0.1 (见estima_cal函数),反而波形变得很平滑而且响应速度很好。
3。 自己做的融合算法简直就是狗P (见fusion_angle),暂时不报以希望。网上说通过三轴角速度得到的角度容易受运动影响,我保持一个角度拿着mpu6050,突然超某方向运动(但维持角度不变),在串口输出上看到影响不是那么大,可能是因为已经滤过波的吧。
4。 PID函数仍然不那么精确, 理论上说在平衡位置(这里设的是45度)时,PID控制的输出量为零,但我这里还有个十几,当然十几的pwm是带不动电机的,所以视频上面表现的还可以。
5。 autoCorrection函数是用来修正陀螺仪误差的,每次三轴加速度得到的角度为零时,也会将陀螺仪积分的角度归零,可囧的是,如果一直保持非水平位置的话,好像都没有修正的机会
希望各位为我做指点。
|