基于esp32的平衡车代码(三环控制)

#include <Arduino.h>
#include "BluetoothSerial.h"
#include <MPU6050_tockn.h>
#include <Wire.h>
#include<Ticker.h>
Ticker timer_read_encoder; 
BluetoothSerial SerialBT;                  //实例化esp32的蓝牙串口对象
MPU6050 mpu6050(Wire);                     //实例化mpu6050对象
  /*【编码器宏定义】*/
#define ENCODER_L       16    
#define DIRECTION_L     17
#define ENCODER_R       18
#define DIRECTION_R     23
#define interrupt_time  20   //中断时间
  /*【引脚宏定义】*/
#define IN1       26         //0号通道
#define IN2       27         //1号通道
#define IN3       32         //2号通道
#define IN4       33         //3号通道
#define sda_pin   13         //SDA
#define scl_pin   15         //SCL
#define leftOffset     50
#define rightOffset    40
  /*【直立环变量】*/
float Balance_Angle_raw =-1.8;         //机械平衡角度   
float kp=5.0;
float kd=0.74;  
float bias; 
float AngleX;                          //X轴角度  
float GyroX;                           //X轴角速度    
  /*【转向环变量】*/
float turn_kp=-0.4;                    
float turn_spd;                        //转向速度目标值 
float GyroZ;                           //Z轴的角速度
  /*【速度环变量】*/
float V_kp=1.2;
float V_ki=0.009; 
float Go_V=0;                                    //小车速度
float error;                                     //速度偏差
float integrate;                                 //偏差的积分
int   error_last;                                //小车上一次速度的偏差
float error_filter;                              //过滤过的速度偏差
int32_t Velocity_L, Velocity_R;                  //左右轮编码器数据
int32_t Velocity_Left, Velocity_Right;           //左右轮速度
  /*【PWM变量】*/
int L_PWM;
int R_PWM;
int vertical_PWM;                  //直立PWM   
int v_PWM;                         //速度PWM
int turn_PWM;                      //转向PWM
int PWM;                           //总PWM 
void READ_ENCODER_L(void)                  {//读取左边编码器的数据
  if (digitalRead(ENCODER_L) == LOW) {                       //如果是下降沿触发的中断
    if (digitalRead(DIRECTION_L) == LOW)      Velocity_L--;  //根据另外一相电平判定方向
    else      Velocity_L++;
    }
  else {     //如果是上升沿触发的中断
    if (digitalRead(DIRECTION_L) == LOW)      Velocity_L++; //根据另外一相电平判定方向
    else     Velocity_L--; 
  }
}
void READ_ENCODER_R(void)                  {//读取右边编码器的数值
  if (digitalRead(ENCODER_R) == LOW) { //如果是下降沿触发的中断
    if (digitalRead(DIRECTION_R) == LOW)      Velocity_R--;//根据另外一相电平判定方向
    else      Velocity_R++;
  }
  else {   //如果是上升沿触发的中断
    if (digitalRead(DIRECTION_R) == LOW)      Velocity_R++; //根据另外一相电平判定方向
    else     Velocity_R--;
  }
}
void read(void)                            {//计算小车的速度
    Velocity_Left = Velocity_L;    Velocity_L = 0;  //读取左轮编码器数据,并清零,这就是通过M法测速(单位时间内的脉冲数)得到速度。
    Velocity_Right = Velocity_R;    Velocity_R = 0; //读取右轮编码器数据,并清零
}
void motor(int left_EN, int right_EN)      {//马达驱动函数

  left_EN = constrain(left_EN, -255, 255);
  right_EN = constrain(right_EN, -255, 255);  //限定PWM区间在-255~255
  if (left_EN >= 0)
  {
    ledcWrite(0, left_EN);
    ledcWrite(1, 0);
  }
  if (left_EN < 0)
  {
    ledcWrite(0, 0);
    ledcWrite(1, 0 - left_EN);
  }
  if (right_EN >= 0)
  {
    ledcWrite(2, right_EN);
    ledcWrite(3, 0);
  }
  if (right_EN < 0)
  {
    ledcWrite(2, 0);
    ledcWrite(3, 0 - right_EN);
  }
}
void serial_debug()                        {//蓝牙串口调试和控制函数,根据手机端发送来的串口数据调试或控制
  if (SerialBT.available()> 0){
    char DATA = SerialBT.read();
    delay(5);
    switch (DATA)
    {
      /*【调节平衡点】*/
      case 'u':Balance_Angle_raw += 0.1;  break;
      case 'd':Balance_Angle_raw -= 0.1;  break; 
      /*【调节直立环】*/
      case '0':kp -= 0.2;                 break;
      case '1':kp += 0.2;                 break;
      case '2':kd -= 0.01;                break;
      case '3':kd += 0.01;                break;
      /*【调节速度环】*/
      case '4':V_kp-= 0.02;                break;
      case '5':V_kp += 0.02;               break;   
      case '6':V_ki -= 0.0001;             break;
      case '7':V_ki += 0.0001;             break;  
      /*【调节转向环】*/
      case '8':turn_kp -= 0.1;            break;
      case '9':turn_kp += 0.1;            break;
      /*【小车指令控制】*/
      case 's':Go_V=0;integrate=0;            break;     //停止
      case 'a':Go_V=10;integrate=0;           break;     //前进
      case 'b':Go_V=-10;integrate=0;          break;     //后退
      case 'z':turn_spd = 0;integrate=0;      break;     //不转向
      case 'l':turn_spd = 300;                break;     //左转
      case 'r':turn_spd = -300;               break;     //右转
    }
    /*【蓝牙打印输出】*/
    SerialBT.print("Balance_Angle_raw: ");SerialBT.println(Balance_Angle_raw);
    SerialBT.println("直立环控制参数:");
    SerialBT.print("kp:");SerialBT.print(kp);SerialBT.print(" kd:");SerialBT.println(kd);
    SerialBT.println("速度环控制参数:");
    SerialBT.print("V_kp:");SerialBT.print(100*V_kp);SerialBT.print("V_ki:");SerialBT.println(100*V_ki);
    SerialBT.println("转向环控制参数:");
    SerialBT.print("  turn_kp:");SerialBT.println(turn_kp);   
    SerialBT.print(" PWM:");SerialBT.print(rightOffset);SerialBT.println("------"); 
  }
}
void vertical_pwm_calculation()            {//直立PMW计算,采用PD控制
  AngleX = mpu6050.getAngleX();            //获取X轴的角度
  GyroX = mpu6050.getGyroX();              //获取X轴的角速度              
  bias = AngleX - Balance_Angle_raw;       // 计算角度偏差
  vertical_PWM = kp*bias + kd * GyroX;     //计算实时的PWM脉冲。
}
void turn_pwm_calculation()                {//转向PMW计算
  GyroZ = mpu6050.getGyroZ();              //获取陀螺仪Z轴角速度
  turn_PWM = turn_kp * (turn_spd - GyroZ);
}
void v_pwm_calculation()                   {//速度PWM计算,采用PI控制
  error=(Velocity_Left+Velocity_Right)/2-Go_V;   //计算偏差
  error_filter=0.3*error+0.7*error_last;         //一阶滤波
  error_last=error;                              //将测得的偏差变成上一次的偏差,供下次利用
  integrate=integrate+error_filter;              //将过滤过的偏差积分
  integrate = constrain(integrate,-1500,1500);   //限定误差积分的最大和最小值
  v_PWM=V_kp*error+V_ki*integrate;               //计算速度的PWM
}
void motor_control()                       {//PWM补偿,转向PWM,并控制马达输出,让电机转动
  /*【死区补偿】*/
  if (PWM > 0){L_PWM = PWM + leftOffset;R_PWM = PWM + rightOffset;}
  if (PWM < 0){L_PWM = PWM - leftOffset;R_PWM = PWM - rightOffset;}          
  if (PWM == 0){L_PWM = 0; R_PWM = 0;} 
  /*【转向控制】*/
  L_PWM -= turn_PWM;
  R_PWM += turn_PWM; 
  /*【输出限幅】*/
  L_PWM = constrain(L_PWM, -255, 255); 
  R_PWM = constrain(R_PWM, -255, 255);
  /*【判断状态】*/
  if (AngleX > 45 || AngleX < -45){motor(0, 0);}
  /*【马达输出】*/
  motor(L_PWM, R_PWM);
}
void channel()                             {//PWM通道函数
  ledcSetup(0,1000,8);ledcAttachPin(IN1,0);//设置通道0
  ledcSetup(1,1000,8);ledcAttachPin(IN2,1);//设置通道1
  ledcSetup(2,1000,8);ledcAttachPin(IN3,2);//设置通道2
  ledcSetup(3,1000,8);ledcAttachPin(IN4,3);//设置通道3
}
void Encoder_Init()                        {//编码器初始化
  pinMode(ENCODER_L, INPUT);       //编码器引脚 输入模式
  pinMode(ENCODER_R, INPUT);       //编码器引脚 输入模式
  pinMode(DIRECTION_L, INPUT);     //编码器引脚 输入模式
  pinMode(DIRECTION_R, INPUT);     //编码器引脚 输入模式 
                                                         //编码器接口1 开启外部跳边沿中断 
  attachInterrupt(ENCODER_L, READ_ENCODER_L, CHANGE);    //中断函数READ_ENCODER_L
                                                         //编码器接口2 开启外部跳边沿中断 
  attachInterrupt(ENCODER_R, READ_ENCODER_R, CHANGE);    //中断函数READ_ENCODER_R
  interrupts();                                          //打开外部中断      
  timer_read_encoder.attach_ms(interrupt_time, read);    //设置20毫秒读取一次read函数
}                                  
void setup()                               {//初始化函数
  channel();                           //选择通道
  Serial.begin(9600);                  //串口初始化
  SerialBT.begin("ESP32_car");         //蓝牙初始化
  Wire.begin(sda_pin, scl_pin);        //I2C初始化
  Encoder_Init();                      //编码器初始化
  mpu6050.begin();                             //mpu6050初始化
  mpu6050.calcGyroOffsets(true);               //mpu6050数值补偿
  motor(0, 0);                                 //马达驱动初始化
  delay(10);                                   //延时
}
void loop()                                {//主函数
  serial_debug();                //串口调试+控制
  mpu6050.update();              //陀螺仪刷新
  v_pwm_calculation();           //速度环PWM计算
  vertical_pwm_calculation();    //直立环PWM计算
  turn_pwm_calculation();        //转向PWM计算
  PWM = vertical_PWM+kp*v_PWM;   //直立环和速度环的串级PID控制,将速度环的输出量输入到角度环,从而实现对速度的控制
  motor_control();               //最终由将得到的PWM给到马达,控制马达的输出
  Serial.print("integrate");Serial.print(integrate);
  Serial.print("PWM");Serial.print(PWM);
  Serial.print("v_PWM");Serial.println(kp*v_PWM);
}

 PID部分参数需自行修改

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
串级PID控制算法是一种常用的控制策略,适用于需要精确控制的系统,例如平衡。串级PID算法的结构包括两个PID控制器,一个用于控制系统的主环路,另一个用于控制系统的从环路。 在平衡的串级PID算法中,主要有两个控制回路:速度环和姿态环。速度环用于控制的移动速度,姿态环用于控制平衡姿态。下面是串级PID算法的一般步骤: 1. 设定目标速度和目标姿态。 2. 通过速度传感器获取当前速度信息,计算速度误差。 3. 使用PID控制器计算速度环的输出控制信号。PID控制器根据速度误差、积分误差和微分误差来计算输出。 4. 将速度环的输出控制信号作为姿态环的参考输入。 5. 通过陀螺仪等传感器获取当前姿态信息,计算姿态误差。 6. 使用PID控制器计算姿态环的输出控制信号。PID控制器根据姿态误差、积分误差和微分误差来计算输出。 7. 将姿态环的输出控制信号作为电机驱动的控制信号,用于调节电机的输出功率和方向,以实现平衡控制的姿态。 8. 循环执行上述步骤,不断更新控制信号,使小保持平衡并达到目标速度和姿态。 需要注意的是,串级PID算法的参数调节是一个重要的环节,需要根据实际情况进行优化和调整,以获得良好的控制效果。此外,还可以结合其他控制策略和方法,如模糊控制、神经网络等,来进一步优化控制性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值