3. 底层电机调试:电机驱动原理、PID参数整定、编码器数据读取与校准、电机堵转与过流保护
电机调试,是AMR控制系统里最「接地气」的一环。说白了,你算法写得再漂亮,电机转不起来,车就是一块废铁。我刚开始做机器人那会儿,就吃过这个亏——PID参数调了一整天,结果发现编码器线接反了。嗯,这种低级错误,谁还没犯过几次呢?
核心要点:电机调试的本质,是让控制指令与物理运动之间建立精确的映射关系。你给电机发一个速度指令,它就得老老实实按这个速度转,不能抖、不能偏、不能过热。
3.1 电机驱动原理
先说说驱动原理。AMR常用的直流有刷电机或无刷电机,驱动方式其实不复杂——靠PWM波控制MOS管的开关,调节平均电压来调速。我习惯把驱动电路分成三块:功率级、逻辑级、保护级。
- 功率级:H桥电路,控制电机正反转。四个MOS管,对角导通。
- 逻辑级:PWM信号输入,一般用定时器产生,频率我常用10kHz~20kHz。
- 保护级:电流采样电阻+比较器,一旦过流立刻关断。
这里有个坑——PWM频率不能太低。我见过有人用1kHz,电机嗡嗡响,发热严重。为什么?频率太低,电流纹波大,铁损飙升。建议至少10kHz起步,听不到啸叫为止。
// 简单的PWM初始化(STM32 HAL库)
void Motor_PWM_Init(void)
{
TIM_HandleTypeDef htim;
htim.Instance = TIM2;
htim.Init.Prescaler = 84-1; // 84MHz / 84 = 1MHz
htim.Init.Period = 100-1; // 1MHz / 100 = 10kHz
htim.Init.CounterMode = TIM_COUNTERMODE_UP;
HAL_TIM_PWM_Init(&htim);
TIM_OC_InitTypeDef sConfigOC;
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0; // 初始占空比0
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_1);
}
我的经验:调试驱动电路时,先别接电机。用示波器看PWM波形,确认频率、占空比、死区时间都对,再上电机。我曾经跳过这一步,结果MOS管直通短路,板子冒烟了……
3.2 PID参数整定
PID整定,是电机调试的重头戏。很多人一上来就调三个参数,结果越调越乱。我个人的习惯是:先调P,再调I,最后调D。别急着上积分,先让系统稳定下来。
| 参数 | 作用 | 调大后的效果 | 调小后的效果 |
|---|---|---|---|
| Kp(比例) | 快速响应误差 | 响应快,可能超调 | 响应慢,稳态误差大 |
| Ki(积分) | 消除稳态误差 | 消除静差,可能振荡 | 稳态误差消除慢 |
| Kd(微分) | 抑制超调 | 阻尼增加,响应变慢 | 超调明显,系统易抖 |
具体怎么调?我一般用「临界比例法」:
- 先把Ki和Kd设为0,只留Kp。
- 慢慢增大Kp,直到系统出现等幅振荡。
- 记录此时的Kp(记为Ku)和振荡周期Tu。
- 按经验公式计算:Kp=0.6Ku,Ki=1.2Ku/Tu,Kd=0.075Ku*Tu。
- 微调,直到响应曲线满意。
注意:积分项千万别设太大。我遇到过一台AGV,Ki设高了,电机低速时来回抖,像得了帕金森。后来把Ki砍掉一半,立马稳了。积分饱和也是个常见问题,记得加限幅。
// 位置式PID实现(C语言)
typedef struct {
float Kp, Ki, Kd;
float integral;
float prev_error;
float limit; // 输出限幅
} PID_Controller;
float PID_Update(PID_Controller *pid, float target, float current)
{
float error = target - current;
pid->integral += error;
// 积分限幅
if(pid->integral > pid->limit) pid->integral = pid->limit;
if(pid->integral < -pid->limit) pid->integral = -pid->limit;
float output = pid->Kp * error
+ pid->Ki * pid->integral
+ pid->Kd * (error - pid->prev_error);
pid->prev_error = error;
// 输出限幅
if(output > pid->limit) output = pid->limit;
if(output < -pid->limit) output = -pid->limit;
return output;
}
3.3 编码器数据读取与校准
编码器是电机的「眼睛」。没有准确的编码器数据,PID就是瞎调。我常用的编码器有两种:增量式和绝对式。增量式便宜,但上电不知道位置;绝对式贵,但一开机就知道角度。
读取编码器,核心是处理脉冲信号。我习惯用定时器的编码器模式,硬件自动计数,省CPU资源。
// 定时器编码器模式配置
void Encoder_Init(void)
{
TIM_Encoder_InitTypeDef sEncoder;
TIM_MasterConfigTypeDef sMasterConfig;
htim3.Instance = TIM3;
htim3.Init.Prescaler = 0;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 65535;
sEncoder.EncoderMode = TIM_ENCODERMODE_TI12; // 双沿计数
sEncoder.IC1Polarity = TIM_ICPOLARITY_RISING;
sEncoder.IC2Polarity = TIM_ICPOLARITY_RISING;
HAL_TIM_Encoder_Init(&htim3, &sEncoder);
}
校准这一步,很多人会忽略。我建议做两件事:
- 零点校准:让电机转到机械零点,把编码器计数值清零。
- 方向校准:正转时计数值增加,反转时减少。如果反了,交换A、B相或者软件取反。
避坑指南:我曾经遇到过编码器读数跳变的问题。查了半天,发现是信号线太长,干扰严重。后来加了RC滤波(100Ω+100nF),问题解决。编码器线一定要用双绞屏蔽线,别省这个钱。
3.4 电机堵转与过流保护
电机堵转,是AMR的常见故障。轮子卡住了、撞到障碍物了,电机还在使劲转,电流飙升,轻则烧MOS管,重则起火。所以过流保护必须做,而且要做两层:硬件保护和软件保护。
硬件保护:用电流采样电阻+比较器,电流超过阈值直接关断PWM。这个响应时间在微秒级,能保命。
软件保护:在控制循环里检测电流,如果连续N个周期电流都超过阈值,就主动停止电机并报错。
// 软件过流检测
#define CURRENT_THRESHOLD 3.0f // 3A
#define OVERCURRENT_COUNT 10 // 连续10次触发
uint8_t overcurrent_cnt = 0;
void Motor_Overcurrent_Check(float current)
{
if(current > CURRENT_THRESHOLD)
{
overcurrent_cnt++;
if(overcurrent_cnt >= OVERCURRENT_COUNT)
{
Motor_Stop(); // 立即停止
Error_Report(MOTOR_OVER_CURRENT);
overcurrent_cnt = 0;
}
}
else
{
overcurrent_cnt = 0; // 正常就清零
}
}
重要提醒:堵转保护不能只靠软件。我见过一个案例,软件跑飞了,保护逻辑失效,电机一直堵转,最后把减速器齿轮打碎了。硬件保护是最后一道防线,必须独立于MCU运行。
另外,堵转检测还有个技巧——别只看电流。有时候电流不大,但电机就是不转(比如机械卡死但阻力不大)。我习惯同时检测编码器速度:如果PWM输出很大,但编码器反馈速度接近0,那基本就是堵转了。
// 堵转检测:结合电流和速度
bool Motor_Stall_Check(float current, float speed, float pwm_output)
{
// 如果PWM大于50%,但速度小于1rpm,且电流偏大
if(pwm_output > 0.5f && fabs(speed) < 1.0f && current > 1.0f)
{
return true; // 堵转
}
return false;
}
嗯,电机调试这块,说到底就是细心加耐心。驱动原理搞明白,PID参数慢慢试,编码器数据校准好,保护逻辑做周全——这四步走下来,你的AMR底盘基本就稳了。我调试过几十台机器人,每次都是按这个流程走,没出过大问题。