ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SimpleFOC闭环控制调试:编码器校准与PID整定

SimpleFOC闭环控制调试:编码器校准与PID整定 1. 为什么电机开环转得飞起一上闭环就抽风嘶叫拿到Makerbase SimpleFOC Shield V3.2这块板子第一课基本都是在开环模式下把电机转起来看到三相绕组呼呼转圈感觉FOC这事也就那么回事。但只要你切到闭环模式问题立刻来了电机要么嗡嗡作响原地抖动要么突然疯转根本停不下来严重的时候甚至直接过流保护把驱动芯片干掉。这不是你的板子坏了而是你还没有真正理解SimpleFOC这套库对闭环控制这件事到底要求了什么。开环控制和闭环控制的本质区别在于开环只是给定子绕组施加一组旋转电压矢量电机转不转、转多快全靠磁场牵引系统不关心真实转子位置而闭环则需要实时读取转子角度把目标值和实际值做差通过PID控制器去修正误差让电机朝目标运动。这个读数—比较—修正的回路一旦哪里不对比如角度读数跳变、方向反了、PID增益过大系统就会像喝醉了酒一样失控。这一课我们要做的就是在Shield V3.2平台上把闭环控制真正跑稳。闭环控制分为三个层面电流环、速度环、位置环。SimpleFOC把电流环封装在底层用户一般直接面对的是速度环和位置环而这两个环本质上就是PID控制器在起作用。你会看到PIDController这个类出现在motor.controller相关配置里P是比例增益、I是积分增益、D是微分增益这三个参数就是整个闭环系统手感的关键。很多初学者上来就把PID参数乱填一通比如P填5、I填0.5结果电机抖成筛子。这不怪你PID调参本来就是一个先理解回路特性、再按步骤逼近的过程。好消息是SimpleFOC的设计者已经把大部分底层细节藏了起来你只需要按正确的顺序调好外层两个环就能获得非常顺滑的控制效果。这篇文章我尽量用实际测试的记录来说话用的硬件就是Makerbase SimpleFOC Shield V3.2加一颗常见的小型云台电机磁编码器是AS5600。整个调试过程包括编码器校准、PID参数整定、故障排查三个大部分全部都是我在工作台上反复试过的可以直接套用。2. 上电之前先把这几件事钉死不然PID调得再漂亮也没用很多人调PID失败根子不在PID而在前置条件没做好。闭环控制依赖三大要素正确的转子角度、正确的电机相序、稳定的供电。这三件事任何一件出错PID参数再合理也会失控。2.1 编码器方向与零位校准调参前必须跨过的门槛SimpleFOC的闭环系统里编码器就是电机的眼睛。如果编码器读数方向和电机实际旋转方向不一致速度环就会变成正反馈你想让它往正方向转它偏往反方向跑然后误差越来越大最终满电压输出电机像疯牛一样冲出去撞限位。我的实测经验是第一次接好AS5600后先不要急着上闭环用开环模式把电机驱动起来观察motor.sensor_direction是否被正确识别。具体做法是#include SimpleFOC.h BLDCMotor motor BLDCMotor(7); BLDCDriver3PWM driver BLDCDriver3PWM(PA0, PA1, PA2, PB5); MagneticSensorI2C sensor MagneticSensorI2C(AS5600_I2C); // 或者如果是SPI版本用 MagneticSensorSPI void setup() { driver.init(); sensor.init(); motor.linkDriver(driver); motor.linkSensor(sensor); motor.controller MotionControlType::openloop; motor.target 2; // 开环转速设定 motor.init(); motor.initFOC(); }开环模式下电机转起来后你在串口监视器里打印sensor.getMechanicalAngle()如果这个角度在电机旋转时是单向递增或递减的而不是来回跳说明编码器基本通信正常。接着调用motor.initFOC()它会执行零位校准把当前电角度对齐到编码器的某个位置。校准完成后SimpleFOC会打印Zero electric angle: xxx这样的信息保存这个值。这里有一个非常关键的细节方向错误时校准出的zero angle会是错的但不会报错。所以你需要用开环模式先确认motor.sensor_direction的返回值是Direction::CW还是CCW。如果方向反了不用改接线直接在代码里设置motor.sensor_direction Direction::CW;或CCW强制修正即可。千万不要指望PID能把方向搞反的回路拉回来那是不可能的。2.2 供电纹波与堵转电流PID调参失败的隐形元凶Shield V3.2的电源设计有个特点逻辑供电和功率供电是分开的编码器读取和单片机运行依赖逻辑供电电机旋转依赖功率供电。如果你只接了大功率电源而没给逻辑部分供电单片机要么不工作要么在上电瞬间出现反复重启。功率供电这一路我建议至少用额定电流3A以上的直流稳压电源最好是开关电源加一个470uF以上的电解电容并联在供电端。不要为了省事直接用USB供电去转大电机USB口输出电流普遍只有500mAFOC算法的电流尖峰瞬间就能把USB供电拉垮表现就是电机一顿一顿地转PID控制完全无法稳定你还以为是参数问题。还有一点容易被忽略电调板上电机三相输出到电机之间一定要保证接触良好。我遇到过接线端子松了的情况表现出来就是电机在低速时正常一旦PID给大一点电压就突然卡死或反转查了半天最后发现是接触电阻过大导致电流采样畸变。这个检查很简单用手拨一下线束看串口打印的电流值有没有瞬间跳变。2.3 位置环与速度环的enable时机SimpleFOC里的闭环初始化有个次序问题。motor.initFOC()做的是电角度对齐这需要电机处于一个可被驱动的状态也就是说此时电机有电但它不会转动只是锁死在一个固定位置。如果你对电机施加外力转动它initFOC()可能会失败串口打印FOC init failed。实战中正确的上电顺序是void setup() { // 初始化驱动、传感器、电机... motor.init(); motor.initFOC(); motor.enable(); // 在这之后设置控制模式和目标值 motor.controller MotionControlType::velocity; motor.target 10; // 10 rad/s }很多人喜欢先enable()再initFOC()这会导致电机在校准阶段突然一抖然后失控因为enable之后PWM信号已经建立了而此时电角度还没对齐施加的电压矢量是乱的。记住initFOC永远在enable之前。3. 理解速度环和位置环的关系先从PID的每个字母说起PID调参不是玄学但前提是你得知道你在调哪个环。SimpleFOC中速度环和位置环是级联结构位置环的输出作为速度环的目标值速度环的输出再作为电流环的目标值。这种串联结构的好处是控制逻辑清晰但调参时必须从内往外调也就是先调速度环再调位置环。3.1 P、I、D三个参数在实际电机上的表现先说P比例增益。速度环的P直接决定了系统对速度误差的反应强度。P太小电机转速起不来你设10rad/s它可能只有2rad/s而且有比较大的稳态误差P太大电机在目标速度附近来回过冲带负载时表现就是咔嗒咔嗒的抖动空载时听起来像蚊子叫。I积分增益的作用是消除稳态误差。P只能基于当前误差做比例修正误差小到一定程度后修正量也变小导致永远达不到目标值。I则是对历史误差累积哪怕误差很小累积起来也能把电机推到位。但I过大的下场更惨积分饱和电机在目标点附近出现低频振荡周期性地一冲一刹。D微分增益是预估误差变化趋势的。速度环里我一般不调D因为速度信号本身已经有高频噪声微分会把噪声放大效果反而更差。位置环里D可以理解为阻尼能有效抑制超调但调大了会让电机在目标点前肉得很手感迟钝。这里我用一个简单的表格总结一下参数设置过小设置过大手摸电机感受P响应慢、追不上目标抖动、啸叫、啸声尖锐能拧动但发虚 / 麻手、发热明显I稳态误差无法消除低频振荡、过冲来回总是差一点 / 周期性一冲一刹D超调抑制不足噪声放大、电机发烫冲过头 / 原地高频细震3.2 为什么建议先调速度环再调位置环级联结构决定了外层环的每一次调节都会经由内层环去执行。如果内层速度环本身不稳位置环再怎么调也无济于事。我见过很多新手一上来就把位置环的P填到20电机动一下就冲过头然后振荡好几圈才停下来他们以为是位置环参数问题其实是速度环P太小位置环的输出已经饱和速度环却执行不到位。正确的顺序是先把速度环调稳确保给定一个目标速度后电机能在1秒内到达且无明显超调和抖动。再叠加位置环从很小的P开始加大到出现轻微超调往回退一点得到临界值。最后微调速度环的I弥补位置环静差。这样一层层往上搭就像盖房子先打地基后面才能稳。3.3 手动调参前的硬件准备调参前建议把电机锁在一个支架上不要悬空拿着因为你调P的时候电机会突然发力悬空持握非常危险。另外准备一个可以实时显示转速的窗口SimpleFOC自带的串口监视器就能做到通过motor.monitor_variables设置需要打印的变量。还有一点强烈建议在电机和负载之间预留一个机械缓冲环节比如弹性联轴器。硬连接下如果PID参数激进的瞬间冲击力过大轻则打齿重则连轴器直接断裂。我的工作台上现在还有一对被调参过程振裂的联轴器别省这个钱。4. 实战PID调参步骤一套可以完整照做的流程下面这部分是核心我会一步步展示我在Shield V3.2上从零把速度环调稳再做位置环的完整过程包含代码、串口打印数据和调参判断逻辑。4.1 速度环开整从P0.1开始试探先把控制模式设为速度环PID参数初始化如下motor.controller MotionControlType::velocity; motor.PID_velocity.P 0.1; motor.PID_velocity.I 0.0; motor.PID_velocity.D 0.0; motor.PID_velocity.limit 12; // 输出电压限幅 motor.PID_velocity.ramp 10000; // 目标值斜坡速度单位rad/s^2 motor.target 5; // 目标速度5 rad/s重点是limit这个参数它限制了PID输出电压的最大绝对值。12代表最大12V占空比输出如果你供电是24V这个值就是最大50%占空比。先设小一点很有好处即使参数爆了电机也不会满压冲出去给你一个反应的时间。我通常用串口Plotter或SimpleFOCStudio观察实时速度曲线。如果你用的是Arduino IDE串口绘图器可以这样配置monitormotor.useMonitoring(Serial); motor.monitor_variables _MON_TARGET_VELOCITY | _MON_VELOCITY | _MON_VOLTAGE_Q; motor.monitor_downsample 50; // 50ms打印一次给目标速度5rad/s后观察实际速度曲线。这时大概率会出现两种情况实际速度缓慢上升但离5还有明显差距说明P太小加大P每次增加0.05左右。实际速度在目标附近来回震荡震荡频率较高说明P偏大减小P每次减小0.02。我的经验是大部分小型云台电机配合AS5600在12V供电下速度环P在0.2~0.8之间能获得不错的响应。如果你的电机出现了尖锐的啸叫不要犹豫立刻把P降下来那说明电压矢量切换频率已经进入听觉范围而且电流尖峰很大。4.2 速度环加I前先把P的临界点找出来这里有一个很重要的小技巧在没有加I之前你会发现电机在稳定后始终和设定速度差几rad/s这是正常的。你可能会忍不住赶紧加I但我建议你先故意把P加大到让系统出现持续临界振荡也就是电机在目标速度附近以非常规律的频率轻微来回抖记录下这个P值这个值就是当前系统的临界增益然后把它除以2到3得到一个相对安全的P值。这么做是为了让你心里有数知道系统稳定的边界在哪里。之后再加I。I的起点我推荐0.01然后逐渐加大。加I之后观察两种情况一、稳态误差是否被消除二、是否存在低频振荡。如果你看到速度曲线在目标值附近以0.5Hz左右的频率来回波动这个基本就是I太大了往回退一半。按照我的实测记录某次调参过程的数据大致如下P值I值现象0.10稳态速度4.2差0.8响应慢0.30稳态速度4.8差0.2略有超调0.50临界振荡速度在4.5~5.5之间抖动0.250.01稳态5.0无振荡响应约300ms0.250.05稳态5.0出现低频振荡约0.8Hz0.250.02稳态5.0无振荡响应约200ms从这个表格可以看出P0.25、I0.02是一个比较理想的组合。实际速度环响应大约200ms到达目标过程中有一点轻微超调但不影响使用。如果你追求更快的响应可以维持P不变适当提高I到0.03同时观察有没有振荡。4.3 位置环基于已经调好的速度环往上搭速度环稳了之后位置环就有个好底子。位置环的目标值单位是弧度rad我把位置环参数初始化为motor.controller MotionControlType::angle; motor.PID_angle.P 6.0; motor.PID_angle.I 0.0; motor.PID_angle.D 0.0; motor.PID_angle.limit 30; // 最大输出速度对应速度环的目标值 motor.target 3.14; // 转到3.14rad注意位置环的limit含义和速度环不同这里是输出给速度环的目标速度限幅单位是rad/s。如果你设的太大比如50一旦位置误差大电机就会全速冲向目标冲击力很大。我一般从10开始不够再加。位置环P的调试逻辑和速度环类似从较小的值开始看电机转到目标位置后的表现。P太小电机慢慢悠悠转到目标附近但误差一直停在0.1rad左右不动这是速度环的静差传导过来的P太大电机会冲过目标然后来回振荡两三次才停下。位置环有一个非常好的指标叫做稳态位置误差。调好之后用手去拧电机轴它会有一个抵抗力把人手推开松开后自动回正。实测我的配置在P12、I0.1、D0.2时稳态误差小于0.02rad大约1度左右手拧回正速度也很快。位置环加D确实能显著改善回正时的阻尼感但要注意D的参数范围很小超过0.5就容易出现高频颤抖。4.4 用 SimpleFOCStudio 加速调参如果你不想一遍遍改代码烧录强烈建议用SimpleFOCStudio这个上位机工具。它的原理是通过串口实时修改变量不需要重新烧录固件调参效率提升五倍以上。在Arduino代码里加上motor.useMonitoring(Serial); motor.monitor_downsample 50; CommandParser parser CommandParser(Serial); parser.registerVariable(P, motor.PID_velocity.P); parser.registerVariable(I, motor.PID_velocity.I); parser.registerVariable(D, motor.PID_velocity.D);然后用Studio连接对应串口你就可以在电脑上拖滑块调P、I、D实时看波形反馈。这个工具在微软商店和Linux上都有Windows下需要确保串口没被其他程序占用。5. 实测中踩过的坑那些不试一次绝对不知道的教训调参过程中我踩了不少坑论坛和文档上很多都没写透。我把这些教训整理出来希望你能少走弯路。5.1 速度环啸叫而位置环正常问题很可能出在限幅有一次我调一个负载惯性比较大的电机速度环P从0.3加到0.4一切正常加到0.45后电机突然开始尖叫。我以为是P过头了降回0.4还是叫就很奇怪。排查到最后发现速度环的limit设的是1515V我供电是12V也就是说PID输出限幅超过了实际母线电压控制器在极限处出现了持续的饱和切换音调就是6kHz左右的开关啸叫。把limit改成11V之后同样的P值一点声音都没有。这个问题的本质是PID输出达到限幅后会有一段持续的饱和期占空比长时间打满开关频率和电机电感产生谐振机械结构把这个谐振放大成声波。解决办法除了降低P之外更关键是检查limit是否匹配母线电压。5.2 编码器方向反了位置环会变成自动归位失败编码器方向反了的表现很有意思速度环可能看起来还算正常因为速度是微分信号方向反了只影响正负号你设正目标它可能转反方向但位置环就完全乱套电机不会停在目标位置而会越转越过然后反转再越过再反转振幅越来越大。这是因为位置环的误差符号恒定反向了P乘以一个反号的误差输出会不断增加系统正反馈。如果你遇到位置环振荡幅度越来越大第一件事不是调参而是回头检查编码器方向sensor.init(); // 手动旋转电机轴打印角度变化确认方向 Serial.println(sensor.getMechanicalAngle());旋转方向和实际角度增加方向一致才能继续。方向反了就在代码里设motor.sensor_direction Direction::CCW;修正或者在传感器初始化前把方向反转。5.3 I参数不要在上电瞬间就设太大否则位置环必过冲位置环加I的时候我建议设置一个启动时的保护逻辑。因为位置环启动瞬间电机往往不在目标位置位置误差很大。I在这时会快速累积一个巨大的积分值即使后来电机到了目标点这个积分残留还会继续推动电机冲过头。等积分慢慢释放电机又回落于是来回摆。SimpleFOC的PIDController类内部没有积分限幅这个参数所以我们需要在逻辑层面做限制。我的做法是if (abs(motor.shaft_angle - motor.target) 0.5) { motor.PID_angle.I 0.0; // 误差太大时关闭积分 } else { motor.PID_angle.I 0.05; // 接近目标时才启用积分 }这个逻辑放在loop循环里实测能够显著降低启动过冲。当然为了省事也可以用PID_angle.limit来间接限制积分输出上限但效果不如上面这种直接。5.4 温度漂移不解决PID调得再好也是白搭这个坑主要针对磁编码器AS5600。AS5600在通电一段时间后会因为芯片自热产生角度漂移最大能有3~5度的变化。别小看这3度在位置环里就是0.05rad的误差如果你的PID参数调到了很高的稳态精度就会发现控制精度随时间推移慢慢变差。解决办法有两种一是每次上电后等待传感器温度稳定再做initFOC零位校准通常等三分钟二是用高精度编码器替换比如AS5047P这类磁编码器或者高分辨率光电编码器。对于演示项目等三分钟就够了但如果你要做长时间运行的设备一定要考虑漂移问题。5.5 电机堵转时立刻烧驱动那是限幅保护没做好堵转是闭环控制系统里最危险的情况。电机堵转时速度环误差极大PID输出会瞬间打到限幅最大值大电流持续通过绕组和驱动芯片。Shield V3.2使用的驱动芯片有一定的过流能力但长时间堵转一定烧。SimpleFOC没有内置堵转保护需要你自己加。我的做法是通过串口监视电机电流如果电流连续500ms超过阈值就立刻motor.disable()。这个功能用SimpleFOC的底层电流检测接口就可以实现。如果不想写额外代码也可以从硬件上串一个大电流保险丝或热敏电阻。6. 一套可以直接抄作业的完整配置与最后的几点体会下面是经过多次试验后我在Shield V3.2上稳定运行的一套完整配置电机为2204云台电机12V供电AS5600配径向磁铁#include SimpleFOC.h BLDCMotor motor BLDCMotor(7); BLDCDriver3PWM driver BLDCDriver3PWM(PA0, PA1, PA2, PB5); MagneticSensorI2C sensor MagneticSensorI2C(AS5600_I2C); void setup() { Serial.begin(115200); driver.voltage_power_supply 12; driver.init(); sensor.init(); motor.linkDriver(driver); motor.linkSensor(sensor); motor.controller MotionControlType::angle; motor.PID_velocity.P 0.25; motor.PID_velocity.I 0.02; motor.PID_velocity.D 0.0; motor.PID_velocity.limit 11; motor.PID_angle.P 12.0; motor.PID_angle.I 0.05; motor.PID_angle.D 0.2; motor.PID_angle.limit 20; motor.target 0; motor.init(); motor.initFOC(); motor.enable(); motor.useMonitoring(Serial); motor.monitor_variables _MON_TARGET_ANGLE | _MON_ANGLE | _MON_VELOCITY; motor.monitor_downsample 20; } void loop() { motor.loopFOC(); motor.move(); motor.monitor(); }这个配置下位置环稳态误差小于0.02rad从0转到90度大约0.4秒基本无超调手拧回正有力且不发散。关于这套系统后续还能怎么玩我个人经验里最值得扩展的方向有两个一是把电机的运动轨迹做成梯形加减速规划而不是直接阶跃给目标值这样能大幅减小机械冲击也可以在motor.move之前对target做插值二是把速度环的数据通过外接串口发到上位机做实时记录分析PID响应曲线这样调参就有曲线依据不再是纯手感。最后说一点个人体会调PID这件事在这个板子上其实没有你想象的那么玄学只要你把编码器校准做扎实、按照速度环到位置环的顺序、每次只动一个参数绝大多数电机都能在半小时内调稳。如果调不稳九成原因是前置条件没做好而不是PID的参数你不会填。上手Makerbase SimpleFOC Shield V3.2的第二课能把这些闭环控制的概念和调试方法论建立起来后面再去玩更多高级玩法就有底气了。
返回列表