近几年做电控的朋友应该都有同感:只要项目里同时出现 CANopen、EtherCAT 和无感 FOC 这三个词,基本就意味着这套东西要进设备、要进产线,而不是在实验台上转两圈就完事。我手上这套基于 SguanFOC 的驱动器,核心就是三件事:用滑模观测器加磁链观测器把无感 FOC 的位置估算做稳,用 CANopen 应付设备内部的节点通信,用 EtherCAT 把实时性拉到一个能跑多轴联动的水平。这篇不聊概念科普,只聊我从拿到工程到跑通、再到挪到自己板子上这一路踩过的东西,包括观测器为什么会抖、磁链观测器在低速段到底差在哪、总线那层该怎么配、代码移植时要动哪几个文件。刚入门无感 FOC 的可以照着走一遍,做过一段时间但一直卡在低速的,应该也能拿到点能直接用的思路。
1. 整体方案拆解与选型逻辑
1.1 为什么要在同一套电控里塞进两种总线
很多人第一反应是"两个总线不是冗余吗",其实在实际设备里这两者的分工非常清楚。CANopen 胜在便宜、线少、抗干扰够用,一个节点一条双绞线就能挂上去,做传感器、按钮盒、小型执行器这一类低速外设通信非常划算;EtherCAT 胜在带宽和同步精度,多轴伺服联动的场合,轴与轴之间的同步抖动要压到微秒级,CANopen 根本做不到。所以一套驱动器同时支持这两种现场总线,本质上是同一块控制板卖给不同档次的设备:单轴、低速、成本敏感的机器用 CANopen;多轴、高动态、要插补的机器用 EtherCAT。SguanFOC 这套代码把通信层做成可裁剪的模块,我认为这是它比较务实的地方,移植时你可以只留一路,编译出来体积也能小不少。
从热词里能看到"canopen cia 讲解""canopen协议详解""ethercat通信协议"这类检索量很高,说明大量的人卡在协议本身而不是控制算法。我的建议是先把 CiA 301 的状态机和 CiA 402 的驱动子协议吃透,再去看具体实现,否则你翻代码只会看到一堆 0x6040、0x6041 的魔数,完全不知道谁在什么时候写进去。
提示:CiA 402 的状态机是设备能不能动起来的第一道门,驱动器上电后必须按顺序收到控制字的特定位翻转,才会从 Switch on disabled 一路走到 Operation enabled,顺序错了就永远不使能。
1.2 SguanFOC 的定位与这套方案的取舍
SguanFOC 属于教学友好型的开源 FOC 工程,代码分层比较清楚,观测器、环路、通信三块基本分开,适合拿来当移植基座。它默认走的是无感方案,也就是没有编码器、没有霍尔,靠电压电流反推转子位置。这个选择带来的好处是省器件、省线束、少一个故障点;代价是低速和零速段的位置估算精度会掉得厉害,这也是为什么它要把滑模观测器和磁链观测器凑到一块用。
我个人的判断是:如果你做的设备最低运行转速不到额定转速的 5%,纯无感基本别抱幻想,老老实实上高频注入或者加个低成本编码器;如果最低工作转速在额定 10% 以上,滑模加磁链这套组合完全够用,而且调好了比很多商用驱动器的表现还稳。选型阶段把这个边界想清楚,后面能省掉大量"为什么我的电机一转低速就抖"的调试时间。
2. 无感 FOC 的观测器内核:滑模与磁链怎么搭
2.1 滑模观测器:原理简单,抖动难缠
滑模观测器的本质思路是拿估算电流和实测电流做差,把这个差值当作滑模面,然后用一个开关函数去强行把差值拽回零。当系统进入滑模面之后,开关函数里的等效分量就恰好等于反电动势,滤波一下就能拿到角度。听起来很美好,问题是那个开关函数是硬切换的,实际电流里会叠加高频抖振,直接拿去算角度,转速波形上全是毛刺。
工程上的常规处理有两个方向。一是把符号函数换成饱和函数或者双曲正切之类的连续函数,用边界层把切换"软化",代价是损失一点抗扰性;二是滑模增益不要一味加大,够用就行。我在 24V 母线、额定 3000 转的电机上实测,滑模增益取到反电动势峰值的 1.5 到 2 倍比较舒服,再大抖振就开始往电流环里灌。
/* 滑模观测器核心片段(简化示意,基于常见实现改写) */ float e_alpha = u_alpha - R * i_alpha - L * di_alpha; /* 反电动势项 */ float s_alpha = i_hat_alpha - i_alpha; /* 滑模面 */ float k_slide = 1.8f; /* 滑模增益 */ float z_alpha = k_slide * sat(s_alpha / BOUNDARY); /* 饱和函数代替符号函数 */ /* z_alpha 经过一阶低通后即为等效反电动势估计值 */上面这段是示意,实际代码里连续域的微分要离散化,我一般直接用后向差分,采样周期就是电流环周期。低通滤波器的截止频率要跟电机最高电频率挂钩,通常取电频率上限的 2 到 4 倍,取太低会带来明显的相位滞后,角度就偏了,转速一高直接失步。
2.2 磁链观测器为什么在低速段难受
磁链观测器的路子完全不同:把定子电压减去电阻压降,然后对时间积分就得到磁链,磁链的幅角就是转子位置。数学上非常干净,中高速段表现也确实好,因为这时候反电动势大,电压和电流的信噪比都高。可一到低速,两个问题同时冒出来。
第一是纯积分器的直流偏置累积。电压采样哪怕只差几十毫伏,积分下去就是一个持续增长的直流分量,磁链圆慢慢偏心,角度跟着漂,最后观测器直接崩掉。做法上几乎没人用纯积分,都是换成一阶低通:用 1/(s+ωc) 代替 1/s,把直流增益压到有限值。代价是引入了幅值衰减和相位滞后,得再乘一个补偿系数把幅相拉回来。
第二是定子电阻的敏感性。低速时反电动势可能只有几伏,电阻上的压降却是同一量级,电阻随温度漂 20%,磁链估算就偏得离谱,角度误差能到十几度。这就是"磁链观测器在低速时存在什么问题"这个问题最常见的答案,解决办法要么在线辨识电阻,要么把低速段交给别的方法去管。
2.3 两种观测器的融合与切换策略
把两者拼起来用,核心就是分段负责:中高速用磁链观测器(或者滑模),低速用另一种更抗噪的手段,中间用权重函数平滑过渡,避免切换瞬间角度跳变导致电机一顿。
我用的权重函数是一个跟转速相关的 S 形曲线,转速低于切换下限时权重完全给低速观测器,高于上限时完全给中高速观测器,中间线性过渡。切换点不能拍脑袋定,一般取额定转速的 8% 到 15%,具体要看你电机的凸极率和电流采样精度。定得太低,低速段依然在抖;定得太高,过渡区正好落在最难受的区间。
注意:切换过程中两个观测器给出的角度必须做连续性处理,常见做法是记录切换瞬间的角度偏差,在过渡区内把这个偏差线性消掉,否则电机会在切换点"咯噔"一下。
2.4 启动阶段:无感绕不开的开环强拖
零速时反电动势为零,任何基于反电动势的观测器都拿不到有效信息,所以无感 FOC 的启动必须靠开环强拖:给定一个电流矢量,让电角度按设定的斜率从零往上爬,电机被"拽"着转起来,等转速和观测器估算值收敛到接近了,再切进闭环。
这套流程里几个参数很关键。强拖电流一般取额定电流的 30% 到 60%,太小拖不动,太大发热还容易过流;爬升斜率要看负载惯量,惯量大的斜率要缓;切入闭环的判据最忌讳只看转速,比较稳的做法是同时判断估算角度和开环角度的偏差小于阈值、估算转速稳定、以及持续满足若干个控制周期。
/* 启动状态机简化示意 */ typedef enum { ST_IDLE, ST_ALIGN, ST_RAMP, ST_SWITCH, ST_CLOSED } run_state_t; /* 对齐:给固定角度通电若干毫秒,把转子拉到已知位置 */ /* 强拖:电角度按斜率递增,电流幅值保持 */ /* 切入判据:角度偏差 < 15° 且转速波动 < 5% 持续 100 个周期 */这个状态机的每一档持续时间都要单独调,我见过太多人写在纸上觉得没问题,一上电机就是"启动反转""启动堵转",回去一查都是强拖斜率或者切入判据太激进。
3. 跑通 SguanFOC 的实操全过程
3.1 最小硬件系统清单
先用最小系统把算法跑通,再谈移植到自己的板子,这个顺序千万别反。需要的硬件不多:
- 一块支持浮点运算的主控板,STM32F4 级别就够了,观测器里的开方和三角函数对算力有一定要求,没有 FPU 会比较吃力
- 一块三相逆变板,MOS 或者 IPM 都行,母线 24V 是最省心的选择,安全且够用
- 一路电流采样,最便宜的是双电阻加运放,运放输出要抬到母线的一半做偏置,这样正负电流都能采
- 一台小功率永磁同步电机,带负载的更真实,空载调出来的参数换到带载工况经常不认人
- 一个能看波形的上位机工具,串口打点也行,但打点会拖慢控制节拍,建议用 DMA 搬一段缓冲区出来再发
我要重点说电流采样。采样时刻必须跟 PWM 对齐,中心对齐模式下一般在下溢或者上溢的时刻触发 ADC,保证采到的是稳定值。更讲究的做法是双电阻采样时采两次、分别在下桥臂全导通的两个窗口,避开开关瞬间。这一步没做对,后面所有观测器调参都是白费,因为你采到的电流本来就是脏的。
3.2 参数整定顺序与实测记录
整定顺序我坚持一条主线:电流环 → 采样校准 → 观测器 → 速度环 → 启动状态机。顺序颠倒过来,你会发现每个环节都在互相甩锅。
电流环我一般用 PI,带宽取开关频率的 1/10 到 1/20。20kHz 的 PWM,电流环带宽做 1kHz 到 2kHz 是比较稳的区间。比例项可以先按 L·ωbw 粗算,积分项按 R/L 时间常数去配,再微调。
示例:L = 0.5 mH, R = 0.3 Ω, 目标带宽 1500 Hz Kp ≈ L * 2π * f_bw = 0.0005 * 2π * 1500 ≈ 4.7 Ki ≈ Kp * R / L = 4.7 * 0.3 / 0.0005 ≈ 2820 (离散前需按 Ts 折算)采样校准必须在电机不通电的状态下做,把测到的电流偏置值记下来存进变量,后面每次转换都减掉这个偏置。我吃过一次亏:偏置没校准干净,磁链观测器跑个十几秒就开始漂,转速读数慢慢往上飘,最后保护停机,排查了大半天才回到这一步。
下面是我在一台 24V/400W 电机上的实测记录,供对照:
| 阶段 | 关键参数 | 实测现象 |
|---|---|---|
| 电流环 | Kp=4.5, Ki=2600 | 阶跃响应约 0.8ms 上升到 90%,无明显超调 |
| 强拖 | 电流 1.5A,斜率 80 rad/s² | 约 0.5s 拉到 300rpm,无堵转 |
| 切入闭环 | 角度差 < 12° | 切换瞬间转速波动约 3%,可接受 |
| 磁链观测器 | 低通截止 30Hz,电阻补偿开启 | 500rpm 以上角度误差约 3° |
| 低速段 | 切换到滑模观测器 | 100rpm 仍能稳定运行,但转矩脉动偏大 |
3.3 波形怎么看才算看懂了
很多人看波形只看转速,这是不够的。至少要同时看四路:转速估算值和实测值(如果有编码器对照)、角度误差、d/q 轴电流、以及观测器输出的反电动势。
转速偶尔跳一下不用慌,那是观测器在收敛;但如果角度误差的均值不为零、而且随负载变化,说明观测器里的电阻或者电感参数不对。d 轴电流出现周期性波动,通常是角度误差引起的,跟电流环无关,别去动 PI。q 轴电流在稳态下应该接近一条直线,如果它跟着转速一起波动,大概率是 PWM 死区补偿没做好,低压小电流工况下死区影响特别明显。
4. CANopen 与 EtherCAT 通信层落地
4.1 CANopen 侧:对象字典和 PDO 才是重点
CANopen 最容易被低估的就是对象字典。你得先想清楚哪些量要周期性传、哪些量只是偶尔配置,前者走 PDO,后者走 SDO。PDO 是"无确认"的广播式传输,靠 SYNC 对象触发,周期到了直接发,不带协议开销;SDO 是带握手和分段传输的,用来读写对象字典里的任意条目,速度慢但可靠。
做驱动器,CiA 402 那几个对象是必看的:控制字 0x6040、状态字 0x6041、运行模式 0x6060、位置实际值 0x6064、速度实际值 0x606C。上位机想让你动,就是往 0x6040 写位组合,你通过 0x6041 回报状态。
提示:PDO 映射一旦定好,就别随便改。设备出厂后上位机是按固定映射解析的,改映射意味着所有上位机配置都要同步更新,现场会非常痛苦。
通信参数上,波特率 500k 或者 1M 是驱动器的常规选择,节点 ID 通过拨码或者存储参数配置。心跳周期我一般设 200ms 到 500ms,太短会增加总线负载,太长故障发现不及时。国产驱动器里汇川那套 CANopen 案例结构非常清楚,值得拿来当参考,看它怎么安排状态迁移和 PDO 收发时机。
4.2 EtherCAT 侧:主站选型和实时性底线
EtherCAT 这边思路完全不同。它用的是"飞读飞写"的处理方式,一帧数据穿过所有从站,从站硬件在帧经过时就把自己的数据塞进去或者取出来,带宽利用率很高。代价是主站必须保证周期严格稳定,所以主站平台的实时性是第一道门槛。
如果主站跑在通用 Linux 上,基本都要打实时补丁,或者用 Xenomai 这类双内核方案。我看到不少人在 RK3568 这种板子上做 EtherCAT 主站,走的是在较新内核上加实时补丁的路子,网卡驱动得支持相应的收发机制,否则周期抖动会大到没法用。这条路的优点是主站省钱、生态现成;缺点是配置琐碎,内核版本、补丁、驱动三者得对上,不然后面调试起来全是玄学。
从站侧的核心是 ESC 芯片,常见的几种方案差别主要在接口和成本上。硬件电路要注意的地方不少:PHY 的走线和晶振、EEPROM 的 SII 信息(决定从站描述)、以及分布式时钟的传播延迟补偿。SII 里的配置写错,主站扫描就会看到一堆莫名其妙的告警。
EtherCAT 从站状态迁移(AL 状态): Init -> PreOP -> SafeOP -> OP PreOP 阶段配置邮箱和对象字典,SafeOP 阶段开始同步但输出保持安全值, OP 阶段才真正放开过程数据输出。干扰观测器(FMMU)和同步管理器(SM)的配置是最容易翻车的地方。SM 负责区分邮箱数据和过程数据的方向和缓冲区,FMMU 负责把逻辑地址映射到从站物理内存。这两个配错,现象就是主站能扫到从站、也能进 OP,但过程数据永远是零或者全是乱值。
4.3 总线与控制环的时序配合
总线周期和控制周期怎么配,是个很多人忽略的点。EtherCAT 主站一般跑 1ms 或者 500μs 的周期,而电流环可能是 50μs 一次。你不能让电流环等着总线数据,正确做法是两层解耦:总线侧只负责把上位机下发的目标值写进共享变量,电流环按自己的节拍读这个变量;反馈量则通过一个缓冲区交给总线任务去取,取的时候做临界区保护。
/* 总线任务与电流环任务之间用双缓冲或原子变量交换数据 */ volatile int32_t g_target_velocity; /* 总线侧写 */ volatile int32_t g_actual_current; /* 电流环侧写 */ /* 读的时候先关中断拿一份快照,避免拿到半更新数据 */CANopen 侧的时序要求松得多,SYNC 周期几百微秒到几毫秒都行,因为 CAN 本身带宽有限。但要注意 PDO 的发送必须和 SYNC 对齐,否则上位机那边会判定失步。
5. 代码移植到自己的板子上
5.1 需要动手改的几类文件
移植的时候别想着全改,按层来。我把要动的东西分成四类:
- 硬件抽象层:GPIO、时钟、定时器、ADC 的初始化和读写,这部分跟原来主控芯片绑得最死,基本要重写
- 采样与调制层:PWM 生成方式、死区配置、电流采样触发点,虽然逻辑一样,但寄存器操作完全不同
- 参数配置层:极对数、额定电流、电阻电感、母线电压、保护阈值,这部分是纯改数值,但最容易改错
- 通信适配层:CAN 外设或者 ESC 接口的驱动,如果原来是 CAN 外设现在换成外部 ESC 芯片,这层要重写
观测器和环路算法那部分我建议一个字都别动,先把新板子跑出跟原板子一样的电流环响应,再谈其他。我有个习惯:移植后第一件事是让 d 轴电流给一个固定值、q 轴给零,电机应该只发热不转。这一招能快速验证相序、极对数、采样方向是不是全对。
5.2 时钟与采样链路的坑
时钟树这条必须单独拎出来说。FOC 对时序极其敏感,如果你把主频配歪了,PWM 频率就跟着歪,电流环的离散参数全都不对,表现出来就是"同样的参数换块板子就炸"。移植后一定要实测 PWM 输出频率,用示波器看,别信代码里的注释。
ADC 采样时机是第二个大坑。中心对齐模式下,ADC 触发一般放在计数器的下溢点,这时候开关噪声最小。如果你的定时器配置成边沿对齐,采样点就变了,电流波形会明显带毛刺。第三个坑是采样通道的顺序和相序的对应关系,U、V、W 三路如果弄反了,反馈到算法里就是负的,电流环会直接发散。
5.3 移植后的验证清单
我给自己定的验证顺序是这样,每次移植都照跑一遍:
| 顺序 | 验证项 | 通过标准 |
|---|---|---|
| 1 | PWM 频率与死区 | 示波器实测与设计值偏差 < 2% |
| 2 | 电流采样偏置 | 断电状态下三路读数接近中点,抖动 < 20mA |
| 3 | 开环旋转 | 给定电角度匀速转,电机平稳无异响 |
| 4 | 电流闭环 | d 轴给定阶跃,响应无超调无振荡 |
| 5 | 观测器空载 | 强拖切入闭环,角度误差 < 15° |
| 6 | 观测器带载 | 加额定负载,转速波动 < 5% |
| 7 | 总线通信 | 上位机能读状态字、能正常使能和停机 |
第 3 步很多人跳过,直接上闭环,结果一出问题就不知道是硬件还是算法。开环能把硬件侧的问题全部筛出来,省下的时间远超那几分钟。
6. 常见问题与排查速查表
6.1 启动类问题
启动反转或者启动堵转几乎是无感 FOC 最常见的问题,原因排下来一般是三个:强拖电流太小带不动负载、爬升斜率太陡导致失步、切入闭环的判据太宽松导致观测器还没收敛就接管。我的处理顺序是先把强拖电流加到额定的一半,看能不能转起来;能转但切不进闭环,就放宽切入的转速条件、收紧角度差条件。
还有一种情况是启动时电机"咯噔"一下然后进入正常,这多半是对齐阶段给的角度和实际转子位置偏差太大,加大对齐电流或者延长对齐时间能缓解。
6.2 运行类问题
低速抖动、莫名啸叫、电流波形发毛,这三类问题占了运行问题的八成。低速抖动先查观测器切换点是不是设得太低,把切换点上调试试;啸叫先看 PWM 频率是不是落在人耳敏感区,20kHz 以上基本听不见,如果还响,可能是死区补偿参数不对;电流发毛优先怀疑采样时机和 ADC 触发点。
我整理了一张速查表,都是实际遇到的:
| 现象 | 优先排查 | 常见根因 |
|---|---|---|
| 低速转矩脉动大 | 观测器切换点、电流环带宽 | 观测器角度误差周期性波动 |
| 高速失步 | 滑模增益、低通截止频率 | 相位滞后过大 |
| 磁链漂移 | 电流偏置、定子电阻参数 | 积分器直流累积 |
| 总线周期抖动 | 主站实时性、网卡驱动 | 未打实时补丁或中断被抢占 |
| 从站进不了 OP | SII 配置、SM 配置 | 过程数据映射错误 |
6.3 几个没人写在文档里的经验
第一条,观测器的参数不要一次调太多,一次只动一个,而且改完要跑够时间看稳定性,很多问题要跑几分钟才暴露。第二条,磁链观测器的电阻参数最好做成可在线修改的,电机热起来之后电阻变化很明显,固定值会慢慢失准。第三条,EtherCAT 主站的周期不要一步到位设到 250μs,先用 1ms 跑稳,再逐步往下压,压不下去就是主站平台的问题,不是从站的问题。第四条,调试阶段把原始数据用 DMA 打到缓冲区再慢慢分析,比在线打点靠谱得多,因为打点本身会破坏时序。
我个人在实际项目里的体会是,无感 FOC 这东西,算法本身没有太多秘密,真正拉开差距的是采样链路干不干净、参数是不是跟着工况在变、以及通信层有没有把实时性这条底线守住。我见过太多把观测器公式抠到极致、结果电流采样偏置都没校准的工程,那种项目调三个月也调不出来。另外这套代码后续还能往一个方向扩:把观测器参数做成随转速和温度自适应调整的,配合总线下发的工作点信息,低速段的表现还能再上一个台阶,这个我还在试,等跑出稳定数据再拿出来聊。