新学期和老师做了大创的项目,由我负责底盘的控制,第一做这种项目,内容可能有很多不足,欢迎指正。
文章目录
-
一.硬件资料
- 二.CUBEMX配置
- 三.代码实现
一、硬件资料
开发板:stm32正点原子开发板 战舰
电机驱动×4 电机驱动模块 7A-160
(在使用过程中发现该驱动模块的模拟信号输入区域的接入电压有误,在接入3.3v电压可正常读取信号而输入为5v电压时则会无反应且有可能烧坏驱动模块!)
12v电源 使用时测好电压防止欠压操作
usb转ch340
编码器电机×4 驰海电机 JGB37-520 AB向 11个磁向石
麦克纳姆轮×4
二、CUBEMX配置
先配置时钟树打开外部晶振![](https://i-blog.csdnimg.cn/blog_migrate/c1ad24ac8c25efcfe81c193e86a4ba9c.png)
配置一个50ms的定时器用于PID的计算(20ms是因为听一个电赛大佬说20ms这个电机编码器测速最准)
72mhz/72/20000=50hz=20ms
配置定时器开启编码器模式
在函数中用HAL_TIM_Encoder_Start开启该模式
用HAL_TIM_GetCounter即可得到该定时器的上升沿和下降沿的次数
具体可看编码器教程
讲的比较详细我也是看着他来做的
再打开pwm通道用来控制电机转速
此外还需要开启几个gpio口的输入用来控制电机驱动的逻辑输入
三.代码实现
1.引入pid库
我直接用的时大疆rm的pid代码,Pid代码可以再网上任意找
2.编写编码器读取
void motor_count_out(Motor *motorx,pid_type_def *motorx_speed_pid,uint8_t i)
{
if(i==1){
int16_t pluse = COUNTERNUM1 - RELOADVALUE1/2;
motorx->totalAngle = (pluse + motorx->loopNum * RELOADVALUE1/2);
}
else if(i==2){
int16_t pluse = COUNTERNUM2 - RELOADVALUE2/2;
motorx->totalAngle = (pluse + motorx->loopNum * RELOADVALUE1/2);
}
else if(i==3){
int16_t pluse = COUNTERNUM3 - RELOADVALUE3/2;
motorx->totalAngle = (pluse + motorx->loopNum * RELOADVALUE1/2);
}
else if(i==4){
int16_t pluse = COUNTERNUM4 - RELOADVALUE4/2;
motorx->totalAngle = (pluse + motorx->loopNum * RELOADVALUE1/2);
}
motorx->speed = (float)(motorx->totalAngle - motorx->lastAngle)/(4*11*RR)*50*60; //进行速度计算,根据前文所说的,4倍频,编码器11位,减速比??,
motorx->lastAngle = motorx->totalAngle; //更新转过的圈
}
pluse即为编码器读取电压脉冲的增量
用电压脉冲的增量去计算速度
在计算前可先用debug对转子不上电拨动,查看转子转动时返回编码器读数的情况以及判断其正负状态
PID
设置预期值
void set_pid_target(pid_type_def *pid, float temp_val)
{
pid->set = temp_val; // 设置当前的目标值
}
PID控制的输入
float speed_pid_contro(Motor *motorx,pid_type_def *motorx_speed_pid,int set)
{
float cont_val = 0.0; // 当前控制值
uint64_t set_give=0;
fp64 actual_speed=0;
set_pid_target(motorx_speed_pid,set);
set_give=motorx_speed_pid->set;
actual_speed = fabs(motorx->speed);
cont_val = PID_calc(motorx_speed_pid,actual_speed,set_give); // 进行 PID 计算
return cont_val;
}
PID控制电机
因为使用pid控制电机维持在一个恒定的速度属于一个持续的过程,故不可将PID写在主函数中(因为主函数中程序一步一步执行,如果你在pid控制电机输出后加一个delay那么电机占空比将在这一时刻保持不变就无法进行动态的调整了),所以将pid控制电机输出的占空比写再定时器中
if(htim->Instance==htim6.Instance) //20ms中断
{
motor_count_out(&motor1,&motor1_speed_pid,1);
motor_count_out(&motor2,&motor2_speed_pid,2);
motor_count_out(&motor3,&motor3_speed_pid,3);
motor_count_out(&motor4,&motor4_speed_pid,4);
switch(point){
case 1:ZhiZou(25,25,25,25);break;
case 2:HouTui(25,25,25,25);break;
case 3:ZuoYi(25,25,25,25);break;
case 4:YouYi (25,25,25,25);break;
case 5:stop();break;
default:break;
}
以下为电机指令的代码
void ZhiZou(int set1,int set2,int set3,int set4)
{
zhenzhuan1(set1);
zhenzhuan2(set2);
zhenzhuan3(set3);
zhenzhuan4(set4);
}
void zhenzhuan1 (int set) //1号电机速度正转
{
uint64_t out=0;
IN1_1(0);
IN2_1(1);
out=speed_pid_contro(&motor1,&motor1_speed_pid,set);
__HAL_TIM_SetCompare(&htim8,TIM_CHANNEL_3,out);//out
}
pid参数的整定
仅为我个人的一些看法,再起初调节Pid时我得到的反馈值总是会处于波动的状态,后来发现是需要给一些D的参数值,P一般是用来提供系统快速的响应,但是会和目标值有一定的稳态误差,这是引入I值来消除这些稳态误差,但是这又会带给系统一些振荡,故需要调节D值給一些阻尼,来减小波动
总结
至此可以完成底盘电机的基本控制