• 平衡车终于成功了


    说来惭愧2017-12-0118:13:27

    并非原创,代码资料也是从论坛搜刮的。自己做了适配性的调整。

    这个小车断断续续造了将近1个月!

      1 #include  "Wire.h"`
      2 #include <U8g2lib.h>
      3 #include <SPI.h>
      4 const uint8_t IMUAddress = 0x68; // AD0 is logic low on the PCB
      5 const uint16_t I2C_TIMEOUT = 100; // Used to check for errors in I2C communication
      6 
      7 uint8_t i2cWrite(uint8_t registerAddress, uint8_t data, bool sendStop) {
      8   return i2cWrite(registerAddress, &data, 1, sendStop); // Returns 0 on success
      9 }
     10 
     11 uint8_t i2cWrite(uint8_t registerAddress, uint8_t *data, uint8_t length, bool sendStop) {
     12     Wire.beginTransmission(IMUAddress);
     13     Wire.write(registerAddress);
     14     Wire.write(data, length);
     15     uint8_t rcode = Wire.endTransmission(sendStop); // Returns 0 on success
     16     if (rcode) {
     17       Serial.print(F("i2cWrite failed: "));
     18       Serial.println(rcode);
     19     }
     20     return rcode; // See: http://arduino.cc/en/Reference/WireEndTransmission
     21 }
     22 
     23 uint8_t i2cRead(uint8_t registerAddress, uint8_t *data, uint8_t nbytes) {
     24     uint32_t timeOutTimer;
     25     Wire.beginTransmission(IMUAddress);
     26     Wire.write(registerAddress);
     27     uint8_t rcode = Wire.endTransmission(false); // Don't release the bus
     28     if (rcode) {
     29       Serial.print(F("i2cRead failed: "));
     30       Serial.println(rcode);
     31       return rcode; // See: http://arduino.cc/en/Reference/WireEndTransmission
     32     }
     33     Wire.requestFrom(IMUAddress, nbytes, (uint8_t)true); // Send a repeated start and then release the bus after reading
     34     for (uint8_t i = 0; i < nbytes; i++) {
     35       if (Wire.available())
     36         data[i] = Wire.read();
     37       else {
     38         timeOutTimer = micros();
     39         while (((micros() - timeOutTimer) < I2C_TIMEOUT) && !Wire.available());
     40         if (Wire.available())
     41           data[i] = Wire.read();
     42         else {
     43           Serial.println(F("i2cRead timeout"));
     44           return 5; // This error value is not already taken by endTransmission
     45         }
     46       }
     47     }
     48     return 0; // Success
     49 }
     50 
     51 /******************************************************/
     52 
     53 //2560 pin map  引脚定义好即可,然后改变一下PID的几个值(kp,kd,ksp,ksi)即可,剩下的全部都是固定的程序,
     54 //可能小车会有一点重心不在中点的现象,加一下角度值或者减一点即可
     55 //至于每个MPU6050的误差,自己调节一下即可,不是很难
     56 //调试时先将速度环的ksp,ksi=0,调到基本可以站起来,然后可能会出现倒,或者自动跑起来的时候加上速度环
     57 //这时就会很稳定的站起来,然后用小力气的手推不会倒。
     58 
     59 int ENA=10;
     60 int ENB=11;
     61 int IN1=4;
     62 int IN2=5;
     63 int IN3=6;
     64 int IN4=7;
     65 int MAS,MBS;
     66 /* IMU Data */
     67 double accX, accY, accZ;
     68 double gyroX, gyroY, gyroZ;
     69 int16_t tempRaw;
     70 double gyroXangle, gyroYangle; // Angle calculate using the gyro only
     71 double compAngleX, compAngleY; // Calculated angle using a complementary filter
     72 double kalAngleX, kalAngleY; // Calculated angle using a Kalman filter
     73 uint8_t i2cData[14]; // Buffer for I2C data
     74 uint32_t timer;
     75 unsigned long lastTime;      
     76 /***************************************/
     77 double P[2][2] = {{ 1, 0 },{ 0, 1 }};
     78 double Pdot[4] ={ 0,0,0,0};
     79 static const double Q_angle=0.001, Q_gyro=0.003, R_angle=0.5,dtt=0.005,C_0 = 1;
     80 double q_bias, angle_err, PCt_0, PCt_1, E, K_0, K_1, t_0, t_1;
     81 double angle,angle_dot,aaxdot,aax;
     82 double position_dot,position_dot_filter,positiono;
     83 /*-------------Encoder---------------*/
     84 
     85 #define LF 0
     86 #define RT 1
     87 
     88 //The balance PID
     89 float kp,kd,ksp,ksi;
     90 
     91 int Lduration,Rduration;
     92 boolean LcoderDir,RcoderDir;
     93 const byte encoder0pinA = 2;
     94 const byte encoder0pinB = 8;
     95 byte encoder0PinALast;
     96 const byte encoder1pinA = 3;
     97 const byte encoder1pinB = 9;
     98 byte encoder1PinALast;
     99 
    100 int RotationCoder[2];
    101 int turn_flag=0;
    102 float move_flag=3.5;//////////////////////////////////
    103 float right_need = 0, left_need = 0;;
    104 
    105 int pwm;
    106 int pwm_R,pwm_L;
    107 float range;
    108 float range_error_all;
    109 float wheel_speed;
    110 float last_wheel;
    111 float error_a=0;
    112 U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 12, /* data=*/ 13, /* reset=*/ U8X8_PIN_NONE);   // 四角显示屏
    113 void Kalman_Filter(double angle_m,double gyro_m)
    114 {
    115     angle+=(gyro_m-q_bias) * dtt;
    116     Pdot[0]=Q_angle - P[0][1] - P[1][0];
    117     Pdot[1]=- P[1][1];
    118     Pdot[2]=- P[1][1];
    119     Pdot[3]=Q_gyro;
    120     P[0][0] += Pdot[0] * dtt;
    121     P[0][1] += Pdot[1] * dtt;
    122     P[1][0] += Pdot[2] * dtt;
    123     P[1][1] += Pdot[3] * dtt;
    124     angle_err = angle_m - angle;
    125     PCt_0 = C_0 * P[0][0];
    126     PCt_1 = C_0 * P[1][0];
    127     E = R_angle + C_0 * PCt_0;
    128     K_0 = PCt_0 / E;
    129     K_1 = PCt_1 / E;
    130     t_0 = PCt_0;
    131     t_1 = C_0 * P[0][1];
    132     P[0][0] -= K_0 * t_0;
    133     P[0][1] -= K_0 * t_1;
    134     P[1][0] -= K_1 * t_0;
    135     P[1][1] -= K_1 * t_1;
    136     angle+= K_0 * angle_err;
    137     q_bias += K_1 * angle_err;
    138     angle_dot = gyro_m-q_bias;//也许应该用last_angle-angle
    139 }
    140  
    141 void setup() {
    142     Wire.begin();
    143     u8g2.begin();   //选择U8G2模式,或者U8X8模式
    144     Serial.begin(9600);
    145     pinMode(IN1, OUTPUT);
    146     pinMode(IN2, OUTPUT);
    147     pinMode(IN3, OUTPUT);
    148     pinMode(IN4, OUTPUT);  
    149     pinMode(ENA, OUTPUT);
    150     pinMode(ENB, OUTPUT);
    151     TWBR = ((F_CPU / 400000L) - 16) / 2; // Set I2C frequency to 400kHz
    152  
    153     i2cData[0] = 7; // Set the sample rate to 1000Hz - 8kHz/(7+1) = 1000Hz
    154     i2cData[1] = 0x00; // Disable FSYNC and set 260 Hz Acc filtering, 256 Hz Gyro filtering, 8 KHz sampling
    155     i2cData[2] = 0x00; // Set Gyro Full Scale Range to ±250deg/s
    156     i2cData[3] = 0x00; // Set Accelerometer Full Scale Range to ±2g
    157     while (i2cWrite(0x19, i2cData, 4, false)); // Write to all four registers at once
    158     while (i2cWrite(0x6B, 0x01, true)); // PLL with X axis gyroscope reference and disable sleep mode
    159  
    160     while (i2cRead(0x75, i2cData, 1));
    161     if (i2cData[0] != 0x68) { // Read "WHO_AM_I" register
    162         Serial.print(F("Error reading sensor"));
    163         while (1);
    164     }
    165 
    166     delay(20); // Wait for sensor to stabilize
    167 
    168     while (i2cRead(0x3B, i2cData, 6));
    169     accX = (i2cData[0] << 8) | i2cData[1];
    170     accY = (i2cData[2] << 8) | i2cData[3];
    171     accZ = (i2cData[4] << 8) | i2cData[5];
    172 
    173 
    174     double roll  = atan2(accX, accZ) * RAD_TO_DEG;
    175     EnCoderInit();
    176     timer = micros();
    177 
    178       //The balance PID
    179     kp= 42;//42;//24.80;43
    180     kd= 2.0;//1.9;//9.66;1.4
    181     ksp=8.5;//8.5;//4.14;
    182     ksi=2.1;//2.1;//0.99; 0.550
    183      u8g2.firstPage();
    184   do {
    185      u8g2.setFont(u8g2_font_ncenB14_tr); //设置字体/////u8g2_font_5x7_tr
    186      u8g2.setCursor(0, 15);    //设置光标处
    187      u8g2.print("MiniBlack");  //输出内容
    188      //u8g2.setCursor(0,30);    //设置光标处
    189      //u8g2.print("T:");  //输出内容
    190   } while ( u8g2.nextPage() );
    191 }
    192 
    193 void EnCoderInit()
    194 {
    195     pinMode(8,INPUT);
    196     pinMode(9,INPUT);
    197     attachInterrupt(LF, LwheelSpeed, RISING);
    198     attachInterrupt(RT, RwheelSpeed, RISING);
    199 }
    200 
    201 void pwm_calculate()
    202 {
    203     unsigned long  now = millis();       // 当前时间(ms)
    204     int Time = now - lastTime;
    205     int range_error;
    206     range += (Lduration + Rduration) * 0.5;
    207     range *= 0.9;
    208     range_error = Lduration - Rduration;
    209     range_error_all += range_error;
    210     
    211     wheel_speed = range - last_wheel;   
    212     //wheel_speed = constrain(wheel_speed,-800,800);
    213     //Serial.println(wheel_speed);
    214     last_wheel = range;  
    215     pwm = (angle + 0.825) * kp + angle_dot * kd + range * ksp + wheel_speed * ksi;     
    216     if(pwm > 255)pwm = 255;
    217     if(pwm < -255)pwm = -255;
    218     
    219     if(turn_flag == 0)
    220     {
    221          pwm_R = pwm + range_error_all;
    222          pwm_L = pwm - range_error_all;
    223     }
    224     else if(turn_flag == 1)     //左转
    225     {
    226         pwm_R = pwm ;  //Direction PID control
    227         pwm_L = pwm + left_need * 68;
    228         range_error_all = 0;     //clean
    229     }
    230     else if(turn_flag == 2)
    231     {
    232         pwm_R = pwm + right_need * 68;  //Direction PID control
    233         pwm_L = pwm ;
    234         range_error_all = 0;     //clean
    235     }
    236        
    237        Lduration = 0;//clean
    238        Rduration = 0;
    239        lastTime = now;
    240 }
    241 
    242 void PWMD()
    243 {  
    244       if(pwm>0)
    245       {
    246           digitalWrite(IN1, HIGH);
    247           digitalWrite(IN2, LOW);
    248           digitalWrite(IN3, LOW);
    249           digitalWrite(IN4, HIGH);    
    250       }
    251       else if(pwm<0)
    252       {
    253           digitalWrite(IN1, LOW);
    254           digitalWrite(IN2, HIGH);
    255           digitalWrite(IN3, HIGH);
    256           digitalWrite(IN4, LOW);
    257       }
    258       int PWMr = abs(pwm);
    259       int PWMl = abs(pwm);
    260     
    261       analogWrite(ENB, max(PWMl,51)); //PWM调速a==0-255  51
    262       analogWrite(ENA, max(PWMr,54)); //PWM调速a==0-255  54
    263       
    264 }
    265 
    266 void LwheelSpeed()
    267 {
    268       if(digitalRead(encoder0pinB))
    269         Lduration++;
    270       else Lduration--;
    271 }
    272 
    273 
    274 void RwheelSpeed()
    275 {
    276       if(digitalRead(encoder1pinB))
    277         Rduration--;
    278       else Rduration++;
    279 }
    280 
    281 void control()
    282 {
    283     if(Serial.available()){
    284       int val;
    285       val=Serial.read();
    286       switch(val){
    287         case 'w':
    288           if(move_flag<5)move_flag += 0.5;
    289           else  move_flag = 0;
    290           break;
    291         case 's':
    292           if(move_flag<5)move_flag -= 0.5;
    293           else  move_flag = 0;
    294          Serial.println("back");
    295           break;
    296         case 'a':
    297           turn_flag = 1;
    298           left_need = 0.6;
    299           Serial.println("zuo");
    300           break;
    301         case 'd':
    302           turn_flag = 2;
    303           right_need = 0.6;
    304           Serial.println("you");
    305           break;
    306         case 't':
    307           move_flag=0;
    308           turn_flag=0;
    309           right_need = left_need = 0;
    310           Serial.println("stop");
    311           break;
    312         default:
    313           break;
    314           }
    315       }
    316 }
    317 
    318 void loop()
    319 {
    320 
    321     control();
    322     while (i2cRead(0x3B, i2cData, 14));
    323     accX = ((i2cData[0] << 8) | i2cData[1]);
    324     //accY = ((i2cData[2] << 8) | i2cData[3]);
    325     accZ = ((i2cData[4] << 8) | i2cData[5]);
    326     //gyroX = (i2cData[8] << 8) | i2cData[9];
    327     gyroY = (i2cData[10] << 8) | i2cData[11];
    328     //gyroZ = (i2cData[12] << 8) | i2cData[13];
    329 
    330     double dt = (double)(micros() - timer) / 1000000; // Calculate delta time
    331     timer = micros();
    332  
    333     double roll  = atan2(accX, accZ) * RAD_TO_DEG - move_flag;
    334 
    335 
    336     double gyroXrate = gyroX / 131.0; // Convert to deg/s
    337     double gyroYrate = -gyroY / 131.0; // Convert to deg/s
    338 
    339     //gyroXangle += gyroXrate * dt; // Calculate gyro angle without any filter
    340     //gyroYangle += gyroYrate * dt;
    341 
    342     Kalman_Filter(roll,gyroYrate);   
    343     if(abs(angle)<35){
    344         //Serial.println(angle); 
    345        ////////////////////////////////////////////
    346        
    347         //////////////////////////////////////  
    348         pwm_calculate();
    349         PWMD();
    350     }
    351     else{
    352         analogWrite(ENB, 0); //PWM调速a==0-255
    353         analogWrite(ENA, 0); //PWM调速a==0-255
    354     }  
    355     delay(2);
    356 }

    代码有很多都不理解,资料是从论坛大神那里查到的。

    @青山不移,文笔不息。学习,坚持,梦想青春!
  • 相关阅读:
    实验四 交换机的Telnet远程登陆配置
    实验三 交换机的基本配置与管理
    实验二 认识Packet Tracer软件
    实验一 网络连接线的制作
    python入门(七)
    python入门(六)
    python入门(五)
    Android练习(二)
    Android练习(一)
    python入门(四)
  • 原文地址:https://www.cnblogs.com/pengwenzheng/p/7943931.html
Copyright © 2020-2023  润新知