1. 项目概述与硬件选型多电机协同控制在机器人底盘驱动和工业自动化领域非常常见比如四轮驱动的AGV小车、机械臂多关节控制等场景。STM32系列微控制器凭借其丰富的外设资源和实时性优势成为这类项目的首选。我最近用STM32F407芯片做了一个四电机协同控制的项目实测下来发现几个关键点CAN总线通信稳定性和PID参数整定直接决定系统性能。硬件配置上需要准备主控芯片STM32F407带双CAN控制器适合多节点通信电机驱动模块大疆C610电调支持CAN协议最高支持10A电流电机M3508直流无刷电机内置编码器可反馈转速和位置通信线路双绞线CAN总线终端需加120Ω匹配电阻这里有个坑要注意STM32的CAN控制器默认引脚是PA11(CAN_RX)和PA12(CAN_TX)但实际布线时建议用带屏蔽层的双绞线我在实验室测试时用普通杜邦线5米外就出现数据丢包。2. CAN总线通信配置2.1 CubeMX基础配置在STM32CubeMX中配置CAN需要三步启用CAN1控制器设置波特率为1MbpsPrescaler3, BS110Tq, BS23Tq开启接收FIFO中断// CAN初始化代码片段 hcan1.Instance CAN1; hcan1.Init.Prescaler 3; hcan1.Init.Mode CAN_MODE_NORMAL; hcan1.Init.SyncJumpWidth CAN_SJW_1TQ; hcan1.Init.TimeSeg1 CAN_BS1_10TQ; hcan1.Init.TimeSeg2 CAN_BS2_3TQ; HAL_CAN_Init(hcan1);2.2 电机ID设置与数据帧解析大疆电调的CAN协议中0x200对应ID1~4号电机0x1FF对应ID5~8。比如要给1号电机发送转速指令帧ID设为0x200数据域前两个字节是电流值-16384~16384对应-20A~20A。接收数据时在回调函数处理void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) { CAN_RxHeaderTypeDef rx_header; uint8_t rx_data[8]; HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, rx_header, rx_data); // 解析电机反馈数据 if(rx_header.StdId 0x201 rx_header.StdId 0x204) { int motor_id rx_header.StdId - 0x200; motor_data[motor_id-1].ecd (rx_data[0]8)|rx_data[1]; // 编码器值 motor_data[motor_id-1].speed_rpm (rx_data[2]8)|rx_data[3]; // 转速 } }3. PID控制算法实现3.1 位置式PID代码实现位置式PID适合速度控制场景代码实现要注意积分限幅typedef struct { float kp, ki, kd; float error_sum; float last_error; float max_output; // 输出限幅 float max_i_term; // 积分限幅 } PID_Controller; float PID_Calculate(PID_Controller* pid, float target, float feedback) { float error target - feedback; pid-error_sum error; // 积分限幅 if(pid-error_sum pid-max_i_term) pid-error_sum pid-max_i_term; else if(pid-error_sum -pid-max_i_term) pid-error_sum -pid-max_i_term; float p_term pid-kp * error; float i_term pid-ki * pid-error_sum; float d_term pid-kd * (error - pid-last_error); pid-last_error error; float output p_term i_term d_term; if(output pid-max_output) output pid-max_output; else if(output -pid-max_output) output -pid-max_output; return output; }3.2 参数整定经验调试M3508电机时建议初始参数速度环Kp2.0, Ki0.05, Kd0先调P再调I位置环Kp50, Ki0.1, Kd1.0实测中发现电机空载和带载时特性差异很大。我的做法是先空载调出基础参数然后加上50%负载后微调Ki值。有个技巧用J-Scope实时监控误差曲线当出现小幅震荡时适当降低Kp并增加Kd。4. 多电机同步策略4.1 主从控制架构在四轮驱动系统中我采用一个主控制器三个从控制器的架构。主控制器通过CAN广播发送目标速度各从控制器根据自身编码器反馈做本地PID调节。同步关键代码// 主控制器发送同步指令 CAN_TxHeaderTypeDef tx_header; tx_header.StdId 0x199; // 同步指令ID tx_header.RTR CAN_RTR_DATA; tx_header.IDE CAN_ID_STD; uint8_t tx_data[8] {target_speed 8, target_speed 0xFF}; HAL_CAN_AddTxMessage(hcan1, tx_header, tx_data, mailbox);4.2 抗干扰处理遇到CAN总线干扰时可以增加CRC校验大疆协议本身带CRC设置超时重发机制我设置的50ms超时在PCB布局时CAN走线远离电源线路5. 系统集成与调试5.1 调试工具链推荐以下工具组合CAN分析仪PCAN-USB或USB-CAN适配器实时监控J-ScopeSEGGER RTT参数调节自制上位机我用PyQtpython-can库5.2 典型问题排查问题1电机响应迟缓检查CAN波特率是否一致用逻辑分析仪测量CAN波形是否畸变问题2电机抖动降低PID的Kp值检查编码器连接是否松动这个项目最终实现了四电机同步误差±5RPM响应时间100ms。关键是要吃透CAN总线时序和PID的相互作用关系建议先用单个电机调试透彻再扩展多电机系统。
实战指南:基于STM32的CAN总线与PID算法实现多电机协同控制
发布时间:2026/6/23 18:42:25
1. 项目概述与硬件选型多电机协同控制在机器人底盘驱动和工业自动化领域非常常见比如四轮驱动的AGV小车、机械臂多关节控制等场景。STM32系列微控制器凭借其丰富的外设资源和实时性优势成为这类项目的首选。我最近用STM32F407芯片做了一个四电机协同控制的项目实测下来发现几个关键点CAN总线通信稳定性和PID参数整定直接决定系统性能。硬件配置上需要准备主控芯片STM32F407带双CAN控制器适合多节点通信电机驱动模块大疆C610电调支持CAN协议最高支持10A电流电机M3508直流无刷电机内置编码器可反馈转速和位置通信线路双绞线CAN总线终端需加120Ω匹配电阻这里有个坑要注意STM32的CAN控制器默认引脚是PA11(CAN_RX)和PA12(CAN_TX)但实际布线时建议用带屏蔽层的双绞线我在实验室测试时用普通杜邦线5米外就出现数据丢包。2. CAN总线通信配置2.1 CubeMX基础配置在STM32CubeMX中配置CAN需要三步启用CAN1控制器设置波特率为1MbpsPrescaler3, BS110Tq, BS23Tq开启接收FIFO中断// CAN初始化代码片段 hcan1.Instance CAN1; hcan1.Init.Prescaler 3; hcan1.Init.Mode CAN_MODE_NORMAL; hcan1.Init.SyncJumpWidth CAN_SJW_1TQ; hcan1.Init.TimeSeg1 CAN_BS1_10TQ; hcan1.Init.TimeSeg2 CAN_BS2_3TQ; HAL_CAN_Init(hcan1);2.2 电机ID设置与数据帧解析大疆电调的CAN协议中0x200对应ID1~4号电机0x1FF对应ID5~8。比如要给1号电机发送转速指令帧ID设为0x200数据域前两个字节是电流值-16384~16384对应-20A~20A。接收数据时在回调函数处理void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) { CAN_RxHeaderTypeDef rx_header; uint8_t rx_data[8]; HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, rx_header, rx_data); // 解析电机反馈数据 if(rx_header.StdId 0x201 rx_header.StdId 0x204) { int motor_id rx_header.StdId - 0x200; motor_data[motor_id-1].ecd (rx_data[0]8)|rx_data[1]; // 编码器值 motor_data[motor_id-1].speed_rpm (rx_data[2]8)|rx_data[3]; // 转速 } }3. PID控制算法实现3.1 位置式PID代码实现位置式PID适合速度控制场景代码实现要注意积分限幅typedef struct { float kp, ki, kd; float error_sum; float last_error; float max_output; // 输出限幅 float max_i_term; // 积分限幅 } PID_Controller; float PID_Calculate(PID_Controller* pid, float target, float feedback) { float error target - feedback; pid-error_sum error; // 积分限幅 if(pid-error_sum pid-max_i_term) pid-error_sum pid-max_i_term; else if(pid-error_sum -pid-max_i_term) pid-error_sum -pid-max_i_term; float p_term pid-kp * error; float i_term pid-ki * pid-error_sum; float d_term pid-kd * (error - pid-last_error); pid-last_error error; float output p_term i_term d_term; if(output pid-max_output) output pid-max_output; else if(output -pid-max_output) output -pid-max_output; return output; }3.2 参数整定经验调试M3508电机时建议初始参数速度环Kp2.0, Ki0.05, Kd0先调P再调I位置环Kp50, Ki0.1, Kd1.0实测中发现电机空载和带载时特性差异很大。我的做法是先空载调出基础参数然后加上50%负载后微调Ki值。有个技巧用J-Scope实时监控误差曲线当出现小幅震荡时适当降低Kp并增加Kd。4. 多电机同步策略4.1 主从控制架构在四轮驱动系统中我采用一个主控制器三个从控制器的架构。主控制器通过CAN广播发送目标速度各从控制器根据自身编码器反馈做本地PID调节。同步关键代码// 主控制器发送同步指令 CAN_TxHeaderTypeDef tx_header; tx_header.StdId 0x199; // 同步指令ID tx_header.RTR CAN_RTR_DATA; tx_header.IDE CAN_ID_STD; uint8_t tx_data[8] {target_speed 8, target_speed 0xFF}; HAL_CAN_AddTxMessage(hcan1, tx_header, tx_data, mailbox);4.2 抗干扰处理遇到CAN总线干扰时可以增加CRC校验大疆协议本身带CRC设置超时重发机制我设置的50ms超时在PCB布局时CAN走线远离电源线路5. 系统集成与调试5.1 调试工具链推荐以下工具组合CAN分析仪PCAN-USB或USB-CAN适配器实时监控J-ScopeSEGGER RTT参数调节自制上位机我用PyQtpython-can库5.2 典型问题排查问题1电机响应迟缓检查CAN波特率是否一致用逻辑分析仪测量CAN波形是否畸变问题2电机抖动降低PID的Kp值检查编码器连接是否松动这个项目最终实现了四电机同步误差±5RPM响应时间100ms。关键是要吃透CAN总线时序和PID的相互作用关系建议先用单个电机调试透彻再扩展多电机系统。