成功站立的独轮车技巧揭秘

真是废了不少力

卡了这么久首要原因就是过于青睐串级PID,串级PID可以自主寻找机械中位的特性实在是太优雅了,但动量轮这种对即使性要求极高的系统似乎不能用串级PID实现。昨天沉下心把串级PID推掉换成并联,波形一下就朝着正常的方向改变,再稍加调参那么三四个小时动量轮就很稳了。

没想到的是减速电机居然比动量轮还难调,O车模整车太重1:19的减速比就不太够用。调来调去电机都只有猛抽一下和静止两种状态,最终虽然平衡了但总体的静态效果还是不太可观。好在比赛只要保持稳定前进就行,静态不稳应该问题不大。

CSDN不支持发视频,那就把平衡代码发出来给大家参考参考吧。

虽然代码可读性和移植性都比较不错,但不建议大家直接拿去用,硬件差异很大。

我的硬件完全是自己搭的,陀螺仪MPU6050,减速电机电压7.4V,减速电机驱动集成H桥A4950,编码器也是自制的霍尔编码器一圈大概340线。

由此也证明了,龙邱逐飞为了利润把智能车市场拉太高了,明明六块钱的软件I2C陀螺仪就够用了他要推六十块的SPI陀螺仪,两块钱的集成H桥也够用了他要推MOS管搭的板子,我自制编码器全程投入不超过五十块而且效果优雅,他总推荐大家用一百多一个的高精度编码器。骄奢淫逸,把智能车搞得愈发商业化。

//X编码器积分限幅
static int range_x_enco(int value)
{
  static const int range_max = 10000;
  //略
}
float x_target_ang = 0;   //x轴目标角度(机械中位)

//X轴角度环PD
static void x_balance_cont(void)
{
  const float bal_kp = 90,bal_kd = 1100;          //PD参数
  int pwm_balance;                //临时存放输出量
  static float last_value;        //上一次的偏差量
  float ang_value = x_target_ang - atti.pitch;    //获取偏差量
  pwm_balance = bal_kp*ang_value + bal_kd*(ang_value - last_value);   //角度环PD
  speed.x1 = pwm_balance;   //输出PWM X1
  speed.x2 = pwm_balance;   //输出PWM X2
  last_value = ang_value;   //保存本次偏差用于下次微分
}
//X轴速度环PI
static void x_speed_cont(void)
{
  const float sp_kp = 9, sp_ki = 3, sp_kd = 12;   //PID参数
  int pwm_speed;                      //临时存放输出量
  int enco_value = enco.x1+enco.x2;   //两个动量轮编码器相加
  static int last_value = 0;
  static long enco_d_value;           //编码器积分
  enco_d_value += enco_value;         //累加出积分
  enco_d_value = range_x_enco(enco_d_value);   //编码器积分限幅
  pwm_speed = -(sp_kp*enco_value + sp_ki*enco_d_value/100 + sp_kd*(enco_value-last_value));    //速度环PID
  speed.x1 += pwm_speed;        //并联直接累加
  speed.x2 += pwm_speed;
  last_value = enco_value;      //记录本次差值用于微分
}

//Y轴角度积分限幅
int range_y_ang(int value)
{
  static const int value_max = 80;
  //略
}
//Y轴编码器积分限幅
int range_y_sp(int value)
{
  static const int value_max = 200;
  //略
}

float y_target_ang = 2.4;   //初始机械中位
float y_target_sp = 0;      //目标速度,要实现前进后退就改这个
//Y轴平衡环
static void y_balance_cont(void)
{
  const float bal_kp = 50,bal_ki = 6, bal_kd = 320;   //平衡环PD参数
  int pwm_balance;                        //临时存储输出量
  static float last_value = 0;            //记录偏差量,微分用
  static int ang_d_value = 0;             //角度积分量
  float ang_value = (y_target_ang - atti.roll);   //获取偏差量
  ang_d_value += ang_value;                       //积分
  ang_d_value = range_y_ang(ang_d_value);         //积分限幅
  if(ang_value > 0)                               //根据输出正负添加电机死区补偿
  {
    pwm_balance = (bal_kp*ang_value) + bal_kd*(ang_value-last_value)+40;   //角度环PD
  }  
  else if(ang_value < 0)
  {
    pwm_balance = (bal_kp*ang_value) + bal_ki*ang_d_value/100 + bal_kd*(ang_value-last_value)-40;
  }
  speed.y = pwm_balance;
  last_value = ang_value;                         //记录偏差量下次微分用
}
//Y轴速度环
static void y_speed_cont(void)
{
  const float sp_kp = 0.015, sp_kd = 0.09;    //速度环PD参数
  static int last_value;                 //记录上次的偏差量
  int sp_value = y_target_sp - enco.y;      //速度环偏差量
  y_target_ang -= (sp_kp*sp_value + sp_kd*(sp_value-last_value));    //速度环PD
  last_value = sp_value;  //保存偏差量用于下次微分
}

//定时器中断任务调度
//10ms定时器中断内调用
void tim_it_loop(void) 
{
  const int MAX_ANG = 20;   //安全角度
  glo_time+=10;             //全局时间
  //10ms
  {
    if(atti.pitch<MAX_ANG&&atti.pitch>-MAX_ANG&&atti.roll<MAX_ANG&&atti.roll>-MAX_ANG&&atti.work==1)    
    {//安全角度  MAX_ANG
      x_balance_cont();   //X平衡环
      y_balance_cont();   //Y平衡环
      get_enco_x();       //获取X编码器
      x_speed_cont();     //X速度环
      set_speed(speed.x1, speed.x2, speed.y);   //赋值PWM
    }
    else
    {//超过安全角度停止工作
      set_speed(0, 0, 0);
    }
  }
  //50ms
  if(glo_time%50==0)
  {
    get_enco_y();     //获取Y编码器
    y_speed_cont();   //Y速度环
  }
}
物联沃分享整理
物联沃-IOTWORD物联网 » 成功站立的独轮车技巧揭秘

发表评论