第一次接触RoboMaster电控开发时,面对CAN总线配置和电机控制,我花了整整三天时间才让GM6020电机正常转动。期间经历了引脚配置错误、波特率计算失误、筛选器初始化遗漏等各种问题。本文将用实战经验带你避开这些"坑",从CubeMX配置到完整代码实现,手把手教你驱动大疆C板上的GM6020电机。
在开始编码前,我们需要先了解硬件特性和搭建开发环境。大疆C型开发板(简称C板)是RoboMaster官方推荐的控制器,而GM6020则是常用于云台控制的高性能电机。
必备硬件清单:
软件工具准备:
提示:所有官方文档都可以在RoboMaster官网下载,包括C板用户手册和GM6020说明书。建议开发时始终保持这两个文档处于打开状态。
GM6020的ID设置需要特别注意。电机底部有三位拨码开关(bit0-bit2),采用二进制编码:
code复制ID1: 0 0 0
ID2: 0 0 1
ID3: 0 1 0
...
ID8: 1 1 1
本文示例使用ID1(拨码全关),对应的标准CAN ID为0x205(接收)和0x1FF(发送)。
启动CubeMX后,首先配置系统时钟:
c复制// 验证时钟配置是否正确
SystemCoreClock = 168000000; // 应在main()中输出确认
CAN配置是驱动GM6020最易出错的环节。在Connectivity下启用CAN1后:
引脚重映射(关键步骤!)
波特率计算:
波特率 = APB1时钟 / (Prescaler * (BS1 + BS2 + 1))code复制Prescaler = 7
BS1 = 2 (时间段1)
BS2 = 3 (时间段2)
波特率 = 42MHz / (7*(2+3+1)) = 1MHz
筛选器配置(看似无用但必须):
注意:CubeMX生成的筛选器代码可能不完整,需要在代码中手动补全初始化部分。
建议添加以下配置便于调试:
在CubeMX生成代码基础上,需要添加以下关键功能:
电机数据结构体(bsp_can.h):
c复制typedef struct {
uint16_t can_id; // 电机ID
int16_t set_voltage; // 设定电压(-30000~30000)
uint16_t rotor_angle; // 机械角度(0-8191)
int16_t rotor_speed; // 转速(RPM)
int16_t torque_current; // 转矩电流
uint8_t temp; // 温度
} moto_info_t;
筛选器初始化(bsp_can.c):
c复制void can_filter_init(void) {
CAN_FilterTypeDef can_filter_st;
can_filter_st.FilterActivation = ENABLE;
can_filter_st.FilterMode = CAN_FILTERMODE_IDMASK;
can_filter_st.FilterScale = CAN_FILTERSCALE_32BIT;
can_filter_st.FilterIdHigh = 0x0000;
can_filter_st.FilterIdLow = 0x0000;
can_filter_st.FilterMaskIdHigh = 0x0000;
can_filter_st.FilterMaskIdLow = 0x0000;
can_filter_st.FilterBank = 0;
can_filter_st.FilterFIFOAssignment = CAN_RX_FIFO0;
HAL_CAN_ConfigFilter(&hcan1, &can_filter_st);
HAL_CAN_Start(&hcan1);
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
}
电机控制函数:
c复制void set_GM6020_voltage(CAN_HandleTypeDef* hcan, int16_t v1) {
CAN_TxHeaderTypeDef tx_header;
uint8_t tx_data[8] = {0};
tx_header.StdId = 0x1ff; // ID1发送地址
tx_header.IDE = CAN_ID_STD;
tx_header.RTR = CAN_RTR_DATA;
tx_header.DLC = 8;
tx_data[0] = (v1>>8)&0xff; // 电压高字节
tx_data[1] = v1&0xff; // 电压低字节
HAL_CAN_AddTxMessage(hcan, &tx_header, tx_data, NULL);
}
GM6020需要角度环和速度环协同控制:
PID结构体定义(pid.h):
c复制typedef struct _pid_struct_t {
float kp, ki, kd; // PID参数
float i_max; // 积分限幅
float out_max; // 输出限幅
float ref; // 目标值
float fdb; // 反馈值
float err[2]; // 当前/上次误差
float p_out, i_out, d_out; // 各环节输出
float output; // 总输出
} pid_struct_t;
PID计算函数:
c复制float pid_calc(pid_struct_t *pid, float ref, float fdb) {
pid->ref = ref;
pid->fdb = fdb;
pid->err[1] = pid->err[0];
pid->err[0] = pid->ref - pid->fdb;
// P项
pid->p_out = pid->kp * pid->err[0];
// I项(带限幅)
pid->i_out += pid->ki * pid->err[0];
if(pid->i_out > pid->i_max) pid->i_out = pid->i_max;
else if(pid->i_out < -pid->i_max) pid->i_out = -pid->i_max;
// D项
pid->d_out = pid->kd * (pid->err[0] - pid->err[1]);
// 总输出
pid->output = pid->p_out + pid->i_out + pid->d_out;
if(pid->output > pid->out_max) pid->output = pid->out_max;
else if(pid->output < -pid->out_max) pid->output = -pid->out_max;
return pid->output;
}
在main.c中实现控制循环:
c复制int main(void) {
// 硬件初始化
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_CAN1_Init();
can_filter_init();
gimbal_PID_init(); // 初始化PID参数
float target_angle = 3.14f; // 目标角度(弧度)
while (1) {
// 角度环计算
float current_angle = motor_yaw_info.rotor_angle * (2*3.14159f/8191.0f);
pid_calc(&angle_pid, target_angle, current_angle);
// 速度环计算
pid_calc(&speed_pid, angle_pid.output, motor_yaw_info.rotor_speed);
// 发送控制量
set_GM6020_voltage(&hcan1, (int16_t)speed_pid.output);
HAL_Delay(10); // 10ms控制周期
}
}
当电机无法正常响应时,按照以下步骤排查:
CAN通信检查
软件调试技巧
c复制if(HAL_CAN_AddTxMessage(...) != HAL_OK) {
// 发送失败处理
}
c复制printf("Received ID: 0x%X\n", rx_header.StdId);
PID参数整定建议
典型错误代码对照表:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 电机无反应 | CAN未初始化 | 检查can_filter_init()是否调用 |
| 偶尔收到数据 | 波特率不匹配 | 确认CubeMX配置与计算一致 |
| 发送失败 | 引脚配置错误 | 检查PB8/PB9是否重映射正确 |
| 数据异常 | 筛选器未配置 | 确保筛选器初始化完整 |
实际调试中发现,最容易被忽视的是CubeMX生成的CAN初始化代码可能缺少筛选器配置。即使不需要过滤任何ID,也必须完整初始化筛选器,否则通信无法建立。这也是为什么新手容易在这个环节卡住——因为从逻辑上看这个配置似乎"没用",但却是CAN通信的必要条件。