电话/手机:联系客服
支持国产踩到雷了:禾川 X3E 伺服通过 EtherCAT 接汇川 PLC 跑轴控制。 电机是 23 位绝对值编码器的。 在 PLC 里监控它的实际反馈位置(Actual Position),发现一个致命 Bug: 电机单向连续转动时,PLC 读到的位置脉冲数总体是递增的,但是在某些固定的关口(比如累计脉冲到 65536、131072 这些 216 的整倍数时),数值会瞬间向上或者向下‘蹦跳’个几十个脉冲,然后又继续正常递增。 导致我的闭环控制系统每次到了这个地方就会因为‘瞬时速度过大’而发生机械抖动。 这绝对是驱动器往 EtherCAT 总线上传数据时,高低 16 位拼接逻辑写错了,厂商打死不承认,有大佬能提供补丁或者解决办法吗?
优质评论内容
亲,这个先别急着认定是禾川驱动器 EtherCAT 高低 16 位拼接 Bug。
你说跳点固定在:
65536
131072
196608
……
这个特征太明显了:这是 2^16 边界,不是 23 位绝对值编码器的边界。
23 位编码器单圈边界应该是:
2^23 = 8388608 脉冲/圈
所以它每到 65536、131072 这种关口跳一下,更像是PLC 侧、PDO 映射侧、数据类型侧、位置展开算法侧碰到了 16 位边界,而不是编码器本体或驱动器绝对位置原始数据坏了。
CiA402 里常用的 0x6064 Position actual value 本来就是 32 位 DINT 位置实际值,不应该拆成两个 16 位去自己拼;很多 EtherCAT/CiA402 资料也把 0x6064 定义为 32-bit / DINT 的实际位置对象。
一、第一嫌疑:PLC 把 32 位位置拆成两个 WORD 读了
最常见写法是这样:
LowWord := PDO_Word0;
HighWord := PDO_Word1;
ActualPos := HighWord * 65536 + LowWord;
看起来没问题,但如果 LowWord 和 HighWord 不是同一个 EtherCAT 周期的快照,就会出事。
尤其在低 16 位从:
65535 → 0
翻转的瞬间,如果 PLC 读到:
低字已经更新
高字还没更新
或者反过来,就会造成一个很短暂的错误位置。
你说“只蹦几十个脉冲”,不是蹦 65536 个脉冲,也很像这种情况:
PLC 读数周期
EtherCAT PDO 更新周期
速度计算周期
三者不同步
低字翻转附近,位置被采到一个“半新半旧”的组合,速度差分就被放大成尖峰。
正确做法是:
不要拆 WORD
直接把 0x6064 映射成 DINT / LINT
在同一个任务周期里一次性读取
不要在不同程序段、不同任务里分别取高低字
二、第二嫌疑:PDO 映射长度错了,只映射了低 16 位
要检查汇川 PLC 的 EtherCAT 配置里,Actual Position 到底是不是:
Index: 0x6064
Subindex: 0
Length: 32 bit
Type: DINT / INT32
如果配置成了:
WORD
UINT
INT
16 bit
那到 65535 附近一定会出怪事。
有些软件界面会把 PDO 显示成两个 WORD,这不代表你应该在程序里按两个 WORD 用。要看最终变量类型是不是 32 位有符号整型。
三、第三嫌疑:符号扩展或 UINT / DINT 转换错了
位置实际值通常按有符号 32 位处理。
如果 PLC 程序里用了:
UINT
WORD
INT
DWORD
REAL
再转来转去,很容易在 16 位边界出毛刺。
尤其要查这些写法:
WORD_TO_INT
INT_TO_DINT
UINT_TO_DINT
SHL(HighWord, 16)
HighWord * 65536
LowWord + HighWord
安全写法应该是:
ActualPos_DINT : DINT;
然后 EtherCAT PDO 直接绑定到这个 DINT 变量。
速度计算也用:
DeltaPos := ActualPos_DINT - LastActualPos_DINT;
不要先转 INT,也不要用 REAL 做原始位置差分。
四、第四嫌疑:你用的是绝对编码器“单圈/多圈拼接值”,不是 CiA402 0x6064
很多伺服除了 0x6064,还会提供厂家对象,例如:
单圈位置低位
单圈位置高位
多圈圈数
编码器原始值
机械位置
反馈位置
如果你读的是厂家扩展对象,而不是标准 0x6064,那 PLC 可能真的在读两个分开的量:
单圈低 16 位
单圈高 16 位
多圈计数
这类数据如果没有驱动器内部一次性锁存,主站自己拼接时就容易在 16 位进位点跳动。
所以建议先做一个对比:
A:PLC 当前读到的 Actual Position
B:驱动器调试软件里显示的实际位置
C:SDO 直接读 0x6064:0 的值
D:PDO 映射进 PLC 的 0x6064 值
如果 B、C 都平滑,只有 D 或 PLC 程序变量跳,那就是 PLC 映射/程序问题。
五、第五嫌疑:速度计算没有做边界保护
你说“闭环系统因为瞬时速度过大而机械抖动”,这里也要小心。
很多人速度计算是:
Velocity := ActualPos - LastActualPos;
如果 ActualPos 偶发毛刺几十个脉冲,速度就会被放大。
如果再拿这个速度直接进闭环,会把机械打一下。
即使最终查明是映射问题,闭环程序也应该加保护:
如果 DeltaPos > 本周期最大可能位移 × 2
则判为异常采样,不参与速度计算
保持上一周期速度
记录一次 position glitch
这不是掩盖问题,而是运动控制里必须有的输入防毛刺逻辑。
六、怎么快速证明是不是驱动器 Bug?
1. 用 EtherCAT 抓包或主站在线监控看原始 PDO
重点看 0x6064 的 4 个字节。
比如位置在 65536 附近,正常应该是:
65535 = 16#0000FFFF
65536 = 16#00010000
65537 = 16#00010001
EtherCAT 小端显示时,字节顺序会是:
65535:FF FF 00 00
65536:00 00 01 00
65537:01 00 01 00
如果抓包里原始 4 字节是连续的,而 PLC 变量跳,那就是 PLC 映射或程序问题。
如果抓包里 4 字节本身就跳,那才有资格怀疑驱动器固件。
2. 把 0x6064 直接映射成 DINT
临时建一个最简单工程:
只映射:
6041 Statusword
6064 Position actual value
606C Velocity actual value
不做高低字拼接
不做缩放
不做闭环
只记录原始 DINT
让电机慢慢单向转过 65536、131072。
如果这个原始 DINT 不跳,说明原程序错了。
3. 用 SDO 周期读 0x6064 对照 PDO
虽然 SDO 不适合实时控制,但用来诊断很好:
PDO ActualPos
SDO 0x6064 ActualPos
驱动器软件 ActualPos
三者对照。
结果判断:
PDO 跳,SDO 不跳:PDO 映射/主站解析问题
PDO 和 SDO 都跳:驱动器实际对象有问题
只有 PLC 闭环变量跳:程序处理问题
七、现场临时解决办法
如果现在项目急着跑,可以先这样做:
1. 0x6064 直接映射为 DINT,不再拆 WORD
2. EtherCAT 任务和运动控制任务放同一周期
3. 只在 EtherCAT 数据更新后读取位置
4. 速度差分加最大位移限幅
5. 遇到 1 个周期异常跳变,丢弃该周期速度
6. 闭环控制不要直接吃未滤波的 Actual Position 差分速度
例如逻辑上可以这样:
Delta := ActualPos - LastPos;
IF ABS(Delta) > MaxDeltaPerCycle THEN
Velocity := LastVelocity;
GlitchCounter := GlitchCounter + 1;
ELSE
Velocity := Delta / CycleTime;
LastPos := ActualPos;
END_IF;
这个能先防止机械抖动,但根因还是要继续查。
八、我会优先判断的结论
这个案例最像:
第一:
0x6064 的 32 位位置值被 PLC 拆成高低 16 位处理,低 16 位进位时采样不同步。
第二:
EtherCAT PDO 映射长度或 PLC 变量类型错,把 32 位 DINT 当成 WORD/UINT/INT 用。
第三:
程序里做了高低字拼接、符号扩展或 REAL 转换,在 65536 边界出错。
第四:
读的不是标准 0x6064,而是厂家单圈/多圈/低高字扩展对象,主站自己拼接时没有锁存。
第五:
速度差分没有异常采样保护,一个几十脉冲的小毛刺被放大成瞬时速度冲击。
一句话:
23 位编码器不会在 65536 这种 16 位边界天然出问题;65536、131072 固定关口跳动,第一反应应该查 32 位位置值在 PLC 侧有没有被拆 WORD、错类型、错同步。先抓原始 PDO 的 4 个字节,如果 EtherCAT 帧里的 0x6064 连续,厂商驱动器就不是第一嫌疑。