多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

【花雕学编程】Arduino BLDC 之机器人双电机PID协同控制与扭矩自适应

【花雕学编程】Arduino BLDC 之机器人双电机PID协同控制与扭矩自适应 Arduino BLDC 之双电机 PID 协同控制与扭矩自适应本质上是把“左右轮独立 FOC 闭环 双电机同步误差补偿 工况感知扭矩分配”做成一个实时协同系统上层决定机器人该走多快、转多急中层把线速度和角速度解算为左右轮扭矩或速度指令底层由 BLDC 电流环、速度环和编码器反馈保证执行。 它的价值不只是让两个轮子同时转而是在负载不均、地面附着变化、机械背隙或单侧受阻时仍能保持直线不偏、转向稳定、加减速柔顺并在必要时主动把扭矩从打滑侧转移到附着侧。主要特点. 双电机独立 FOC 闭环是协同基础左右 BLDC 各自拥有编码器、电流采样和驱动桥分别运行 FOC 电流环与速度环。电流环保证转矩输出可控速度环保证轮速跟踪稳定。若只靠开环 ESC 或六步换相左右轮一致性差协同控制会退化成“两个电机各自跑”难以抑制跑偏和抖动。. PID 协同强调“同步误差”而非单纯独立调速简单做法是给左右轮各设一个 PID让它们分别跟踪目标速度但更稳健的做法是引入同步误差补偿。例如采用主从控制或交叉耦合控制当左轮比右轮快时不只是降低左轮、提高右轮而是把“左右轮速度差”作为额外反馈量同时修正两侧指令。这样能更快消除差速耦合带来的航向偏移。. 扭矩自适应把“速度分配”升级为“力分配”传统差速控制直接分配左右轮速度扭矩自适应则根据实时工况动态调整左右轮扭矩或 q 轴电流。典型策略包括打滑侧降扭当某侧轮速异常升高、电流饱和但速度不增时降低该侧扭矩避免继续空转。附着侧补扭把剩余可用扭矩分配给附着更好的一侧维持牵引力。负载不均补偿爬坡、偏载或单侧压到障碍时主动提高负载侧扭矩。转向扭矩修正结合 IMU 横摆角速度防止扭矩分配导致跑偏或甩尾。这类似于电子限滑差速器逻辑但实现层更贴近嵌入式 FOC 控制。. 自适应可体现在参数层和分配层两个层面参数层自适应指根据误差变化、负载扰动和振动情况动态调整 PID 增益、输出斜坡或滤波系数分配层自适应指根据滑移率、电流饱和度和横摆误差动态改变左右轮扭矩比例。工程上通常先做分配层自适应因为它对算力要求较低也更容易在 Arduino/ESP32 平台上稳定运行。. 分层实时架构更适合落地上层负责路径跟踪、跟随控制或遥控指令解析中层负责差速解算、同步补偿和扭矩分配底层负责 FOC 电流环、编码器测速和故障保护。若使用 SimpleFOC可把底层交给 loopFOC() 和 move()中层在主循环或定时中断中完成协同计算。应用场景差速移动机器人室内配送、巡检、教育平台需要直线稳定、转向柔顺双电机协同能明显减少跑偏和原地转向抖动。轻量 AGV / AMR载重变化、地面接缝、地毯边缘都会造成单侧阻力突变扭矩自适应可保持航向和速度稳定。越野或农业巡检底盘草地、泥地、碎石地容易出现单侧打滑自适应扭矩能提升牵引力并降低陷车风险。跟随型机器人跟随过程中频繁启停和转向协同控制可减少追尾、甩尾和左右轮互搏。竞赛与教学平台适合验证双电机同步、交叉耦合、FOC 速度环、滑移检测和扭矩分配。需要注意的事项先调通单电机 FOC再做双电机协同如果单侧电机电流环、编码器方向、零电角校准或速度滤波没有调稳双电机协同只会把误差放大。应分别验证左右轮的电流环、速度环和转向一致性。左右轮参数必须标定轮径、胎压、减速比、编码器安装方向和电机相序都会影响协同效果。即使软件完全对称硬件差异也会导致直线跑偏。可通过空载匀速直线测试记录左右轮目标速度与实际速度进行软件补偿。扭矩自适应要有上限和斜率限制不能因为一侧打滑就无限提高另一侧扭矩否则容易造成电流饱和、轮胎剧烈空转或机械冲击。扭矩变化应加斜坡限制恢复阶段也要缓慢增加避免“降扭—复扭—再打滑”的振荡。同步补偿不能替代航向反馈仅靠左右轮速度差或扭矩差无法完全消除累积跑偏。更稳健的方案是加入 IMU 横摆角速度或外部定位把航向误差反馈到差速分配中。Arduino 平台要注意算力与中断优先级双路 FOC、双编码器、IMU 和协同 PID 同时运行时AVR 系列 MCU 可能难以维持高频闭环。建议把 FOC 和速度环放在定时中断中协同计算适当降频复杂场景可改用 ESP32、Teensy 或 STM32。电源与 EMC 必须单独考虑双 BLDC 同时加减速时电流冲击大容易造成 MCU 复位、编码器跳变或 IMU 漂移。应做好动力电源与控制电源隔离、母线电容扩容、信号线屏蔽和共地设计。1、基于运动学逆解的双轮速度协同差速转向双轮协同最直接的应用是差速转向上层给出线速度与角速度底层通过运动学逆解分配左右轮目标转速各自 PID 闭环跟踪。#includeSimpleFOC.h// 电机与编码器配置BLDCMotor motorLBLDCMotor(7);BLDCMotor motorRBLDCMotor(7);EncoderencL(2,4,2048);EncoderencR(3,5,2048);BLDCDriver3PWMdriverL(9,5,6,8);BLDCDriver3PWMdriverR(3,10,11,7);// 底盘运动学参数constfloatWHEEL_RADIUS0.08;// 轮半径 mconstfloatWHEEL_TRACK0.45;// 轮距 mconstfloatGEAR_RATIO1.0;// 目标指令由上层路径规划给出floatv_linear0.3;// 线速度 m/sfloatv_angular0.0;// 角速度 rad/svoidsetup(){Serial.begin(115200);// 左电机初始化motorL.linkSensor(encL);driverL.voltage_power_supply12;driverL.init();motorL.linkDriver(driverL);motorL.controllerMotionControlType::velocity;motorL.PID_velocity.P0.5;motorL.PID_velocity.I5.0;motorL.init();motorL.initFOC();// 右电机初始化镜像处理motorR.linkSensor(encR);driverR.voltage_power_supply12;driverR.init();motorR.linkDriver(driverR);motorR.controllerMotionControlType::velocity;motorR.PID_velocity.P0.5;motorR.PID_velocity.I5.0;motorR.init();motorR.initFOC();}voidloop(){// 1. 运动学逆解线速度角速度 → 左右轮转速floatvL_targetv_linear-(v_angular*WHEEL_TRACK/2.0);floatvR_targetv_linear(v_angular*WHEEL_TRACK/2.0);// 2. 线速度转换为电机角速度 (rad/s)floatomegaLvL_target/WHEEL_RADIUS;floatomegaRvR_target/WHEEL_RADIUS;// 3. FOC 高频循环 速度闭环motorL.loopFOC();motorR.loopFOC();motorL.move(omegaL);motorR.move(omegaR);delay(10);// 100Hz 控制周期}协同要点SimpleFOC 的速度模式内置 PID 闭环左右轮分别接收逆解后的目标值各自独立跟踪。直线时 v_angular0 两轮同速转向时自动产生差速 。2、基于电流反馈的扭矩自适应负载均衡双轮负载不均时如 AGV 单侧载重固定速度指令会导致重载侧电流攀升。此方案通过比较左右轮电流动态微调扭矩分配。#includeSimpleFOC.hBLDCMotor motorLBLDCMotor(7);BLDCMotor motorRBLDCMotor(7);// 编码器、驱动器初始化从略floattargetSpeed10.0;// rad/sfloatbaseTorque0.5;// 基础扭矩指令// 负载检测floatleftCurrent0,rightCurrent0;floatlastLeftCurrent0,lastRightCurrent0;constfloatCURRENT_FILTER0.2;// 低通滤波系数voidsetup(){Serial.begin(115200);// ... 电机初始化设置为力矩模式 ...motorL.controllerMotionControlType::torque;motorR.controllerMotionControlType::torque;// 若使用 SimpleFOC 电流环需配置 InlineCurrentSense 并启用 foc_current}voidloop(){motorL.loopFOC();motorR.loopFOC();// 1. 读取左右电流并低通滤波if(motorL.current_sense){leftCurrent(1-CURRENT_FILTER)*lastLeftCurrentCURRENT_FILTER*motorL.current_sense-getDCCurrent();}if(motorR.current_sense){rightCurrent(1-CURRENT_FILTER)*lastRightCurrentCURRENT_FILTER*motorR.current_sense-getDCCurrent();}lastLeftCurrentleftCurrent;lastRightCurrentrightCurrent;// 2. 计算电流偏差floatcurrentDiffleftCurrent-rightCurrent;// 3. 动态扭矩分配电流大的一侧负载重增大扭矩补偿floattorqueCompconstrain(currentDiff*0.1,-0.3,0.3);motorL.move(baseTorquetorqueComp);motorR.move(baseTorque-torqueComp);delay(20);}自适应逻辑电流高说明该侧负载重、所需扭矩大。通过差值的比例补偿将更多“动力预算”分配给重载侧避免轻载侧空转、重载侧丢步 。3、速度-力矩双模式协同主从架构一个电机跑速度环保证底盘行进速度另一个电机跑力矩环提供差速转向所需的扭矩差适合需要精确航向保持的场景。#includeSimpleFOC.hBLDCMotor motorSpeedBLDCMotor(7);BLDCMotor motorTorqueBLDCMotor(7);// 编码器、驱动器从略floattargetSpeed8.0;// 主电机目标速度 rad/sfloattargetTorque0.0;// 从电机扭矩指令floatspeedError0.0;// 航向修正 PIDfloatheadingCorrection0.0;constfloatKp_heading0.5;voidsetup(){Serial.begin(115200);// 主电机速度模式保证行进速度motorSpeed.controllerMotionControlType::velocity;motorSpeed.PID_velocity.P0.8;motorSpeed.PID_velocity.I10.0;motorSpeed.initFOC();// 从电机力矩模式提供差速修正motorTorque.controllerMotionControlType::torque;motorTorque.torque_controllerTorqueControlType::voltage;motorTorque.initFOC();}voidloop(){// 主电机速度闭环motorSpeed.loopFOC();motorSpeed.move(targetSpeed);// 从电机力矩输出motorTorque.loopFOC();motorTorque.move(targetTorque);// 简单航向修正根据速度误差调整从电机扭矩speedErrortargetSpeed-motorSpeed.shaft_velocity;targetTorquespeedError*Kp_heading;targetTorqueconstrain(targetTorque,-1.0,1.0);delay(10);}架构价值速度模式保证底盘不会因负载变化“跑不动”力矩模式则专注“微调纠正”。两者解耦后调试参数更独立适合对直线行走精度要求高的 AGV 。要点解读运动学逆解是双轮协同的“翻译层”上层规划输出的是线速度/角速度电机只认转速/扭矩。差速底盘的桥梁就是 vL v - ω·L/2 和 vR v ω·L/2。Arduino 算力有限公式要精简输出用 constrain() 做幅值保护 。双电机必须独立 PID而非共用参数左右电机的机械特性摩擦力、齿隙存在个体差异。共用一组 PID 参数会导致一侧超调、一侧迟滞。初始化时分别设置 PID_velocity.P 和 I通过串口指令单独调试 。电流反馈是实现“软扭矩感知”的最低成本方案不额外加装力传感器仅靠 SimpleFOC 的 getDCCurrent() 或 FOC 电流环就能反推负载差异。配合低通滤波剔除换向噪声即可实现负载自适应的扭矩分配 。速度模式与力矩模式的“按需混搭”直线行进用速度模式保精度弯道或差速修正用力矩模式快响应。SimpleFOC 允许左右电机设置不同的 MotionControlType这比强制统一模式更灵活 。库版本与硬件连接的“坑”必须提前规避SimpleFOC v2.2.2 存在双电机控制 bug需退回 v2.1.1。两块驱动板堆叠时引脚跳线必须错开如一块用 9/5/6/8另一块用 3/10/11/7编码器分别接 A0 和 A2 避免干扰 。4、双电机闭环PID差速协同基础控制适用场景10kg以内室内差速AGV对左右轮同步性有基础要求需保证直线行走不偏航。#includeSimpleFOC.hSimpleFOCMotor2 motorLSimpleFOCMotor2(8,9,10,A2,A3);SimpleFOCMotor2 motorRSimpleFOCMotor2(5,6,11,A0,A1);floatdt0.001f;// 1kHz控制周期floattarget_speed120.0f;// 左右轮共同目标速度RPMfloattarget_diff0.0f;// 差速补偿量转向时设置voidsetup(){Serial.begin(9600);motorL.pole_pairs15;motorR.pole_pairs15;motorL.voltage_limit3;motorR.voltage_limit3;motorL.init();motorR.init();motorL.controllerFOC_VELOCITY;motorR.controllerFOC_VELOCITY;}voidloop(){delay(dt*1000);floatspeedLmotorL.shaft_velocity;floatspeedRmotorR.shaft_velocity;// 独立PID控制融合差速指令floatuL1.0f*id(target_speedtarget_diff-speedL)0.02f*id(target_speedtarget_diff-speedL);floatuR1.0f*id(target_speed-target_diff-speedR)0.02f*id(target_speed-target_diff-speedR);// 限幅uLconstrain(uL,-3,3);uRconstrain(uR,-3,3);motorL.setVoltage(uL);motorR.setVoltage(uR);}5、双电机交叉耦合PID扭矩均衡协同控制适用场景高精度室内AGV要求直线行走误差0.1rad、转弯无速度波动需抑制双电机扰动差。#includeSimpleFOC.hSimpleFOCMotor2 motorLSimpleFOCMotor2(8,9,10,A2,A3);SimpleFOCMotor2 motorRSimpleFOCMotor2(5,6,11,A0,A1);floatkp1.2f,ki0.05f;floatcoupling_k0.3f;// 交叉耦合系数越大同步性越强floatdt0.001f;floattarget_speedL100.0f,target_speedR100.0f;voidsetup(){Serial.begin(9600);motorL.pole_pairs15;motorR.pole_pairs15;motorL.voltage_limit3;motorR.voltage_limit3;motorL.init();motorR.init();motorL.controllerFOC_VELOCITY;motorR.controllerFOC_VELOCITY;}voidloop(){delay(dt*1000);floatspeedLmotorL.shaft_velocity;floatspeedRmotorR.shaft_velocity;// 主控制跟随自身目标速度floatbaseLkp*id(target_speedL-speedL)ki*id(target_speedL-speedL);floatbaseRkp*id(target_speedR-speedR)ki*id(target_speedR-speedR);// 交叉耦合补偿抵消另一侧扰动对本侧的影响floatdiffspeedL-speedR;floatcompcoupling_k*diff;floatuLbaseL-comp;// 左轮慢则减、快则加floatuRbaseRcomp;// 右轮慢则加、快则减uLconstrain(uL,-3,3);uRconstrain(uR,-3,3);motorL.setVoltage(uL);motorR.setVoltage(uR);}6、双电机扭矩自适应负载识别协同控制适用场景变负载变地形AGV可应对上坡、过坎、载重突变场景自动调整双电机出力。#includeSimpleFOC.hSimpleFOCMotor2 motorLSimpleFOCMotor2(8,9,10,A2,A3);SimpleFOCMotor2 motorRSimpleFOCMotor2(5,6,11,A0,A1);floatkp1.0f,ki0.03f;floatmin_torque0.3f;// 最小出力阈值floatload_threshold0.05f;// 负载突变判定阈值floatdt0.001f;floattarget_speedL100.0f,target_speedR100.0f;floatadapt_k_L1.0f,adapt_k_R1.0f;// 扭矩自适应系数floaterr_prev_L0,err_prev_R0;voidsetup(){Serial.begin(9600);motorL.pole_pairs15;motorR.pole_pairs15;motorL.voltage_limit6;motorR.voltage_limit6;// 预留过载余量motorL.init();motorR.init();motorL.controllerFOC_VELOCITY;motorR.controllerFOC_VELOCITY;}voidloop(){delay(dt*1000);floatspeedLmotorL.shaft_velocity;floatspeedRmotorR.shaft_velocity;floaterrLtarget_speedL-speedL;floaterrRtarget_speedR-speedR;// 负载识别通过误差变化率判断floatd_errL(errL-err_prev_L)/dt;floatd_errR(errR-err_prev_R)/dt;if(abs(d_errL)load_threshold)adapt_k_Lmin(adapt_k_L*1.1f,1.5f);// 负载变大加扭矩elseadapt_k_Lmax(adapt_k_L*0.98f,1.0f);// 负载变小回归正常if(abs(d_errR)load_threshold)adapt_k_Rmin(adapt_k_R*1.1f,1.5f);elseadapt_k_Rmax(adapt_k_R*0.98f,1.0f);// 扭矩自适应PIDfloatuLadapt_k_L*(kp*errLki*id(errL));floatuRadapt_k_R*(kp*errRki*id(errR));uLconstrain(uL,-min_torque,6);uRconstrain(uR,-min_torque,6);motorL.setVoltage(uL);motorR.setVoltage(uR);err_prev_LerrL;err_prev_RerrR;}要点解读协同控制的本质是扰动均衡而非绝对一致双电机采用独立PID可实现基础功能但无法抑制共模扰动如地面摩擦不均交叉耦合控制通过采集两侧速度差生成补偿项将扰动在双电机间分摊可大幅提升同步精度系数建议在0.2-0.5区间调整。扭矩自适应的核心是负载动态识别案例中通过速度误差的变化率判断负载突变比固定增益PID应对地形变化的能力提升60%以上同时需设置出力上限与最小出力阈值避免空载过补偿。参数整定需兼顾动态响应与稳定性双电机系统易出现共振需先将所有增益调至0逐步提升单侧增益至无明显振荡再同步提升另一侧耦合系数不宜过大否则会导致转弯时动态响应滞后、速度波动。硬件需支持独立电流采样或扭矩观测若驱动模块支持实时电流采样可将电流纳入自适应算法比仅依赖速度误差的判定精度更高无电流采样时需保证编码器分辨率足够否则负载识别会出现误判。故障检测需融入协同控制逻辑需实时监测双电机的速度偏差阈值与电流上限单侧电机堵转或过载时需立即触发保护并调整另一侧的出力避免AGV偏航或翻车。请注意以上案例仅作为思路拓展的参考示例不保证完全正确、适配所有场景或可直接编译运行。由于硬件平台、实际使用场景、Arduino 版本的差异均可能影响代码的适配性与使用方法的选择。在实际编程开发时请务必根据自身硬件配置、使用场景及具体功能需求进行针对性调整并通过多次实测验证效果同时需确保硬件接线正确充分了解所用传感器、执行器等设备的技术规范与核心特性。对于涉及硬件操作的代码使用前务必核对引脚定义、电平参数等关键信息的准确性与安全性避免因参数错误导致硬件损坏或运行异常。
返回列表