硬件部分:
1.STM32F407VET6开发板
2.13线霍尔编码器
3.AT8236电机驱动
4.VS code / Platformio
5.星瞳串口波形显示器
#include
//A电机端口定义
#define Motor_A_IN1 PE9 //输入1
#define Motor_A_IN2 PE11 //输入2
#define Motor_A_countA PA15 //编码器A
#define Motor_A_countB PB3 //编码器B
//B电机端口定义
#define Motor_B_IN1 PE13 //输入1
#define Motor_B_IN2 PE14 //输入2
#define Motor_B_countA PB4 //编码器A
#define Motor_B_countB PB5 //编码器B
//C电机端口定义
#define Motor_C_IN1 PE5 //输入1
#define Motor_C_IN2 PE6 //输入2
#define Motor_C_countA PA0 //编码器A
#define Motor_C_countB PA1 //编码器B
//D电机端口定义
#define Motor_D_IN1 PB15 //输入1
#define Motor_D_IN2 PB14 //输入2
#define Motor_D_countA PD12 //编码器A
#define Motor_D_countB PD13 //编码器B
//引脚初始化
void Motor_Init(){
//A电机
pinMode(Motor_A_IN2,OUTPUT); //驱动芯片控制引脚
pinMode(Motor_A_IN1,OUTPUT); //驱动芯片控制引脚,PWM调速
pinMode(Motor_A_countA,INPUT); //编码器A引脚
pinMode(Motor_A_countB,INPUT); //编码器B引脚
//B电机
pinMode(Motor_B_IN2,OUTPUT); //驱动芯片控制引脚
pinMode(Motor_B_IN1,OUTPUT); //驱动芯片控制引脚,PWM调速
pinMode(Motor_B_countA,INPUT); //编码器A引脚
pinMode(Motor_B_countB,INPUT); //编码器B引脚
//C电机
pinMode(Motor_C_IN2,OUTPUT); //驱动芯片控制引脚
pinMode(Motor_C_IN1,OUTPUT); //驱动芯片控制引脚,PWM调速
pinMode(Motor_C_countA,INPUT); //编码器A引脚
pinMode(Motor_C_countB,INPUT); //编码器B引脚
//D电机
pinMode(Motor_D_IN2,OUTPUT); //驱动芯片控制引脚
pinMode(Motor_D_IN1,OUTPUT); //驱动芯片控制引脚,PWM调速
pinMode(Motor_D_countA,INPUT); //编码器A引脚
pinMode(Motor_D_countB,INPUT); //编码器B引脚
//驱动芯片控制引脚全部拉低
digitalWrite(Motor_A_IN2,LOW); //A电机
digitalWrite(Motor_A_IN1,LOW);
digitalWrite(Motor_B_IN2,LOW); //B电机
digitalWrite(Motor_B_IN1,LOW);
digitalWrite(Motor_C_IN2,LOW); //C电机
digitalWrite(Motor_C_IN1,LOW);
digitalWrite(Motor_D_IN2,LOW); //D电机
digitalWrite(Motor_D_IN1,LOW);
}
int Incremental_Pid_A(int current_speed,int target_speed){
static float pwm,bias,last_bias,prev_bias; //静态变量存在程序全周期:pwm:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差
bias=current_speed-target_speed; //计算本次偏差e(k)
pwm-=(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias)); //增量式PID控制器
prev_bias=last_bias; //保存上上次偏差
last_bias=bias; //保存上一次偏差
//PWM 限幅度 Arduino的PWM 最高为255 限制在250
if(pwm<-250){
pwm=250;
}
if(pwm>250){
pwm=250;
}
//Serial.println(pwm);
return pwm; //增量输出
}
/***************************
* 中断函数:读A轮脉冲
*
**************************/
void Read_Moto_A(){
motorA++;
}
/**************************
* 中断函数:读B轮脉冲
*
*************************/
void Read_Moto_B(){
motorB++;
}
/**************************
* 中断函数:读C轮脉冲
*
*************************/
void Read_Moto_C(){
motorC++;
}
/**************************
* 中断函数:读D轮脉冲
*
*************************/
void Read_Moto_D(){
motorD++;
}
/***********************************
* 电机实际速度计算:
* 已知参数:
* 车轮直径100mm,
* AC轮子一圈:13*30=390脉冲(RISING)
* BD轮子一圈:13*30=390脉冲(RISING)
* 单位时间读两个轮子脉冲读取两个轮子脉冲
***********************************/
void Read_Moto_V(){
unsigned long nowtime=0;
motorA=0;
motorB=0;
motorC=0;
motorD=O;
nowtime=millis()+50;//读50毫秒
attachInterrupt(digitalPinToInterrupt(Motor_A_countA),Read_Moto_A,RISING);//左轮脉冲开中断计数
attachInterrupt(digitalPinToInterrupt(Motor_B_countA),Read_Moto_B,RISING);//右轮脉冲开中断计数
attachInterrupt(digitalPinToInterrupt(Motor_C_countA),Read_Moto_C,RISING);//左轮脉冲开中断计数
attachInterrupt(digitalPinToInterrupt(Motor_D_countA),Read_Moto_D,RISING);//左轮脉冲开中断计数
while(millis()
5.电机速度控制/模式选择函数函数
/**************************************************************************
函数功能:设置电机工作模式和运动速度
入口参数:工作模式,pwm
返回 值:无
**************************************************************************/
void Set_Pwm(int mode,int speed_A,int speed_B,int speed_C,int speed_D){
if(mode==1){
//前进模式
//A电机
digitalWrite(Motor_A_IN2,LOW);
analogWrite(Motor_A_IN1,speed_A);
//B电机
digitalWrite(Motor_B_IN2,LOW);
analogWrite(Motor_B_IN1,speed_B);
//C电机
digitalWrite(Motor_C_IN2,LOW);
analogWrite(Motor_C_IN1,speed_C);
//D电机
digitalWrite(Motor_D_IN2,LOW);
analogWrite(Motor_D_IN1,speed_D);
}else if(mode==2){
//后退模式
//A电机
digitalWrite(Motor_A_IN2,speed_A);
analogWrite(Motor_A_IN1,LOW);
//B电机
digitalWrite(Motor_B_IN2,speed_B);
analogWrite(Motor_B_IN1,LOW);
//C电机
digitalWrite(Motor_A_IN2,speed_C);
analogWrite(Motor_A_IN1,LOW);
//D电机
digitalWrite(Motor_B_IN2,speed_D);
analogWrite(Motor_B_IN1,LOW);
}else if(mode==3){
//左旋转模式
//A电机
digitalWrite(Motor_A_IN2,speed_A);
analogWrite(Motor_A_IN1,LOW);
//B电机
digitalWrite(Motor_B_IN2,LOW);
analogWrite(Motor_B_IN1,speed_B);
//C电机
digitalWrite(Motor_C_IN2,speed_C);
analogWrite(Motor_C_IN1,LOW);
//D电机
digitalWrite(Motor_D_IN2,LOW);
analogWrite(Motor_D_IN1,speed_D);
}else if(mode==4){
//右旋转模式
//A电机
digitalWrite(Motor_A_IN2,LOW);
analogWrite(Motor_A_IN1,speed_A);
//B电机
digitalWrite(Motor_B_IN2,speed_B);
analogWrite(Motor_B_IN1,LOW);
//C电机
digitalWrite(Motor_C_IN2,LOW);
analogWrite(Motor_C_IN1,speed_C);
//D电机
digitalWrite(Motor_D_IN2,speed_D);
analogWrite(Motor_D_IN1,LOW);
}else if(mode==5){
//左平移模式
//A电机
digitalWrite(Motor_A_IN2,LOW);
analogWrite(Motor_A_IN1,speed_A);
//B电机
digitalWrite(Motor_B_IN2,speed_B);
analogWrite(Motor_B_IN1,LOW);
//C电机
digitalWrite(Motor_C_IN2,speed_C);
analogWrite(Motor_C_IN1,LOW);
//D电机
digitalWrite(Motor_D_IN2,speed_D);
analogWrite(Motor_D_IN1,LOW);
}else if(mode==6){
//右平移模式
//A电机
digitalWrite(Motor_A_IN2,speed_A);
analogWrite(Motor_A_IN1,LOW);
//B电机
digitalWrite(Motor_B_IN2,LOW);
analogWrite(Motor_B_IN1,speed_B);
//C电机
digitalWrite(Motor_C_IN2,LOW);
analogWrite(Motor_C_IN1,speed_C);
//D电机
digitalWrite(Motor_D_IN2,speed_D);
analogWrite(Motor_D_IN1,LOW);
}
}
//PID串口调试函数
//格式:参数(kp/ki/kp) 数值
//示例:ki 2.1
void PID_test(){
while (Serial.available() > 0) { // 串口收到字符数大于零。
if(Serial.find("kp"))
{
kp = Serial.parseFloat();
}
if(Serial.find("ki"))
{
ki = Serial.parseFloat();
}
if(Serial.find("kd"))
{
kd = Serial.parseFloat();
}
}
}
#include
//A电机端口定义
#define Motor_A_IN1 PE9 //输入1
#define Motor_A_IN2 PE11 //输入2
#define Motor_A_countA PA15 //编码器A
#define Motor_A_countB PB3 //编码器B
//B电机端口定义
#define Motor_B_IN1 PE13 //输入1
#define Motor_B_IN2 PE14 //输入2
#define Motor_B_countA PB4 //编码器A
#define Motor_B_countB PB5 //编码器B
//C电机端口定义
#define Motor_C_IN1 PE5 //输入1
#define Motor_C_IN2 PE6 //输入2
#define Motor_C_countA PA0 //编码器A
#define Motor_C_countB PA1 //编码器B
//D电机端口定义
#define Motor_D_IN1 PB14 //输入1
#define Motor_D_IN2 PB15 //输入2
#define Motor_D_countA PD12 //编码器A
#define Motor_D_countB PD13 //编码器B
//定义相关变量
volatile float motorA=0;//中断变量,A轮子脉冲计数
volatile float motorB=0;//中断变量,B轮子脉冲计数
volatile float motorC=0;//中断变量,C轮子脉冲计数
volatile float motorD=0;//中断变量,D轮子脉冲计数
float V_A=0; //A轮速度 单位cm/s
float V_B=0; //B边轮速 单位cm/s
float V_C=0; //C轮速度 单位cm/s
float V_D=0; //D轮速度 单位cm/s
int v1=0; //单位cm/s
int v2=0; //单位cm/s
int v3=0;
int v4=0;
float Target_V_A=40,Target_V_B=40,Target_V_C=40,Target_V_D=40; //单位cm/s 经过测试最大速度为 :150 cm/s
int Pwm_A=0,Pwm_B=0,Pwm_C=0,Pwm_D=0 ; //左右轮PWM
//PID变量(根据个人情况调试)
float kp=1,ki=0.15,kd=0; //PID参数
//函数声明
void Motor_Init();//电机初始化
void Read_Moto_V();//读取电机速度 单位:cm/s
void Read_Moto_A(); //读取编码器脉冲
void Read_Moto_B();
void Read_Moto_C();
void Read_Moto_D();
void Set_Pwm(int mode,int speed_A,int speed_B,int speed_C,int speed_D);//pwm设置
int Incremental_Pid_A(int current_speed,int target_speed);//pid函数
int Incremental_Pid_B(float current_speed,float target_speed);
int Incremental_Pid_C(float current_speed,float target_speed);
int Incremental_Pid_D(float current_speed,float target_speed);
void PID_test();//离线kp ki kd 调试
/**************************************
* //Arduino初始化函数
*************************************/
void setup() {
Motor_Init();//电机端口初始化
Serial.begin(9600);//开启串口
}
/***************************************
* Arduino主循环
*
***************************************/
void loop() {
Read_Moto_V();//读取脉冲计算速度
Pwm_A=Incremental_Pid_A(V_A,Target_V_A);//A轮PI运算
Pwm_B=Incremental_Pid_B(V_B,Target_V_B);//B轮PI运算
Pwm_C=Incremental_Pid_C(V_C,Target_V_C);//C轮PI运算
Pwm_D=Incremental_Pid_D(V_D,Target_V_D);//D轮PI运算
Serial.print("V_A: ");
Serial.print(V_A);
Serial.print("cm/s, V_B: ");
Serial.print(V_B);
Serial.print("cm/s, V_C: ");
Serial.print(V_C);
Serial.print("cm/s, V_D: ");
Serial.print(V_D);
Serial.println("cm/s");
Set_Pwm(1,Pwm_A,Pwm_B,Pwm_C,Pwm_D); //设置速度
// PID_test();//调试参数
}
/*********************************************************
* 函数功能:增量式PI控制器(A电机)
* 入口参数:当前速度(编码器测量值),目标速度
* 返回 值:电机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):上一次偏差
* e(k-2):上上次偏差
* Pwm:代表增量输出
* 在速度闭环控制系统里面我们只使用PI控制,因此对PID公式可简化为:
* Pwm-=Kp*[e(k)-e(k-1)]+Ki*e(k)
* e(k):本次偏差
* e(k-1):上一次偏差
* Pwm:代表增量输出
*
* 注意增量式PID先调I,再调P,最后再调D
*********************************************************/
int Incremental_Pid_A(int current_speed,int target_speed){
static float pwm,bias,last_bias,prev_bias; //静态变量存在程序全周期:pwm:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差
bias=current_speed-target_speed; //计算本次偏差e(k)
pwm-=(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias)); //增量式PID控制器
prev_bias=last_bias; //保存上上次偏差
last_bias=bias; //保存上一次偏差
//PWM 限幅度 Arduino的PWM 最高为255 限制在250
if(pwm<-250){
pwm=250;
}
if(pwm>250){
pwm=250;
}
//Serial.println(pwm);
return pwm; //增量输出
}
//B电机速度增量式PID控制器
int Incremental_Pid_B(float current_speed,float target_speed){
static float pwm,bias,last_bias,prev_bias; //静态变量存在程序全周期:pwm:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差
bias=current_speed-target_speed; //计算本次偏差e(k)
pwm-=(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias)); //增量式PID控制器
prev_bias=last_bias; //保存上上次偏差
last_bias=bias; //保存上一次偏差
//PWM 限幅度 Arduino的PWM 最高为255限制在250
if(pwm<-250){
pwm=250;
}
if(pwm>250){
pwm=250;
}
//Serial.println(pwm);
return pwm; //增量输出
}
//C电机速度增量式PID控制器
int Incremental_Pid_C(float current_speed,float target_speed){
static float pwm,bias,last_bias,prev_bias; //静态变量存在程序全周期:pwm:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差
bias=current_speed-target_speed; //计算本次偏差e(k)
pwm-=(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias)); //增量式PID控制器
prev_bias=last_bias; //保存上上次偏差
last_bias=bias; //保存上一次偏差
//PWM 限幅度 Arduino的PWM 最高为255限制在250
if(pwm<-250){
pwm=250;
}
if(pwm>250){
pwm=250;
}
//Serial.println(pwm);
return pwm; //增量输出
}
//D电机速度增量式PID控制器
int Incremental_Pid_D(float current_speed,float target_speed){
static float pwm,bias,last_bias,prev_bias; //静态变量存在程序全周期:pwm:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差
bias=current_speed-target_speed; //计算本次偏差e(k)
pwm-=(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias)); //增量式PID控制器
prev_bias=last_bias; //保存上上次偏差
last_bias=bias; //保存上一次偏差
//PWM 限幅度 Arduino的PWM 最高为255限制在250
if(pwm<-250){
pwm=250;
}
if(pwm>250){
pwm=250;
}
//Serial.println(pwm);
return pwm; //增量输出
}
/**************************************************************************
函数功能:设置电机工作模式和运动速度
入口参数:工作模式,pwm
返回 值:无
**************************************************************************/
void Set_Pwm(int mode,int speed_A,int speed_B,int speed_C,int speed_D){
if(mode==1){
//前进模式
//A电机
digitalWrite(Motor_A_IN2,LOW);
analogWrite(Motor_A_IN1,speed_A);
//B电机
digitalWrite(Motor_B_IN2,LOW);
analogWrite(Motor_B_IN1,speed_B);
//C电机
digitalWrite(Motor_C_IN2,LOW);
analogWrite(Motor_C_IN1,speed_C);
//D电机
digitalWrite(Motor_D_IN2,LOW);
analogWrite(Motor_D_IN1,speed_D);
}else if(mode==2){
//后退模式
//A电机
digitalWrite(Motor_A_IN2,speed_A);
analogWrite(Motor_A_IN1,LOW);
//B电机
digitalWrite(Motor_B_IN2,speed_B);
analogWrite(Motor_B_IN1,LOW);
//C电机
digitalWrite(Motor_A_IN2,speed_C);
analogWrite(Motor_A_IN1,LOW);
//D电机
digitalWrite(Motor_B_IN2,speed_D);
analogWrite(Motor_B_IN1,LOW);
}else if(mode==3){
//左旋转模式
//A电机
digitalWrite(Motor_A_IN2,speed_A);
analogWrite(Motor_A_IN1,LOW);
//B电机
digitalWrite(Motor_B_IN2,LOW);
analogWrite(Motor_B_IN1,speed_B);
//C电机
digitalWrite(Motor_C_IN2,speed_C);
analogWrite(Motor_C_IN1,LOW);
//D电机
digitalWrite(Motor_D_IN2,LOW);
analogWrite(Motor_D_IN1,speed_D);
}else if(mode==4){
//右旋转模式
//A电机
digitalWrite(Motor_A_IN2,LOW);
analogWrite(Motor_A_IN1,speed_A);
//B电机
digitalWrite(Motor_B_IN2,speed_B);
analogWrite(Motor_B_IN1,LOW);
//C电机
digitalWrite(Motor_C_IN2,LOW);
analogWrite(Motor_C_IN1,speed_C);
//D电机
digitalWrite(Motor_D_IN2,speed_D);
analogWrite(Motor_D_IN1,LOW);
}else if(mode==5){
//左平移模式
//A电机
digitalWrite(Motor_A_IN2,LOW);
analogWrite(Motor_A_IN1,speed_A);
//B电机
digitalWrite(Motor_B_IN2,speed_B);
analogWrite(Motor_B_IN1,LOW);
//C电机
digitalWrite(Motor_C_IN2,speed_C);
analogWrite(Motor_C_IN1,LOW);
//D电机
digitalWrite(Motor_D_IN2,speed_D);
analogWrite(Motor_D_IN1,LOW);
}else if(mode==6){
//右平移模式
//A电机
digitalWrite(Motor_A_IN2,speed_A);
analogWrite(Motor_A_IN1,LOW);
//B电机
digitalWrite(Motor_B_IN2,LOW);
analogWrite(Motor_B_IN1,speed_B);
//C电机
digitalWrite(Motor_C_IN2,LOW);
analogWrite(Motor_C_IN1,speed_C);
//D电机
digitalWrite(Motor_D_IN2,speed_D);
analogWrite(Motor_D_IN1,LOW);
}
}
//引脚初始化
void Motor_Init(){
//A电机
pinMode(Motor_A_IN2,OUTPUT); //驱动芯片控制引脚
pinMode(Motor_A_IN1,OUTPUT); //驱动芯片控制引脚,PWM调速
pinMode(Motor_A_countA,INPUT); //编码器A引脚
pinMode(Motor_A_countB,INPUT); //编码器B引脚
//B电机
pinMode(Motor_B_IN2,OUTPUT); //驱动芯片控制引脚
pinMode(Motor_B_IN1,OUTPUT); //驱动芯片控制引脚,PWM调速
pinMode(Motor_B_countA,INPUT); //编码器A引脚
pinMode(Motor_B_countB,INPUT); //编码器B引脚
//C电机
pinMode(Motor_C_IN2,OUTPUT); //驱动芯片控制引脚
pinMode(Motor_C_IN1,OUTPUT); //驱动芯片控制引脚,PWM调速
pinMode(Motor_C_countA,INPUT); //编码器A引脚
pinMode(Motor_C_countB,INPUT); //编码器B引脚
//D电机
pinMode(Motor_D_IN2,OUTPUT); //驱动芯片控制引脚
pinMode(Motor_D_IN1,OUTPUT); //驱动芯片控制引脚,PWM调速
pinMode(Motor_D_countA,INPUT); //编码器A引脚
pinMode(Motor_D_countB,INPUT); //编码器B引脚
//驱动芯片控制引脚全部拉低
digitalWrite(Motor_A_IN2,LOW); //A电机
digitalWrite(Motor_A_IN1,LOW);
digitalWrite(Motor_B_IN2,LOW); //B电机
digitalWrite(Motor_B_IN1,LOW);
digitalWrite(Motor_C_IN2,LOW); //C电机
digitalWrite(Motor_C_IN1,LOW);
digitalWrite(Motor_D_IN2,LOW); //D电机
digitalWrite(Motor_D_IN1,LOW);
}
/***********************************
* 电机实际速度计算:
* 已知参数:
* 车轮直径100mm,
* 左边轮子一圈:13*30=390脉冲(RISING)
* 右边轮子一圈:13*30=390脉冲(RISING)
* 单位时间读两个轮子脉冲读取两个轮子脉冲
***********************************/
void Read_Moto_V(){
unsigned long nowtime=0;
motorA=0;
motorB=0;
motorC=0;
motorD=0;
nowtime=millis()+50;//读50毫秒
attachInterrupt(digitalPinToInterrupt(Motor_A_countA),Read_Moto_A,RISING);//A轮脉冲开中断计数
attachInterrupt(digitalPinToInterrupt(Motor_B_countA),Read_Moto_B,RISING);//B轮脉冲开中断计数
attachInterrupt(digitalPinToInterrupt(Motor_C_countA),Read_Moto_C,RISING);//C轮脉冲开中断计数
attachInterrupt(digitalPinToInterrupt(Motor_D_countA),Read_Moto_D,RISING);//D轮脉冲开中断计数
while(millis() 0) { // 串口收到字符数大于零。
if(Serial.find("kp"))
{
kp = Serial.parseFloat();
}
if(Serial.find("ki"))
{
ki = Serial.parseFloat();
}
if(Serial.find("kd"))
{
kd = Serial.parseFloat();
}
}
}
本人pid调参过程极其痛苦,很久才发现方向错误,所以本人不想经历第二次调参过程,所以没有相应过程图片,可以参考一下最后调参完成的波形:
不过下面给大家一些过来人的建议:
对于增量式pid调参,尤其是对于13线霍尔编码器,首先调ki,尽量选择10附近的值采用二分法测量,测量过程对于变化较大的波形应及时舍去,当波形满足略微振荡,在调整时间t_s内调整次数在3-5次可以确保调整时间t_s较小,而最大超调量M_p也较小。接着调节kp,这个主要影响其稳定性,可以从10左右的ki开始下调,直至M_p基本等于零,这样就基本完成pi调试,最后kd我的结果为0,查阅相关资料后,发现大部分情况低精度速度增量编码器很难会用到kd,所以大家参考我这种应用场景就可以忽略。
最后祝大家早日找到属于你的kp和ki!(完结撒花)
cbirdfly.