【智能车学习】全向行进控制算法总结——驱动部分

前言

两年的竞赛生涯一晃而过,不知不觉我也升入了大三,这两天翻出了十六届比赛时写的代码,打算做一下归纳整理,也算是为实验室生活画个句号。

麦轮基本原理

麦克纳姆轮(Mecanum wheel),这种全方位移动方式是基于一个有许多位于机轮周边的轮轴的中心轮的原理上,这些成角度的周边轮轴把一部分的机轮转向力转化到一个机轮法向力上面。
基于麦克纳姆轮技术的全方位运动设备可以实现前行、横移、斜行、旋转及其组合等运动方式。

基本控制算法

速度解算

我们可以将麦轮车的部分分为前进方向(X或Y)和转向方向(Z),在摄像头判断出前方路况后得到X,Y,Z三轴的速度后,需要将车身的X,Y,Z三轴速度分配给四个轮子,以便于后续的控制,这个过程就是速度解算。
速度解算的核心是要得到逆运动学方程矩阵,以便于将导航速度向量映射到四轮角速度向量上。即模型输入为导航向量(车身X,Y,Z三轴速度),输出量为小车四个轮子的转动速度。
麦轮物理模型
(此处推导参考论文《Mecanum轮逆矩阵研究》)
如上图为麦轮运动方程建模,L为车长,W为车宽,车轮顺序为左上,右上,左下,右下。
逆运动学方程如下:
逆运动学方程
将上式列成表达式为:
速度解算(简化前)
由于在程序控制过程中可以根据PID控制系数来改变speed大小,所以我们可以简化上图公式中的系数,并自己合理调整,便于后续参数调节。改动后的程序如下。

/**
 * @file        速度解算,由逆矩阵公式解算
 * @note        参照论文《Mecanum轮逆矩阵研究》
 * @date        2021
 */
void Speed_Calculation(int16 X_speed, int16 Y_speed, int16 Z_speed)
{
    if(Z_speed>100) Z_speed = 100;
    else if(Z_speed < -100) Z_speed = -100;   // 对转向速度限幅

    SpeedSet1= X_speed - 0.3*Z_speed- Y_speed;
    SpeedSet2= X_speed + 0.3*Z_speed+ Y_speed;
    SpeedSet3= X_speed - 0.3*Z_speed+ Y_speed;
    SpeedSet4= X_speed + 0.3*Z_speed- Y_speed;
}

经典pid算法

//PID 参数初始化
void PID_Init(PID *SPID)
{
   SPID->SumError = 0;
   SPID->LastError = 0;   //Error[k-1]
   SPID->PrevError = 0;   //Error[k-2]
   SPID->Proportion = 0;  //比例常数 Proportional Const
   SPID->Integral = 0;    //积分常数 Integral Const
   SPID->Derivative = 0;  //微分常数 Derivative Const
}

int16 PID_Increase(PID *SPID,int16 NextPoint,int16 NowData)
{
    int16 iError,iIncpid;                                                          //当前误差、累计增量
  iError = NextPoint - NowData;                                                         //目标速度-当前速度
  iIncpid = (int16)( (iError-SPID->LastError)* (SPID->Proportion)
                   +  iError*(SPID->Integral)
                   + (iError-2*SPID->LastError+SPID->PrevError) * (SPID->Derivative));
  SPID->PrevError = SPID->LastError;                                                    //E(k-2)
  SPID->LastError = iError;                                                             //E(k-1)
  return(iIncpid);                                                                      //返回增量值
}


int16 PID_Realize(PID *SPID,float iError)
{
    int16 place;//dError;
    //int16 iError;
    //iError = NextPoint - NowData;               //偏差
    SPID->SumError += iError;                     //积分
    //dError = iError - SPID->LastError;          //微分
    //SPID->LastError = iError;
    place = (int16)(SPID->Proportion * iError     //比例项
    + SPID->Integral * SPID->SumError);             //积分项
    //+ SPID->Derivative * icm_gyro_y);             //微分项
    return place;
}

控制算法pro

运用陀螺仪的循迹算法

首先要使用陀螺仪控制,这里可能会涉及到陀螺仪初始化过不了的问题,如果用的是逐飞科技的陀螺仪,可以尝试一下以下方法。
陀螺仪初始化
这里使用到了两层pid控制,首先用第一层pid算出的turn_R,作为前方道路曲率的倒数,再根据车速及曲率导出应有角速度,与陀螺仪测出的实时角速度比较,代入第二层pid,实现最终控制。代码如下。

void Dir_Speed_Control(float Calculate_Center)
{
	turn_R = PID_Realize(&PID1,g_fMid_AD);
	Car_speed = (encoder_data[0]+encoder_data[1]+encoder_data[2]+encoder_data[3])/4;
	w = turn_R * Car_speed;
	SpeedSet_Z = PID_Realize(&PID2,icm_gyro_z-w);
	if(SpeedSet_Z<-100)
		SpeedSet_Z = -100;
		
}

变速控制

为了最大程度发挥小车性能,更快完成赛道,可引入变速控制,具体方案如下。

SpeedSet_X = K_speed*(84/10 - Error/10) + PWM_default;

PWM_default为设定最低速度,error为摄像头算出的偏差(我的摄像头图像宽度为170,所以最大偏差为84),并通过系数K_speed调节速度上限。另外如果想要控制响应更迅速,也可以考虑使用平方项。

驱动部分的总结到此就告一段落了,相对而言这部分的变换还是相对较少的,主要还是要注意参数的调试,也可以尝试着把电机数据发送到上位机辅助调试。

附录:Motor.c代码

#include "headfile.h"

/*********速度测量相关初始化**********/
int32 speed1_power;
int32 speed2_power;
int32 speed3_power;
int32 speed4_power;

int16 encoder_data[4];

int16 Pulse_Filtered_data1=0;
int16 Pulse_Filtered_data2=0;
int16 Pulse_Filtered_data3=0;
int16 Pulse_Filtered_data4=0;

/*********速度解算相关初始化**********/
float L=0.186,W=0.183;    //车尺寸,单位m
//int R=28;

/*********速度控制相关初始化**********/
int16 SpeedSet1;
int16 SpeedSet2;
int16 SpeedSet3;
int16 SpeedSet4;

int16 SpeedSet_X;
int16 SpeedSet_Y;
int16 SpeedSet_Z;

PID Motor1={0,0,0,  16,  3,   3};
PID Motor2={0,0,0,  16,  3,   3};
PID Motor3={0,0,0,  16,  3,   3};
PID Motor4={0,0,0,  16,  3,   3};

PID Dir_Speedr1 ={0,0,0,  11,       0,  0.1};           //右转向
PID Dir_Speedr2 ={0,0,0,  0.002,    0,  0.0002};

PID Dir_Speedl1 ={0,0,0,  10,       0,  0.1};           //左转向
PID Dir_Speedl2 ={0,0,0,  0.002,    0,  0.0002};

PID   Roundr1   ={0,0,0,  10,       0,  0};           //右环
PID   Roundr2   ={0,0,0,  0.0029,   0,  0.0002 };

PID   Roundl1   ={0,0,0,  9,        0,  0};           //左环
PID   Roundl2   ={0,0,0,  0.0028,   0,  0.0001};

PID   chalul     ={0,0,0,  0.37,     0,  0.06};
PID   chalur     ={0,0,0,  0.37,     0,  0.06};


/**
 * @file        相关外设初始化
 * @note        电机&&编码器
 * @date        2021
 */
void Motor_Init(void)
{
    gpio_init(MOTOR1_A, GPO, 0, GPIO_PIN_CONFIG);
    pwm_init(MOTOR1_B,MOTOR_HZ,0);
    gpio_init(MOTOR2_A, GPO, 0, GPIO_PIN_CONFIG);
    pwm_init(MOTOR2_B,MOTOR_HZ,0);
    gpio_init(MOTOR3_A, GPO, 0, GPIO_PIN_CONFIG);
    pwm_init(MOTOR3_B,MOTOR_HZ,0);
    gpio_init(MOTOR4_A, GPO, 0, GPIO_PIN_CONFIG);
    pwm_init(MOTOR4_B,MOTOR_HZ,0);
}

void Encoder_Init(void)
{
    encoder_init_spi(ABS_ENCODER_SPI_PC1_PIN);
    encoder_init_spi(ABS_ENCODER_SPI_PC2_PIN);
    encoder_init_spi(ABS_ENCODER_SPI_PC3_PIN);
    encoder_init_spi(ABS_ENCODER_SPI_PC4_PIN);
}


/**
 * @file        相关测试
 * @note        电机&&编码器
 * @date        2021
 */
void Test_Motor()
{
    ips114_showstr(0, 0, "20191114  mTX-Motortest");
    Key_Read();
    ips114_showstr(0,1,"Motor-Press B2:");
    ips114_showint32(12*12,1,test1,1);
    ips114_showstr(0,2,"Speed-Press B4:");
    ips114_showint32(12*12,2,test2,1);
    ips114_showstr(0,3,"Speed1:");     ips114_showint32(12*8,3,Pulse_Filtered_data1,4);
    ips114_showstr(0,4,"Speed2:");     ips114_showint32(12*8,4,Pulse_Filtered_data2,4);
    ips114_showstr(0,5,"Speed3:");     ips114_showint32(12*8,5,Pulse_Filtered_data3,4);
    ips114_showstr(0,6,"Speed4:");     ips114_showint32(12*8,6,Pulse_Filtered_data4,4);
    if(key1_flag==1&&key2_flag==0&&key3_flag==0)
    {
        test1++;
        key1_flag=0;
        ips114_showstr(0,1,"Motor-Press B2:");
        ips114_showint32(12*12,1,test1,1);
        switch(test1)
        {
          case 1:
              Set_Motor(300,0,0,0);
              systick_delay_ms(1000);
              Motor_Init();
              Set_Motor(-300,0,0,0);
              systick_delay_ms(1000);
            break;

          case 2:
              Set_Motor(0,300,0,0);
              systick_delay_ms(1000);
              Motor_Init();
              Set_Motor(0,-300,0,0);
              systick_delay_ms(1000);
            break;

          case 3:
              Set_Motor(0,0,300,0);
              systick_delay_ms(1000);
              Motor_Init();
              Set_Motor(0,0,-300,0);
              systick_delay_ms(1000);
            break;

          case 4:
              Set_Motor(0,0,0,300);
              systick_delay_ms(1000);
              Motor_Init();
              Set_Motor(0,0,0,-300);
              systick_delay_ms(1000);
              test1=0;
            break;
          default:
            break;
        }
    }

    if(key1_flag==0&&key2_flag==1&&key3_flag==0)
    {
        test2++;
        key2_flag=0;
        ips114_showstr(0,2,"Speed-Press B4:");
        ips114_showint32(12*12,2,test2,1);
        switch(test2)
        {
          case 1:
              Set_Motor(100,100,100,100);
              systick_delay_ms(1000);
            break;
          case 2:
              Set_Motor(200,200,200,200);
              systick_delay_ms(1000);
            break;
          case 3:
              Set_Motor(300,300,300,300);
              systick_delay_ms(1000);
              test2=0;
            break;
          default:
            break;
        }
    }
}

void Test_Encoder(void)
{
    ips114_showstr(0, 0, "20191114  mTX-Encodertest");
    //Speed_Measure();
    //PulseFilter(encoder_data[0],encoder_data[1],encoder_data[2],encoder_data[3]);
    Set_Motor(200,200,200,200);
    ips114_showstr(0,1,"Speed1:");     ips114_showint32(12*8,1,Pulse_Filtered_data1,4);
    ips114_showstr(0,2,"Speed2:");     ips114_showint32(12*8,2,Pulse_Filtered_data2,4);
    ips114_showstr(0,3,"Speed3:");     ips114_showint32(12*8,3,Pulse_Filtered_data3,4);
    ips114_showstr(0,4,"Speed4:");     ips114_showint32(12*8,4,Pulse_Filtered_data4,4);
}


/**
 * @file        电机总控制
 * @note
 * @date        2021
 */
int16 PWM_default;
float Error;
float K_speed=0.15f;
int Fork_road_flag;
int turn_flag;
int time;
int chuku=1;
int time_chuku;
void intrgral(int16 gr);
int t_cha;
int chalu_finish;
void MotorControl(void)
{
    if(chuku)
    {
        SpeedSet_X=50;
        SpeedSet_Y=0;
        SpeedSet_Z=100;

        intrgral(icm_gyro_z);

        if(fabs(Angle)>90)
        {
            Angle = 0;
            chuku = 0;
        }
    }
    if(chuku == 0)
    {
        Error = fabs(g_fMid_AD);

        if(!chalu_flag&&!Startline&&!turn_flag&&!Fork_road_flag)
        {
            SpeedSet_X = K_speed*(84/10 - Error/10)*(84/10 - Error/10) + PWM_default;
            Dir_Speed_Control(g_fMid_AD);
        }

        if((chalu_flag==3&&turn_flag==0)||(chalu_flag==4&&turn_flag==0)
                ||(chalu_flag==7&&turn_flag==0)||(chalu_flag==8&&turn_flag==0))//进三岔前停车
        {
            time++;
            SpeedSet_X=0;
            SpeedSet_Y=0;
            SpeedSet_Z=0;
            if(time > 100)
            {
                Angle = 0;
                turn_flag = 1;
                time = 0;
            }
        }
        //Stopcar();
        if(chalu_flag==3&&turn_flag==1)//左进三岔
        {
            SpeedSet_X=0;
            SpeedSet_Y=0;
            SpeedSet_Z=-100;
            if(fabs(Angle)>40)
            {
                Fork_road_flag=1;
                turn_flag = 0;
                Angle=0;
                g_fMid_AD=0;
            }
        }
        if(chalu_flag==4&&turn_flag==1)//右进三岔
        {
            SpeedSet_X=0;
            SpeedSet_Y=0;
            SpeedSet_Z=-100;
            if(fabs(Angle)>145)
            {
                Fork_road_flag=1;
                turn_flag = 0;
                Angle=0;
                g_fMid_AD=0;
            }
        }
        if(Fork_road_flag==1)//三岔中
        {
            SpeedSet_X=0;
            SpeedSet_Y=65;
            if(SpeedSet_Z<0)   //右转
                SpeedSet_Z = -PID_Realize(&chalur,g_fMid_AD*fabs(g_fMid_AD));
            else
                SpeedSet_Z = -PID_Realize(&chalul,g_fMid_AD*fabs(g_fMid_AD));
        }
        if(chalu_flag==7&&turn_flag==1)//左出三岔
        {
            SpeedSet_X=0;
            SpeedSet_Y=0;
            SpeedSet_Z=100;
            if(fabs(Angle)>115)
            {
                chalu_finish = 1;
                Fork_road_flag=0;
                turn_flag = 0;
                chalu_flag = 9;
                g_fMid_AD = 0;
                Angle=0;
            }
        }
        if(chalu_flag==8&&turn_flag==1)//右出三岔
        {
            SpeedSet_X=0;
            SpeedSet_Y=0;
            SpeedSet_Z=-100;
            if(fabs(Angle)>155)
            {
                chalu_finish = 1;
                Fork_road_flag=0;
                turn_flag = 0;
                chalu_flag = 10;
                g_fMid_AD=0;
            }
        }
        if(chalu_flag == 9 || chalu_flag ==10)
        {
            if(t_cha == 0)
                t_cha = time_count;
            if (time_count - t_cha <= 100)
            {
                SpeedSet_X = 30;
                SpeedSet_Y = 0;
                Dir_Speed_Control(g_fMid_AD);
            }
            else if (time_count - t_cha > 100) {
                chalu_flag = 0;
            }
        }

        if(Startline == 1&&round_count == 0)
            finall_front();
        if(Startline == 1&&round_count == 1)
            garage_in();
    }

    Speed_Calculation(SpeedSet_X, SpeedSet_Y, SpeedSet_Z);
    Speed_Measure();
    //PulseFilter(encoder_data[0],encoder_data[1],encoder_data[2],encoder_data[3]);

    speed1_power += PID_Increase(&Motor1,SpeedSet1,encoder_data[0]);
    speed2_power += PID_Increase(&Motor2,SpeedSet2,encoder_data[1]);
    speed3_power += PID_Increase(&Motor3,SpeedSet3,encoder_data[2]);
    speed4_power += PID_Increase(&Motor4,SpeedSet4,encoder_data[3]);


    if(speed1_power > 500)  speed1_power = 500;
    else if(speed1_power < -500) speed1_power = -500;

    if(speed2_power > 500)  speed2_power = 500;
    else if(speed2_power < -500) speed2_power = -500;


    if(speed3_power > 500)  speed3_power = 500;
    else if(speed3_power < -500) speed3_power = -500;


    if(speed4_power > 500)  speed4_power = 500;
    else if(speed4_power < -500) speed4_power = -500;


    Set_Motor(speed1_power, speed2_power, speed3_power, speed4_power);

}


/**
 * @file        速度测量
 * @note        角速度传感器(绝对式编码器) 范围0-1023
 * @date        2021
 */
void Speed_Measure(void)
{
    encoder_data[0] =  -encoder1_speed_spi(ABS_ENCODER_SPI_PC1_PIN);
    encoder_data[1] =   encoder2_speed_spi(ABS_ENCODER_SPI_PC2_PIN);
    encoder_data[2] =  -encoder3_speed_spi(ABS_ENCODER_SPI_PC3_PIN);
    encoder_data[3] =   encoder4_speed_spi(ABS_ENCODER_SPI_PC4_PIN);

    //data_conversion(encoder_data[0], encoder_data[1], encoder_data[2], encoder_data[3], virtual_scope_data);
}


/**
 * @file        编码器滤波
 * @note
 * @date        2021
 */
void PulseFilter(int data1,int data2,int data3,int data4)
{
  static int Pre_Pulse_data1[4],Pre_Pulse_data2[4],
             Pre_Pulse_data3[4],Pre_Pulse_data4[4];

  for(int i=2;i>=0;i--)
  {
    Pre_Pulse_data1[i+1] = Pre_Pulse_data1[i];
    Pre_Pulse_data2[i+1] = Pre_Pulse_data2[i];
    Pre_Pulse_data3[i+1] = Pre_Pulse_data3[i];
    Pre_Pulse_data4[i+1] = Pre_Pulse_data4[i];
  }

  Pre_Pulse_data1[0]=data1;
  Pre_Pulse_data2[0]=data2;
  Pre_Pulse_data3[0]=data3;
  Pre_Pulse_data4[0]=data4;

  Pulse_Filtered_data1 = (Pre_Pulse_data1[3]+Pre_Pulse_data1[2]+Pre_Pulse_data1[1]+Pre_Pulse_data1[0])/4;
  Pulse_Filtered_data2 = (Pre_Pulse_data2[3]+Pre_Pulse_data2[2]+Pre_Pulse_data2[1]+Pre_Pulse_data2[0])/4;
  Pulse_Filtered_data3 = (Pre_Pulse_data3[3]+Pre_Pulse_data3[2]+Pre_Pulse_data3[1]+Pre_Pulse_data3[0])/4;
  Pulse_Filtered_data4 = (Pre_Pulse_data4[3]+Pre_Pulse_data4[2]+Pre_Pulse_data4[1]+Pre_Pulse_data4[0])/4;

}


/**
 * @file        转向速度求解
 * @note
 * @date        2021
 */
float turn_R,Car_speed,w;
void Dir_Speed_Control(float Calculate_Center)
{
    if(Island_flag==3||Island_flag==4)
    {
        if(Island_flag==3)
        {
            turn_R = PID_Realize(&Roundl1,g_fMid_AD);
            Car_speed = (encoder_data[0]+encoder_data[1]+encoder_data[2]+encoder_data[3])/4;
            w = turn_R * Car_speed;
            SpeedSet_Z = PID_Realize(&Roundl2,icm_gyro_z-w);
            if(SpeedSet_Z<-100)
                SpeedSet_Z = -100;
        }
        if(Island_flag==4)
        {
            turn_R = PID_Realize(&Roundr1,g_fMid_AD);
            Car_speed = (encoder_data[0]+encoder_data[1]+encoder_data[2]+encoder_data[3])/4;
            w = turn_R * Car_speed;
            SpeedSet_Z = PID_Realize(&Roundr2,icm_gyro_z-w);
            if(SpeedSet_Z<-100)
                SpeedSet_Z = -100;
        }
    }
    else
    {
        if(SpeedSet_Z<0)   //右转
        {
            turn_R = PID_Realize(&Dir_Speedr1,g_fMid_AD);
            Car_speed = (encoder_data[0]+encoder_data[1]+encoder_data[2]+encoder_data[3])/4;
            w = turn_R * Car_speed;
            SpeedSet_Z = PID_Realize(&Dir_Speedr2,icm_gyro_z-w);
            if(SpeedSet_Z<-100)
                SpeedSet_Z = -100;
            //SpeedSet_Z = PID_Realize(&Dir_Speedr1,g_fMid_AD);
        }

        else {
            turn_R = PID_Realize(&Dir_Speedl1,g_fMid_AD);
            Car_speed = (encoder_data[0]+encoder_data[1]+encoder_data[2]+encoder_data[3])/4;
            w = turn_R * Car_speed;
            SpeedSet_Z = PID_Realize(&Dir_Speedl2,icm_gyro_z-w);
            if(SpeedSet_Z>100)
                SpeedSet_Z = 100;
            //SpeedSet_Z = PID_Realize(&Dir_Speedl1,g_fMid_AD);
        }
    }
}


/**
 * @file        速度解算,由逆矩阵公式解算
 * @note        参照论文《Mecanum轮逆矩阵研究》
 * @date        2021
 */
void Speed_Calculation(int16 X_speed, int16 Y_speed, int16 Z_speed)
{
    if(Z_speed>100) Z_speed = 100;
    else if(Z_speed < -100) Z_speed = -100;

    SpeedSet1= X_speed - 0.3*Z_speed- Y_speed;
    SpeedSet2= X_speed + 0.3*Z_speed+ Y_speed;
    SpeedSet3= X_speed - 0.3*Z_speed+ Y_speed;
    SpeedSet4= X_speed + 0.3*Z_speed- Y_speed;
}



/**
 * @file        电机驱动
 * @note
 * @date        2021
 */
void Set_Motor(int32 speed1_power, int32 speed2_power, int32 speed3_power, int32 speed4_power)
{


    if(0<=speed1_power) //电机1   正转
    {
        gpio_set(MOTOR1_A, 1);
        pwm_duty(MOTOR1_B, speed1_power);
    }
    else                //电机1   反转
    {
        gpio_set(MOTOR1_A, 0);
        pwm_duty(MOTOR1_B, -speed1_power);
    }

    if(0<=speed2_power) //电机2   正转
    {
        gpio_set(MOTOR2_A, 1);
        pwm_duty(MOTOR2_B, speed2_power);
    }
    else                //电机2   反转
    {
        gpio_set(MOTOR2_A, 0);
        pwm_duty(MOTOR2_B, -speed2_power);
    }

    if(0<=speed3_power) //电机3   正转
    {
        gpio_set(MOTOR3_A, 1);
        pwm_duty(MOTOR3_B, speed3_power);
    }
    else                //电机3   反转
    {
        gpio_set(MOTOR3_A, 0);
        pwm_duty(MOTOR3_B, -speed3_power);
    }

    if(0<=speed4_power) //电机4   正转
    {
        gpio_set(MOTOR4_A, 1);
        pwm_duty(MOTOR4_B, speed4_power);
    }
    else                //电机4   反转
    {
        gpio_set(MOTOR4_A, 0);
        pwm_duty(MOTOR4_B, -speed4_power);
    }

}

如有疑问或错误,欢迎和我私信交流指正。
W.By Xyq

  • 23
    点赞
  • 155
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Carry_qing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值