首页 / 资讯中心 / 文章详情

三段式启动不再顿挫:无感FOC/BLDC电流环到速度环无缝切换实战

三段式启动不再顿挫:无感FOC/BLDC电流环到速度环无缝切换实战 ★ FEATURED ARTICLE
干电机驱动的谁还没被启动瞬间那一下“顿挫”劝退过电流直接顶到限幅转速上去了又掉下来等速度环接管后再猛追一波。做无刷电机的朋友应该都见过这个波形启动阶段走的是“定位——电流闭环——速度闭环”这个传统三段式链路每一段单独看都没问题但段与段之间的交接总让人血压升高。我这次要聊的就是怎么把“电流闭环到速度闭环”这个切换做到无缝让启动曲线从头到尾是一条平滑的线而不是一段一段拼接出来的折线。这篇内容适合正在调无感FOC或者带霍尔BLDC驱动的人特别是那些被三段式启动的切换尖峰、转速跌落、失步问题反复折磨的开发者。我会从传统三段式的痛点拆起讲清楚无缝切换的本质是状态变量交接再给出我实测过后沉淀下来的代码框架和调参顺序。文章里所有示例代码都是C语言风格可以直接移植到你的工程里改改用。1. 三段式启动的“顿挫感”到底出在哪个环节1.1 重新认识传统三段式定位、电流环、速度环三步走先把底层的流程摆出来。传统三段式启动第一步是定位给电机通一个固定方向的电流矢量把转子强行拉到一个已知电角度位置。这一步是给后面的开环或者闭环控制提供一个确定的起点不然你连转子在哪都不知道后面的电流和速度控制全是空中楼阁。第二步是电流闭环给定一个逐渐上升的iq参考值让电流环去追这个参考电机在电流产生的电磁转矩下慢慢转起来。第三步才是速度闭环当转速估算达到某个阈值控制权交给速度环速度环根据目标转速去调整电流指令。这套链路在小功率风机、水泵、云台、电动工具里用得特别多逻辑简单工程上也成熟。但问题恰恰出在第三步的“移交控制权”上——它是一次生硬的切换不是平滑过渡。1.2 切换瞬间的“脑袋空白”是顿挫感的根源为什么传统三段式切换时总有顿挫我拆给你看。速度环的PI控制器在切换之前是不参与工作的它的输出初始化为0积分项也没有建立。切过去那一个控制周期速度环突然接管输出直接从一个完全没有“前文记忆”的状态开始。相当于一个车正在跑换了个新司机上来新司机不知道当前油门该踩多少只能从零开始试探。速度环输出从0开始往目标值爬但此时电流环之前给的是一个比较大的启动电流突然被砍下去电机转矩骤降转速就开始掉。转速掉了以后速度环误差变大PI输出开始猛涨把电流又顶上去。这个“掉下去再顶回来”的过程放大到机械系统上就是肉眼可见的抖动放大到电流波形上就是一个明显的V形凹陷或者尖峰。尤其是在低转速区间转子的反电动势很小电流突变对转子位置的扰动会更明显严重的时候直接失步。所以你要理解一件事三段式启动的顿挫从来不是某一个环的参数没调好而是两个环之间的交接逻辑不够丝滑。你调再细腻的速度环参数只要切换那一刻的状态变量是断的顿挫就在所难免。2. 无缝切换的本质让速度环站着把电流环的班接了2.1 先想清楚切换那一下究竟要交接哪几个状态要做无缝切换第一步不是写代码而是盘点“交接清单”。我把切换瞬间涉及的状态变量列一下这些变量如果交接好了切换就是无缝的任何一个断了切换就是顿挫的。一个是电流指令iq_ref。电流环阶段iq_ref是从0斜坡爬到目标上限的这个值在切换瞬间是多少速度环必须心里有数。第二个是速度环PI的积分项。速度环的积分项本质上就是“在当前工况下维持当前转速需要的平均电流指令”这个值在当前时刻应该近似等于电流环的输出值。第三个是电子转子位置和估算转速这两个状态变量其实是连续的只要切换前后都在同一个控制框架里它们不会断但要注意切换瞬间的采样时序是否对齐。第四个是PWM占空比的实际执行值。即使你软件上把iq_ref交接了实际PWM输出还有一阶惯性这个也要考虑。2.2 速度环输出“续写”电流环输出的实现思路清楚了交接清单以后方案就明朗了速度环不应该从0开始而是应该把电流环当前的输出值当作自己的初始输出再从这个基础上继续调节。具体的做法分两步。第一步叫“积分预置”在切换动作发生的前一个周期把速度环PI控制器的积分项直接赋值为电流环当前的输出值。第二步叫“输出续写”速度环的PID输出从预置的积分项对应的值开始计算而不是从零开始。只做这两步切换瞬间的电流指令就不会断转速也不会塌。不过只做预置还不够我建议再加一个短时间的斜坡过渡切换后的若干个毫秒内实际输出不是瞬间从“电流环输出”变成“速度环输出”而是做一个线性插值从电流环输出平滑过渡到速度环输出。这样即使速度环预置积分和实际最优值之间存在一点偏差也会被斜坡吸收掉不会变成突变。下面是核心思路的示例代码我简化了硬件相关部分只保留逻辑/* 速度环PI结构体 */ typedef struct { float kp; float ki; float integral; float output; float max_integral; } speed_pi_t; speed_pi_t g_speed_pi; /* 切换时的积分预置函数 */ void speed_pi_preset(float current_iq_output) { g_speed_pi.integral current_iq_output; g_speed_pi.output current_iq_output; } /* 过渡期线性插值 */ float transition_mix(float current_output, float speed_output, float factor) { return (1.0f - factor) * current_output factor * speed_output; }看到没有核心逻辑就这么几行。你在实际工程里要解决的是“什么时候调用预置函数”和“斜坡因子按什么时间常数走”这两个问题。2.3 状态机与切换条件设计不能凭感觉切无缝切换的第二个关键是切换条件的判断。很多人大手一挥转速到了就切结果在重载、负载突变的工况下翻了车。我建议把启动过程做成一个状态机至少包含定位态、电流闭环态、速度闭环态以及一个故障态。切换的判据不能只有“转速阈值”这一条。我在实际工程里用的是组合判据包括三个条件同时满足估算转速超过设定的切换阈值比如额定转速的10%到15%。电流闭环的iq_ref持续若干毫秒处于限幅值附近说明电机在电流环阶段已经进入稳定的大电流加速区间转速有继续上升的惯性。母线电压没有大幅度跌落同时转速估算有效标志位置位。这三个条件缺一个都不切。为什么如果你在负载还很大、电流环还在爬升的时候就把控制权交给速度环速度环会以为电流爆了积分项猛冲切换瞬间就会过流。如果你在转速估算噪声很大的时候切换速度环会跟着噪声抖启动过程就废了。状态机切到速度态的那一刻要原子地完成三个动作记录切换时间戳、预置速度环积分、把电流环的iq_ref保存为过渡的起始值。这三个动作必须在同一个中断里完成不能分散到多个任务里否则切换瞬间就多了一个中断延迟的变量那个周期输出就断了。3. 实测跑起来以后我踩过的三个典型坑3.1 坑一切换瞬间的电流尖峰——斜坡太短背了锅我最早做无缝切换的时候心想积分预置都做了斜坡过渡应该只要1毫秒就够了吧。结果实测波形出来切换点出现了一个明显的电流尖峰虽然没有失步但噪声很大MOS管温度也偏高。排查下来发现问题出在斜坡时间上。虽然积分预置把速度环的初始输出对齐到了电流环输出但速度环的kp项在切换后第一个周期就会因为转速误差产生一个修正量。如果斜坡只有1毫秒相当于这个修正量几乎是瞬间全部作用到电流指令上等于还是变相突变。后来我把过渡斜坡时间放宽到10到20毫秒尖峰就基本消失了。这个坑告诉我们斜坡的用途不是掩盖积分预置的错误而是吸收速度环kp的初期修正。斜坡时间太短kp修正瞬间生效尖峰照样存在斜坡时间太长启动响应变慢又失去了无缝切换的意义。我最后用的是15毫秒你可以根据自己电机的转动惯量在5到30毫秒之间扫。3.2 坑二切换点附近速度环抖动——先别怪PI参数有段时间我调完切换逻辑发现一个奇怪现象空载启动一切正常带了一点负载后在切换点附近速度环就开始抖转速曲线像锯齿。我的第一反应是速度环参数没调好疯狂调kp、ki结果越调越糟。后来我把切换点转速从额定转速的8%提高到15%抖动立刻消失了。原因不复杂切换点转速太低的区间我的无感速度估算依赖的反电动势信号还很弱速度估算值本身就带噪声。速度环拿一个噪声很大的反馈值来闭环输出自然跟着抖。老话叫“垃圾进垃圾出”速度环参数再好也扛不住拿噪声当反馈。解决思路有两个一是把切换点往中转速区间挪让速度估算质量足够好再切二是在切换后的前50毫秒内对速度环输出做限幅限制它在一个小范围内变化防止它把噪声放大。这两个办法配合用效果最稳。3.3 坑三重负载下一切换就失步——切换条件的判据不够严这个坑最隐蔽。我的测试工况是轻负载起步一切顺利但把负载加上去以后转速刚到切换阈值一切到速度环电机直接失步。查了很久发现是因为电流环阶段的iq_ref根本没有顶到限幅值负载已经开始拖住转速了此时转速只是勉强穿越了切换阈值但电流环给的启动电流并没有建立足够强的转子磁场同步。我之前的切换条件是“转速大于阈值”加上这一条之后才发现逻辑漏洞。后来我把切换条件改成了组合判据不仅转速要到位还必须检测iq_ref在最近一段时间内一直处于限幅饱和区。只有电流环都“尽力了”说明电机是靠电磁转矩真实加速到这个转速的切换给速度环才是安全的。/* 切换条件判断示例 */ bool check_switch_condition(MotorState state, float speed_rpm, float iq_ref, float iq_max) { /* 状态机必须在电流闭环态 */ if (state ! CURRENT_CLOSE) return false; /* 转速超过切换阈值 */ if (speed_rpm SWITCH_SPEED_THRESHOLD_RPM) return false; /* 电流环饱和持续标志必须置位 */ if (!g_current_saturated_flag) return false; /* 母线电压必须正常 */ if (g_bus_voltage BUS_VOLTAGE_MIN) return false; return true; }这条电流饱和判据加进去以后重载启动再也没出现过失步代价是启动时间稍微长了一点因为要等到电流真正饱和后才切。但这一点点时间跟失步烧一次管子相比完全值得。4. 调参顺序和验证方法别拿“看起来没抖”当成功4.1 判断无缝的三个硬指标东西做出来以后怎么验证是不是真的“无缝”我给自己定了三个硬指标你调的时候也可以照这个来。第一个指标是电流指令iq_ref在切换瞬间有没有出现台阶。所谓台阶就是切换前一个周期的iq_ref和切换后一个周期的iq_ref相差超过5%那就是交接出了问题。第二个指标是转速曲线有没有出现先跌后涨的V形。无缝切换的理想转速曲线应该是平滑逼近目标转速中间可以有略微的过冲但不能有明显的凹陷。第三个指标是相电流的最大瞬时尖峰不超过稳态电流的10%到20%同时整个切换过程的母线电压波动小于5%。这三个指标都过了我才会认为一次切换是合格的。如果指标没过别急着怀疑无缝切换这个方法先看看你的交接清单和斜坡时间。4.2 示波器抓哪些关键波形验证的时候示波器至少要同时抓四个信号速度环给出的iq_ref、实际相电流至少一相的采样值、估算转速、母线电压。四个信号时间轴对齐观察点在切换时间戳附近展开。说一下怎么看这组波形。先把速度环输出和电流环输出的交接点放大看有没有瞬间的断裂或者跳变再观察相电流包络是平滑的还是有毛刺然后估算转速在交接后有没有反向凹陷最后看母线电压有没有跌落尖峰如果有说明电流指令突变导致母线电容掏了一笔瞬时能量。我个人是建议直接用逻辑分析仪配合做脚本抓数把切换前后各50毫秒的数据全部记录下来然后离线分析。实测的时候肉眼看波形容易漏掉瞬态毛刺离线数反而可靠。4.3 我建议的调参顺序我踩过这么多坑以后总结了一套比较省时间的调参顺序照着来可以少走弯路。第一步先把切换点转速设到额定转速的15%左右这个位置反电动势信号足够强速度估算噪声小。第二步把过渡斜坡时间设成20毫秒速度环kp设成平时调试值的一半ki也减半先跑通切换流程别追求性能。第三步看波形确认切换瞬间iq_ref没有大跳变如果有检查积分预置有没有生效、预置值取的是不是当前电流环输出。第四步逐步收紧斜坡时间从20毫秒往下压每次压5毫秒观察转速凹陷和电流尖峰。如果某个时间点开始出现尖峰回退上一档。第五步恢复速度环kp、ki到正常值再复测三个硬指标。这套顺序的核心思想是先把切换机制的“无缝性”跑出来再优化响应速度最后才恢复控制的激进程度。顺序反了会很难受——你根本分不清波形上的毛刺是切换机制的问题还是速度环参数的问题。4.4 工况差异比你想的大最后提醒一句千万别只在空载下调试完就说成功。我自己的测试清单里至少包含四类工况空载启动、带额定负载启动、带1.5倍过载启动、热态连续启动。每一类工况都要跑至少50次记录失败的次数和对应的切换参数。为什么强调满载和过载因为在电流环阶段负载越大iq_ref越接近限幅切换瞬间的电流冲击和转速扰动越大。热态连续启动则是为了测MOS管温度升高后采样偏移和母线电压波动会不会让切换条件误判。这些工况出了问题往往比空载调参时的问题更隐蔽也更致命。5. 一个可以抄作业的简化示例代码5.1 代码框架和硬件假设下面给一份简化但不失真的示例代码。假设你的工程里已经有一套正常的FOC电流环dq轴的pid已经调好电流环在20kHz的中断里执行速度环在1kHz的中断里执行。电机编码器或者无感观测器输出的估算转速是连续可读的。这份代码只负责“启动状态机”和“电流闭环到速度闭环的无缝切换”不包含完整的FOC算法。5.2 核心结构体与状态枚举typedef enum { MOTOR_ALIGN, /* 定位态 */ MOTOR_CURRENT_RAMP, /* 电流闭环加速态 */ MOTOR_SPEED_CLOSE, /* 速度闭环态 */ MOTOR_FAULT /* 故障态 */ } motor_state_t; typedef struct { motor_state_t state; uint32_t state_time_ms; float iq_target; /* 电流环启动目标iq */ float iq_current; /* 当前速度环要求的iq */ float switch_speed_rpm; float current_sat_time_ms; uint32_t switch_timestamp_ms; float switch_start_iq; float transition_ms; } motor_fsm_t;调试时我会把switch_speed_rpm、transition_ms、iq_target都做成可在线修改的参数方便直接通过调试口扫参不用每改一次就重新烧录。5.3 切换状态机实现#define CURRENT_LOOP_FREQ_HZ 20000 #define SPEED_LOOP_FREQ_HZ 1000 #define IQ_LIMIT 5.0f #define SWITCH_SPEED_DEFAULT 800.0f #define TRANSITION_TIME_MS 15.0f motor_fsm_t g_motor { .state MOTOR_ALIGN, .switch_speed_rpm SWITCH_SPEED_DEFAULT, .state_time_ms 0, .transition_ms TRANSITION_TIME_MS, .iq_target IQ_LIMIT, }; /* 电流环中断里调用20kHz */ void current_loop_isr(void) { float iq_ref 0; float iq_meas read_iq(); float speed_rpm get_estimated_speed(); uint32_t now_ms get_tick_ms(); switch (g_motor.state) { case MOTOR_ALIGN: /* 定位态给固定角度的电流持续200ms */ iq_ref 2.0f; if (g_motor.state_time_ms 200) { g_motor.state MOTOR_CURRENT_RAMP; g_motor.state_time_ms 0; } break; case MOTOR_CURRENT_RAMP: /* 电流闭环保iq目标斜坡上升 */ g_motor.iq_current 0.1f; if (g_motor.iq_current g_motor.iq_target) g_motor.iq_current g_motor.iq_target; iq_ref g_motor.iq_current; /* 统计电流饱和持续时间 */ if (g_motor.iq_current g_motor.iq_target * 0.95f) { g_motor.current_sat_time_ms; } else { g_motor.current_sat_time_ms 0; } /* 切换判断 */ if (speed_rpm g_motor.switch_speed_rpm g_motor.current_sat_time_ms 20 bus_voltage_ok()) { /* 原子操作预置速度环积分并记录过渡起点 */ speed_pi_preset(g_motor.iq_current); g_motor.switch_timestamp_ms now_ms; g_motor.switch_start_iq g_motor.iq_current; g_motor.state MOTOR_SPEED_CLOSE; g_motor.state_time_ms 0; } break; case MOTOR_SPEED_CLOSE: /* 速度环给出自己的输出 */ float speed_pi_out speed_loop_pi_update(speed_rpm); /* 过渡斜坡15ms内从电流环输出平滑切到速度环输出 */ float factor (float)(now_ms - g_motor.switch_timestamp_ms) / g_motor.transition_ms; if (factor 1.0f) factor 1.0f; iq_ref transition_mix(g_motor.switch_start_iq, speed_pi_out, factor); break; case MOTOR_FAULT: /* 故障处理关闭输出 */ iq_ref 0; break; } /* 限幅保护 */ if (iq_ref IQ_LIMIT) iq_ref IQ_LIMIT; if (iq_ref -IQ_LIMIT) iq_ref -IQ_LIMIT; set_iq_ref(iq_ref); }这段代码的核心就两点电流闭环保里的speed_pi_preset()在切换瞬间做了积分预置速度闭环态里的transition_mix()做了斜坡过渡。其他部分都是状态机的基本骨架你完全可以根据自己的外设接口替换。5.4 说明一下这段代码可以直接改哪些地方代码里的SWITCH_SPEED_DEFAULT、TRANSITION_TIME_MS、IQ_LIMIT是三个最需要按你的电机适配的参数。切换转速我建议从额定转速的10%到15%开始试过渡时间从20毫秒开始往下压。电流环的iq_target这个值如果取太大启动冲击大切换难无缝如果取太小重载启动不起来。我一般是用“额定电流1.5倍左右”起步再根据启动时间调整。电机的转动惯量大启动时间就得长一点iq_target也可以稍微保守一点。还有一件事要特别提醒如果你的项目用的是有感控制切换点转速可以放得更低因为速度反馈质量高不用担心噪声问题如果是无感控制切换点一定不要低于反电动势可观测的临界转速否则你会碰到我前面写的坑二。写在最后的实操体会这套“电流闭环到速度闭环无缝切换”的逻辑我现在已经固化到几个项目的通用启动模块里了收益最明显的是频繁启停的设备——每次启动少一次顿挫机械结构承受的冲击小启停寿命和NVH表现都会上一个台阶。这里面的核心说穿了就是一句话切换不是在两个控制器之间换人而是让后一个控制器站在前一个控制器的肩膀上继续干活。最后再分享一个我自己觉得特别实用的小细节调试时记得把切换时间戳、切换前的iq_ref、切换后的speed_pi_out这几个变量做成一个环形缓冲实时记录最后200组数据。这样每次实测出现异常直接读出切换前后的历史数据问题在哪个环节一眼就能定位比自己盯波形省心太多了。
阅读完成 · 觉得有帮助?
咨询建站