这是在电赛实验室里接触的第一个控制组小车项目,是22年的电赛C题
以下是赛题的任务部分,要求采用TI的MCU,双车跟随,且具有循迹功能,速度在0.3 ~ 1m/s内可调,能在指定路径上完成行驶操作
基于MSP432P401R的小车跟随系统
搭载TI公司MSP432P401R控制内核的小车跟随系统设计精良。系统利用5路数字灰度传感器实现小车循迹,高精度编码器的直流减速电机配合PID算法,实现精准控速。超声波模块HC-SR04用于距离控制,蓝牙模块HC-05实现通信。按下按键后,领头小车通过蓝牙指令驱动跟随小车,二者匀速行驶。灰度传感器识别黑线,超声波模块防碰撞并实时反馈,串级PID调控跟随小车速度,保持精准距离。领头小车检测到停止标志后停止电机并发送指令给跟随小车。在岔道口,灰度传感器进行路径选择,让两车竞速。提速后,发现“等停指示”标识时停下。
理论分析和计算
小车运控循迹算法
PID控制器,即比例-积分-微分控制器,通过比例-积分-微分对系统的综合控制作用,从而达到调节系统的目的。对于该小车,我们需要对PID的参数进行计算,以速度式的PID为例,MSP432最小系统板通过霍尔编码器,利用检测脉冲的方式来计算转速及位置,输出有关旋转轴运动的信息进一步转换为速度、距离、每分钟转速等信息。MCU对数据进行PID算法处理可以调节小车当前的速度。PID是比例、积分、微分的缩写。比例调节是按比例反应系统的偏差,系统一旦出现了偏差,比例调节立即产生调节作用用以减少偏差。比例作用大,可以加快调节,减少误差,但是过大的比例,使系统的稳定性下降,甚至造成系统的不稳定。积分调节是使系统消除稳态误差,提高无差度。因为有误差,积分调节就进行,直至无差,积分调节停止,积分调节输出一常值。积分作用的强弱取决与积分时间常数Ti,Ti越小,积分作用就越强。反之Ti大则积分作用弱,加入积分调节可使系统稳定性下降,动态响应变慢。积分作用常与另两种调节规律结合,组成PI调节器或PID调节器。几者结合从而保持小车运行的稳定。
小车通信模式
采用两个HC-05蓝牙模块,一个主机,一个从机,用单片机的串口UART和蓝牙通信
蓝牙模块的RXD接单片机的TXD,TXD接RXD,VCC接5V,GND接GND
小车距离控制分析
采用HC-SR04超声测距模块,超声波测距模块采用IO口TRIG触发测距,模块自动发送方波,自动检测信号返回。有信号返回,输出高电平,开启定时器计数
程序设计
模块化代码
#include <stdint.h>
#include <ti/devices/msp432p4xx/driverlib/driverlib.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <math.h>
#include "inv_mpu_dmp_motion_driver.h"
#include "inv_mpu.h"
#include "dmpmap.h"
#include "dmpKey.h"
#include "led.h"
#include "delay.h"
#include "sysinit.h"
#include "key.h"
#include "buzzer.h"
#include "usart.h"
#include "hongwai.h"
#include "blooth_esp.h"
#include "timA.h"
#include "timA0.h"
#include "CCD.h"
#include "interrupt.h"
#include "adc.h"
#include "timer.h"
#include "oled.h"
#include "mpu6050.h"
#include "IOI2C.h"
#include "DataScope_DP.h"
#include "show.h"
#include "HCSR04.h"
小车实物
核心代码
void T32_INT1_IRQHandler(void)
{
MAP_Timer32_clearInterruptFlag(TIMER32_0_BASE); // 清楚中断标志
//等停延时
if(delay_Flag==1)
{
Set_Pwm(0,0);
Buzzer_on();
delay_ms(5000);
delay_Flag=0;
}
else
{
if(Flag_start==1)
{
//不停车
if(Flag_Stop==0)
{
//红外循线
hw_test();
Encoder_Update();
//十次累计精确结果
Enl += Encoder_Left; // 为精确,求10次平均
Enr += Encoder_Right; // 为精确,求10次平均
Encount++; // 计数变量
if(Encount == 10)
{
Encoder_Left = Enl; // 放大十倍后结果相加
Encoder_Right = Enr; // 放大十倍后结果相加
Enall=(Enl+Enr),Enl=0,Enr=0,Encount=0; // 复位,左右轮编码器相加
}
else
{
Encoder_Left=10 * Encoder_Left; // 放大十倍232
Encoder_Right=10 * Encoder_Right; // 放大十倍
}
Get_Velocity_Form_Encoder(Encoder_Left,Encoder_Right);
Velocity_Pwm=Velocity(Encoder_Left,Encoder_Right);
if(mission==1|mission==2)
{
//防止第一次误转,加速冲过去,走外圈
if(Flag_cross == 1)
{
Motor_A=Velocity_Pwm+50+1300; //电机赋PWM,50手动校准误差,转向PWM波
Motor_B=Velocity_Pwm-50+1300; //电机赋PWM,50手动校准误差,转向PWM波
Xianfu_Pwm(4500); //PWM限幅
Set_Pwm(Motor_A,Motor_B); //直接赋值
delay_ms(180);
Flag_cross=0; //清零
}
//正常走
else
{
Motor_A=Velocity_Pwm+Turn_pwm+50; //电机赋PWM,50手动校准误差,转向PWM波
Motor_B=Velocity_Pwm-Turn_pwm-50; //电机赋PWM,50手动校准误差,转向PWM波
Xianfu_Pwm(4500); //PWM限幅
}
}
else if(mission==4)
{
Motor_A=Velocity_Pwm+Turn_pwm+50; //电机赋PWM,50手动校准误差,转向PWM波
Motor_B=Velocity_Pwm-Turn_pwm-50; //电机赋PWM,50手动校准误差,转向PWM波
Xianfu_Pwm(4500); //PWM限幅
}
//任务三走圈循迹
else if(mission==3)
{
//第一次,第三次,加速冲过去
if(Flag_cross == 1)
{
Motor_A=Velocity_Pwm+50+1000; //电机赋PWM,50手动校准误差,转向PWM波,加速冲过去
Motor_B=Velocity_Pwm-50+1200; //电机赋PWM,50手动校准误差,转向PWM波
Xianfu_Pwm(4500); //PWM限幅
Set_Pwm(Motor_A,Motor_B); //直接赋值
delay_ms(180);
Flag_cross=0; //清零
}
//第二次识别到岔道口,走内圈
else if(Flag_cross == 2)
{
Motor_A=Velocity_Pwm+2100; //电机赋PWM,50手动校准误差,转向PWM波
Motor_B=0; //电机关断
Xianfu_Pwm(4500); //PWM限幅
Set_Pwm(Motor_A,Motor_B); //直接赋值
delay_ms(250);
Flag_cross=0; //清零
}
//正常走
else
{
Motor_A=Velocity_Pwm+Turn_pwm+50; //电机赋PWM,50手动校准误差,转向PWM波
Motor_B=Velocity_Pwm-Turn_pwm-50; //电机赋PWM,50手动校准误差,转向PWM波
Xianfu_Pwm(4500); //PWM限幅
}
}
Set_Pwm(Motor_A,Motor_B);
}
//停车
else
{
Set_Pwm(0,0);
Buzzer_on();
}
}
}
}
//串口发送电机速度函数
void USART_Velocity(void)
{
//以下代码用于调试PID
printf_uart0("Velocity_L: %d ",Encoder_Left); //当前编码器位置打印到串口
printf_uart0("Moto_L: %d ",Motor_A); //当前的PID控制器输出打印到串口
printf_uart0("Target_L: %d\r\n",Target_A); //当前的位置目标值打印到串口
printf_uart0("Velocity_R: %d ",Encoder_Right); //当前编码器位置打印到串口
printf_uart0("Moto_R: %d ",Motor_B); //当前的PID控制器输出打印到串口
printf_uart0("Target_R: %d\r\n",Target_B); //当前的位置目标值打印到串口
}
//OLED显示电机位置,并发送到上位机
void OLED_Position(void)
{
OLED_ShowNum(20,6,(int)(Position_Right/1000),1,8);
OLED_ShowNum(28,6,(int)Position_Right/100%10,1,8);
OLED_ShowNum(36,6,(int)Position_Right/10%100,1,8);
OLED_ShowNum(44,6,(int)Position_Right%1000,1,8);
OLED_ShowNum(20,8,(int)(Position_Left/1000),1,8);
OLED_ShowNum(28,8,(int)Position_Left/100%10,1,8);
OLED_ShowNum(36,8,(int)Position_Left/10%100,1,8);
OLED_ShowNum(44,8,(int)Position_Left%1000,1,8);
DataScope();
delay_ms(50);
}
//电机位置控制
void Position_Control(void)
{
//位置PID
Position_Right+=Encoder_Right;
Position_Left+=Encoder_Left;
Motor_A=Position_PID_L(Position_Left,Target_Position_L); //位置PID控制器
Motor_B=Position_PID_R(Position_Right,Target_Position_R); //位置PID控制器
Xianfu_Pwm(3300); //PWM限幅
Set_Pwm(Motor_A,Motor_B);
}
/**************************************************************************
函数功能:增量PI控制器
入口参数:编码器测量值,目标速度
返回 值:电机PWM
根据增量式离散PID公式
pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)]
e(k)代表本次偏差
e(k-1)代表上一次的偏差 以此类推
pwm代表增量输出
在我们的速度控制闭环系统里面,只使用PI控制
pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)
**************************************************************************/
int Incremental_PI_A (int Encoder,int Target)
{
static int Bias,Pwm,Last_bias;
Bias=Target-Encoder; //计算偏差
Pwm+=Velocity_KD*(Bias-Last_bias)+Velocity_KP*Bias; //增量式PI控制器
if(Pwm>4800)
Pwm=4800;
if(Pwm<-4800)
Pwm=-4800;
Last_bias=Bias; //保存上一次偏差
return Pwm; //增量输出
}
int Incremental_PI_B (int Encoder,int Target)
{
static int Bias,Pwm,Last_bias;
Bias=Target-Encoder; //计算偏差
Pwm+=Velocity_KD*(Bias-Last_bias)+Velocity_KP*Bias; //增量式PI控制器
if(Pwm>4800)
Pwm=4800;
if(Pwm<-4800)
Pwm=-4800;
Last_bias=Bias; //保存上一次偏差
return Pwm; //增量输出
}
/**************************************************************************
函数功能:位置式PID控制器
入口参数:编码器测量位置信息,目标位置
返回 值:电机PWM
根据位置式离散PID公式
pwm=Kp*e(k)+Ki*∑e(k)+Kd[e(k)-e(k-1)]
e(k)代表本次偏差
e(k-1)代表上一次的偏差
∑e(k)代表e(k)以及之前的偏差的累积和;其中k为1,2,,k;
pwm代表输出
**************************************************************************/
int Distance_PID (int position,int target)
{
static float Bias,Pwm,Integral_bias,Last_Bias;
Bias = target-position; //计算偏差
Integral_bias += Bias; //求出偏差的积分
//最大积分偏差3000
if(Integral_bias > 20)
Integral_bias = 20;
if(Integral_bias < -20)
Integral_bias = -20;
Pwm = Distance_KP * Bias + Distance_KI * Integral_bias + Distance_KD * (Bias-Last_Bias);//位置式PID控制器
Last_Bias = Bias; //保存上一次偏差
return Pwm; //增量输出
}
/**************************************************************************
函数功能:位置式PID控制器
入口参数:编码器测量位置信息,目标位置
返回 值:电机PWM
根据位置式离散PID公式
pwm=Kp*e(k)+Ki*∑e(k)+Kd[e(k)-e(k-1)]
e(k)代表本次偏差
e(k-1)代表上一次的偏差
∑e(k)代表e(k)以及之前的偏差的累积和;其中k为1,2,,k;
pwm代表输出
**************************************************************************/
int Position_PID_L (int position,int target)
{
static float Bias,Pwm,Integral_bias,Last_Bias;
Bias = target-position; //计算偏差
Integral_bias += Bias; //求出偏差的积分
//最大积分偏差3000
if(Integral_bias > 2000)
Integral_bias = 2000;
if(Integral_bias < -2000)
Integral_bias = -2000;
Pwm = Position_KP * Bias + Position_KI * Integral_bias + Position_KD * (Bias-Last_Bias);//位置式PID控制器
Last_Bias = Bias; //保存上一次偏差
return Pwm; //增量输出
}
int Position_PID_R (int position,int target)
{
static float Bias,Pwm,Integral_bias,Last_Bias;
Bias = target-position; //计算偏差
Integral_bias += Bias; //求出偏差的积分
//最大积分偏差3000
if(Integral_bias > 2000)
Integral_bias = 2000;
if(Integral_bias < -2000)
Integral_bias = -2000;
Pwm = Position_KP * Bias + Position_KI * Integral_bias + Position_KD * (Bias-Last_Bias);//位置式PID控制器
Last_Bias = Bias; //保存上一次偏差
return Pwm; //增量输出
}
//循迹程序
void DataProcessing(uint8_t *data_buf)
{
if(data_buf[4]==0x01)
{
//中间检测 直行
Turn_pwm = 0;
}
if(data_buf[3]==0x01)
{
//左2检测到 小幅度向左调整
if(Turn_pwm > 500)
{
Turn_pwm = 500;
}
else
{
Turn_pwm += 60;
}
}
if(data_buf[2]==0x01)
{
//左1检测到 大幅度向左调整
if(Turn_pwm > 400)
{
Turn_pwm = 400;
}
else
{
Turn_pwm += 80;
}
}
if(data_buf[5]==0x01)
{
//右2检测到 大幅度向右调
if(Turn_pwm < -500)
{
Turn_pwm = -500;
}
else
{
Turn_pwm -= 60;
}
}
if(data_buf[6]==0x01)
{
//右1检测到 小幅度向右调整
if(Turn_pwm < -400)
{
Turn_pwm = -400;
}
else
{
Turn_pwm -= 80;
}
}
}
//距离程序
void fordistance(void)
{
if(mission==2)
{
if(distance_hcsr04<220&distance_hcsr04>180)//18-22
{
}
else if(distance_hcsr04<180&distance_hcsr04>220)//14-18
{
if(Velocity_Car<18)
Velocity_Car=18;
else
Velocity_Car--;
}
else if(distance_hcsr04<260&distance_hcsr04>220)//22-26
{
if(Velocity_Car>35)
Velocity_Car=35;
else
Velocity_Car++;
}
else if(distance_hcsr04<140)//14
{
if(Velocity_Car<5)
Velocity_Car=5;
else
Velocity_Car=Velocity_Car-10;
}
}
else if(mission==1)
{
//1
if(distance_hcsr04<220&distance_hcsr04>180)//18-22
{
}
else if(distance_hcsr04<180&distance_hcsr04>220)//14-18
{
if(Velocity_Car<18)
Velocity_Car=18;
else
Velocity_Car--;
}
else if(distance_hcsr04<260&distance_hcsr04>220)//22-26
{
if(Velocity_Car>23)
Velocity_Car=23;
else
Velocity_Car++;
}
else if(distance_hcsr04<140)//14
{
if(Velocity_Car<15)
Velocity_Car=15;
else
Velocity_Car=Velocity_Car-10;
}
}
else if(mission==3)
{
//1
if(distance_hcsr04<220&distance_hcsr04>180)//18-22
{
}
else if(distance_hcsr04<180&distance_hcsr04>220)//14-18
{
if(Velocity_Car<18)
Velocity_Car=18;
else
Velocity_Car--;
}
else if(distance_hcsr04<260&distance_hcsr04>220)//22-26
{
if(Velocity_Car>23)
Velocity_Car=23;
else
Velocity_Car++;
}
else if(distance_hcsr04<140)//14
{
if(Velocity_Car<15)
Velocity_Car=15;
else
Velocity_Car=Velocity_Car-10;
}
}
else if( mission==4)
{
if(distance_hcsr04<220&distance_hcsr04>180)//18-22
{
}
else if(distance_hcsr04<180&distance_hcsr04>220)//14-18
{
if(Velocity_Car<18)
Velocity_Car=18;
else
Velocity_Car--;
}
else if(distance_hcsr04<260&distance_hcsr04>220)//22-26
{
if(Velocity_Car>35)
Velocity_Car=35;
else
Velocity_Car++;
}
else if(distance_hcsr04<140)//14
{
if(Velocity_Car<5)
Velocity_Car=5;
else
Velocity_Car=Velocity_Car-10;
}
}
}
/*************************************************************************
函数功能:位置式PID控制器
入口参数:编码器测量位置信息,目标位置
返回 值:电机PWM增量
**************************************************************************/
float Position_PID_1(float Position,float Target)
{ //增量输出
static float Bias,Pwm,Integral_bias,Last_Bias;
Bias=Target-Position; //计算偏差
Integral_bias+=Bias; //求出偏差的积分
Pwm=Position_KP_DuoJi*Bias/100+Position_KI_DuoJi*Integral_bias/100+Position_KD_DuoJi*(Bias-Last_Bias)/100; //位置式PID控制器
Last_Bias=Bias; //保存上一次偏差
return Pwm;
}
/*************************************************************************
函数功能:位置式PID控制器
入口参数:编码器测量位置信息,目标位置
返回 值:电机PWM增量
**************************************************************************/
float Position_PID_2(float Position,float Target)
{ //增量输出
static float Bias,Pwm,Integral_bias,Last_Bias;
Bias=Target-Position; //计算偏差
Integral_bias+=Bias; //求出偏差的积分
Pwm=Position_KP_DuoJi*Bias/100+Position_KI_DuoJi*Integral_bias/100+Position_KD_DuoJi*(Bias-Last_Bias)/100; //位置式PID控制器
Last_Bias=Bias; //保存上一次偏差
return Pwm;
}