Motor
- 主要内容
- 前置知识
- CubeMX配置
- 代码
- 出现的问题
- 参考文献
主要内容
基于我鸽的电控操作 主控 STM32F429IGT6 电机TT小黄 所以两路编码器模拟汽车 包括前置知识 PID PWM 定时器 LM2596 L298N等
前置知识
PID你可以看看我之前的这篇文章PID简单算法 PWM你可以看看我之前的这篇文章PWM LM2596可调降压模块 网上信息相当多 也比较好用 拿表笔对着测量旋钮 注意不要反 否则直接击穿就不能再用了 简单介绍一下L298N电机驱动模块 原理图如图 1脚接入12V及以下电压 3脚就能输出5V电压 给单片机供电 5 六是单片机接入PWM波 8 9端接电机 可根据需要或调整的速度控制电机 下图为真值表 也可以通过信号接入控制正反转 当然TB6612也行 性能可能更好 但是我用过的时候有一点bug 钽电容器也烧过一次 直接芯片用单个模块和降压电路安全电压燃烧 后来画PCB集成后就好了 TB6612元10元 L298N也就几块钱 性能没有差别 再带一块LM2596一样的效果
CubeMX配置
控制单片机时注意 用定时器1和2 打开编码器模式 因为输出是距离 因此,需要单开两个基本定时器6 7做计时 一个用来求速度 一个用来调PID 后续代码处可以看到 有一个电机结构 将计算的速度作为结构变量的赋值之一 新建CubeMX工程 开时钟 TIM1和TIM打开编码器模式 TIM3开四路PWM TIM6和TIM7开计时 并根据自己设定的频率计算所需的定时器周期(PSC&ARR)并且开TIM6和TIM7中断 设置抢占优先级为1 如果6.5.x及以上的CubeMX记得调Systick优先级为0 (按上述步骤开始 图中还有其他引脚被其他功能使用 除定时器开启状态外,无参考) 一个小tip:当你自己画板子或者你拿到的板子有些引脚没有引出来 搜索栏可以搜索相应的功能 一般来说,它有与其他引脚相同的功能(重复使用)。把引脚换成那个引脚很好
代码
新建PID文件,Motor电机文件 adv底盘文件 名字不重要 PID看看前面的文章 结构体基本不变 我就不多写了 motor.h:
typedef struct {
int16_t Angle; int32_t LastAngle; float Speed; int16_t TargetSpeed; int32_t TargetAngle; int32_t TotalAngle; PID SpeedPID; CascadePID AnglePID; }Motor;
有些类似#ifndef #endif这里就不介绍C语言背景知识了 少bug操作而已 motor.c:
///统计电机的角度和速度 void Motor_Cal(Motor *motor) {
int32_t dAngle=0; if(motor->Angle-motor->LastAngle<-30000) ///越界处理 dAngle=motor->Angle (65535-motor->LastAngle); else if(motor->Angle-motor->LastAngle>30000) dAngle=-motor->LastAngle-(65535-motor->Angle); else dAngle=motor->Angle-motor->LastAngle;
//将角度增量加入计数器
motor->TotalAngle+=dAngle;
//计算速度
motor->Speed = (float)dAngle/(4*13*48)*50*60;//rpm *50*60与定时器频率匹配
//倍频 刻度 减速比 定时器中断周期 换算成分钟
//记录角度
motor->LastAngle=motor->Angle;
}
前面if里面是对角度的越界处理 比如你的LastAngle有点大 一步跨到ARR之外了 就自动从0开始了 所以做这个判断 速度的计算公式 因为用了AB相 相当于四倍频 电机用的TT 13个刻度 减速比1:48 因电机而异 定时器周期前面设置的对应值 *60换成分钟
//设置马达pwm
void Motor_SetPWM(float PWM1,float PWM2)
{
if(PWM1>0)
{
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_1, PWM1);
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_2, 0);
}
else
{
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_1, 0);
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_2,ABS(PWM1));
//正反转
}
if(PWM2>0)
{
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_3, PWM2);
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_4, 0);
}
else
{
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_3, 0);
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_4, ABS(PWM2));
}
}
通过你设置的PWM来规定正反转 写到对应通道 用上面L298N的真值表对应 adv.h
typedef struct
{
uint8_t mode; //底盘模式
float averge;
Motor motors[2]; //电机结构体
}ADV;
void Adv_init()
{
HAL_TIM_Base_Start_IT(&htim6);
HAL_TIM_Base_Start_IT(&htim7);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1); //电机pwm
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);
HAL_TIM_Encoder_Start(&htim1,TIM_CHANNEL_ALL); //电机编码器
HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_ALL);
Adv_PIDInit();
}
/***************内部函数********************/
//PID初始化
void Adv_PIDInit()
{
PID_Init(&chassis.motor[0].SpeedPID,200,0.005,0,10000,10000);
PID_Init(&chassis.motor[1].SpeedPID,200,0.006,1,10000,10000);
PID_Init(&chassis.motor[0].AnglePID.inner,200,0.005,0,10000,10000);
PID_Init(&chassis.motor[1].AnglePID.inner,200,0.006,1,10000,10000);
PID_Init(&chassis.motor[0].AnglePID.outer,0.2,0,0.5,40,40);
PID_Init(&chassis.motor[1].AnglePID.outer,0.2,0,0.5,40,40);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef*htim)
{
if(htim==&htim6)
{
Adv.motor[0].Angle=__HAL_TIM_GET_COUNTER(&htim1);
Adv.motor[1].Angle=__HAL_TIM_GET_COUNTER(&htim2);
Motor_Cal(&Adv.motor[0]);
Motor_Cal(&Adv.motor[1]);
}
if(htim==&htim7)
{
for(uint8_t i=0;i<2;i++)
{
PID_SingleCalc(&Adv.motor[i].SpeedPID,Adv.motor[i].TargetSpeed,Adv.motor[i].Speed);
}
Motor_SetPWM(Adv.motor[0].SpeedPID.output,Adv.motor[1].SpeedPID.output);
}
}
在回调函数里面分别实现这两个功能 按设定的周期走的 主函数都不用动 除了调用底盘函数
/* USER CODE BEGIN 2 */
Adv_init();
/* USER CODE END 2 */
如果后续还有功能要加 比如巡线等等 直接在底盘里面修改就行
出现的问题
没啥问题 改了几个参数自己又跑了一遍 效果挺好 就是F4有点大材小用 用F1C8T6最小系统就能实现
参考文献
github暂时不太好使 后续补完整工程 1.野火零基础教程&电机教程 野火论坛或者资料下载中心可以下载 东西可太全了 百度一搜就行 2.吉甲电控yyds 学到不少东西 可惜上岁数了打不了rm寄
2022.7.16 加个广告 欢迎各位来看新写的文章 关于简单控制GM6020电机 上硬件实测好用 RM_EE_Note 1