第一次接触机器人底盘开发时,我被各种专业术语搞得晕头转向。经过几个项目的实战,我发现用STM32CubeIDE配合TB6612电机驱动芯片是最适合新手的方案。这个组合就像搭积木一样简单,下面带你一步步搭建开发环境。
硬件准备方面,你需要一块STM32F103核心板(我用的是BluePill)、TB6612电机驱动模块、两个直流减速电机和一块7.4V锂电池。特别提醒:电机驱动模块的VM引脚要接电池正极,VCC接5V,GND共地。我刚开始就犯过错,把VM接到5V导致电机完全没力气。
软件安装有个小技巧:到ST官网下载STM32CubeIDE时,建议勾选"Install STM32CubeMX"选项。这样后续配置会方便很多,就像我最近帮学生调试时发现的,集成环境能避免90%的版本兼容问题。
配置工程时有个关键点:在Clock Configuration界面,一定要把HCLK设置为72MHz。这个数值不是随便填的,它决定了后续PWM输出的精度。记得有次比赛,队友设成了36MHz,结果小车跑起来一卡一卡的,调试到凌晨才发现是这个原因。
差速转向的原理其实很简单:当左右轮速度相同时小车直行,速度不同时就转弯。但在代码实现上,很多新手会忽略电机死区问题。下面分享我优化过的驱动代码:
c复制// 电机控制结构体
typedef struct {
GPIO_TypeDef* IN1_Port;
uint16_t IN1_Pin;
GPIO_TypeDef* IN2_Port;
uint16_t IN2_Pin;
TIM_HandleTypeDef* TIM_Handle;
uint32_t PWM_Channel;
} Motor_TypeDef;
// 带死区的电机控制函数
void Motor_Control(Motor_TypeDef* motor, int16_t speed) {
speed = constrain(speed, -1000, 1000); // 限幅
if(speed > 50) { // 正转死区处理
HAL_GPIO_WritePin(motor->IN1_Port, motor->IN1_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(motor->IN2_Port, motor->IN2_Pin, GPIO_PIN_RESET);
__HAL_TIM_SET_COMPARE(motor->TIM_Handle, motor->PWM_Channel, speed);
}
else if(speed < -50) { // 反转死区处理
HAL_GPIO_WritePin(motor->IN1_Port, motor->IN1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(motor->IN2_Port, motor->IN2_Pin, GPIO_PIN_SET);
__HAL_TIM_SET_COMPARE(motor->TIM_Handle, motor->PWM_Channel, -speed);
}
else { // 停止
HAL_GPIO_WritePin(motor->IN1_Port, motor->IN1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(motor->IN2_Port, motor->IN2_Pin, GPIO_PIN_RESET);
__HAL_TIM_SET_COMPARE(motor->TIM_Handle, motor->PWM_Channel, 0);
}
}
PWM频率设置是另一个容易出错的地方。通过实测,我发现对于大多数直流电机,18kHz是最佳频率。计算公式是:
code复制PWM频率 = 72MHz / (PSC + 1) / (ARR + 1)
设置PSC=3,ARR=999刚好得到18kHz。太高会导致MOS管发热,太低会有可闻噪音。
阿克曼转向机构更接近真实汽车的转向方式。在智能车竞赛中,我们团队通过反复测试总结出这些参数:
舵机控制有三个关键点:
c复制// 优化的舵机角度控制
void Servo_SetAngle(TIM_HandleTypeDef* htim, uint32_t Channel, uint8_t angle) {
uint16_t pulse_width = 50 + angle * 200 / 180; // 0.5ms + angle*1ms/90°
__HAL_TIM_SET_COMPARE(htim, Channel, pulse_width);
}
调试时有个实用技巧:先用手机慢动作视频拍摄舵机转动过程。有次我们发现转向延迟严重,通过视频回放发现是电源功率不足导致的。
在实际项目中,我们需要根据场景切换差速和阿克曼两种转向模式。这是我总结的状态机实现方案:
c复制typedef enum {
MODE_DIFF, // 差速模式
MODE_ACKERMAN, // 阿克曼模式
MODE_STOP // 停止
} DriveMode;
// 运动控制状态机
void Motion_Control(DriveMode mode, int speed, int steer) {
static int last_speed = 0;
switch(mode) {
case MODE_DIFF:
Motor_Control(&left_motor, speed - steer);
Motor_Control(&right_motor, speed + steer);
break;
case MODE_ACKERMAN:
Servo_SetAngle(&htim4, TIM_CHANNEL_3, 90 + steer);
Motor_Control(&left_motor, speed);
Motor_Control(&right_motor, speed);
break;
case MODE_STOP:
Motor_Control(&left_motor, 0);
Motor_Control(&right_motor, 0);
break;
}
// 加速度限制
if(abs(speed - last_speed) > 100) {
speed = last_speed + (speed > last_speed ? 100 : -100);
}
last_speed = speed;
}
在最近的大学生智能车比赛中,我们团队通过加入加速度限制,使小车启动/刹车更加平稳,这个细节让我们比对手快了0.3秒。
调试过程中最让人头疼的是电机干扰问题。分享几个实战经验:
电源问题:电机启动时电压跌落会导致STM32复位。解决方法是在电源输入端加2200μF电容,我习惯用并联方式(如1个1000μF+2个470μF)
PWM抖动:如果发现电机转速不稳,检查:
转向偏差:阿克曼转向需要校准两个参数:
记得有次比赛前夜,小车突然转向失灵。最后发现是舵机连杆螺丝松动导致的,现在我都会用螺纹胶固定关键部位。
基础功能实现后,可以尝试这些进阶功能:
编码器测速:在电机轴安装编码器,通过STM32的输入捕获功能获取转速。建议使用4倍频计数模式提高精度。
PID速度控制:比单纯PWM控制更精准,特别适合爬坡场景。分享一个简易PID实现:
c复制typedef struct {
float Kp, Ki, Kd;
float integral;
float last_error;
} PID_Controller;
float PID_Update(PID_Controller* pid, float error) {
pid->integral += error;
float derivative = error - pid->last_error;
pid->last_error = error;
return pid->Kp * error + pid->Ki * pid->integral + pid->Kd * derivative;
}
在最近做的物流机器人项目中,我们融合了差速和阿克曼转向的优点:低速时用差速实现原地转向,高速时切换阿克曼保证稳定性。这个方案最终使转弯半径减少了40%。