EtherCAT 周期速度 × 故障率 × CPU 占用 调研
0.5ms实测可用上限(2kHz,带 CAN 工况)
≈9.0ppCAN 每拍恒定负担(3 档配对,饱和档除外)
4000 帧/s电机 STATUS 实测刷新(1/0.5ms)
≈8.2kHz纯 ECAT 实测硬上限(设 10/12.5/16kHz 实测均不涨)
结论
- 带 CAN 工况的可用上限在 0.5ms,瓶颈在从站 GD32 不在主站。实测可用到 0.5ms;0.25ms 起电机数据刷新率从 4000 帧/s 掉到 88 帧/s(带 CAN 那轮当场确认
online=True,是真失效)。可用上限只夹在 (0.25ms, 0.5ms] 内,具体位置没测到。 - 纯 ECAT 硬上限 ≈8.2kHz,这次测到了墙本身。断电重启后无 CAN 补测:0.25/0.125ms 满速通过(99.7% / 98.0%);再往下设 0.1 / 0.08 / 0.0625ms,实测循环率全部顶在 8196–8238Hz(达成 82.3% / 65.9% / 51.2%),wkc 全程 3/3、从站始终应答。墙的来历:主站单拍硬耗时
cyc p50各档恒 117–126µs,1/121µs ≈ 8.2kHz —— 是"每拍必须花的往返+处理时间",不是主机 CPU 不够(顶墙时主机 CPU 反而降到 ~85%)。原「3.5kHz 外推」被证伪;首轮无 CAN 0.25ms 停报为残状态偶发,不复现。注意"跑得快"≠"能用":带 CAN 的可用上限仍由电机数据刷新决定(下一条)。 - 拔 CAN 对照解开了混淆:未饱和三档 Δ 稳定 8.7–9.3pp,不随频率张开 → 快档塌陷不是 CAN 挤出来的;饱和两档 Δ 收窄到 ≤0.5pp,正是读数被夹紧的特征。
- 主机 native CPU 恒约 100% 单核、与周期无关(自旋恒转,
/api/motor/spin已是空操作)。唯一的例外是带 CAN 轮的 0.125ms 档降到 87.5% —— 因为从站已停答。无 CAN 补测里从站活着,同档就回到 99.7%。主机 CPU 掉下来那一刻,正是从站死掉那一刻。 - 建议:要吃到余量就停在 0.5ms。0.25ms 不是"更快",是功能损坏。
一、曲线
① 循环率:设定 vs 实测(主站侧)
② 从站每拍负担:带 CAN vs 无 CAN
③ CAN 流量分层:STATUS 刷新与 CONFIG 事件流
④ 主站错拍率
名词解释(表里这几个计数器到底是什么)
- WKC(working counter,工作计数器):EtherCAT 报文自带一个计数器,每有一个从站硬件处理了该报文就 +1。主站知道自己发出了几个报文,这个应得值就是
expected_wkc(本台子 1 个从站、每拍 3 个报文 → 期望 3)。所以wkc 3/3= 从站每拍都完整处理了;wkc -1= 整帧没回来(从站停答/掉 OP);wr(watchdog reset)0→1= 看门狗把总线重拉过一次。 - 主站错拍 skip:主站按周期给自己排了一个"节拍网格"(
ecat_native.c:776-791)。每拍做完 EtherCAT 收发后,若发现已经错过了下一个节拍点(甚至错过多个),就把错过的个数累进skip_total。它量的是"主机软件迟到",不是从站故障 —— 1ms 档 1.1 次/s 只是偶发 OS 调度抖动;0.125ms 档 5107 次/s 说明单拍耗时已超过两个周期,主机也在勉强。它不区分"从站慢"和"主机慢"。 - 在线:电机槽位的
online标志(槽位数据在超时窗内持续刷新才为 true)。带 CAN 轮显示"未记录"是因为当时脚本还没有这列,不是电机掉线;该轮的电机在线状况另有 8s/档定点实测佐证。
二、数据全表
带 CAN(主结论轮)
| 周期 | 实测Hz | 达成率 | 从站load | CAN接收/s | skip/s | wkc | 在线 |
|---|---|---|---|---|---|---|---|
| 4ms | 249 | 99.7% | 20.2% | 3,987 | 0.02 | 3/3 | 未记录* |
| 2ms | 499 | 99.7% | 28.3% | 3,990 | 0.08 | 3/3 | 未记录* |
| 1ms | 998 | 99.8% | 44.5% | 3,992 | 1.10 | 3/3 | 未记录* |
| 0.5ms | 1,999 | 100.0% | 76.7% | 3,995 | 11.86 | 3/3 | 未记录* |
| 0.25ms | 3,989 | 99.7% | 99.7% | 159 | 98.61 | 3/3 | 未记录* |
| 0.125ms | 7,780 | 97.2% | 99.3% | 190 | 5,106.62 | 3/3 | 未记录* |
拔掉全部 CAN 设备(补测轮)
| 周期 | 实测Hz | 达成率 | 从站load | CAN接收/s | skip/s | wkc | 在线 |
|---|---|---|---|---|---|---|---|
| 2ms | 499 | 99.7% | 19.0% | 0 | 0.08 | 3/3 | 0.00 |
| 1ms | 998 | 99.8% | 35.4% | 0 | 0.58 | 3/3 | 0.00 |
| 0.5ms | 2,000 | 100.0% | 68.0% | 0 | 17.08 | 3/3 | 0.00 |
| 0.25ms | 3,988 | 99.7% | 99.4% | 0 | 39.72 | 3/3 | 0.00 |
| 0.125ms | 7,839 | 98.0% | 98.8% | 0 | 4,672.07 | 3/3 | 0.00 |
| 0.0625ms | 8,196 | 51.2% | 99.0% | 0 | 15,720.46 | 3/3 | 0.00 |
| 0.1ms | 8,232 | 82.3% | 98.9% | 0 | 10,039.19 | 3/3 | 0.00 |
| 0.08ms | 8,238 | 65.9% | 98.9% | 0 | 12,282.87 | 3/3 | 0.00 |
电机数据刷新率(带 CAN 轮,8s/档定点实测)
| 周期 | 刷新率 帧/s | 判定 |
|---|---|---|
| 1ms | 4,000.2 | 正常 |
| 0.5ms | 4,000.2 | 正常 |
| 0.25ms | 87.9 | 塌陷 |
| 0.125ms | 147.5 | 塌陷 |
三、机理与上限推算
上限已实测:≈8.2kHz,墙是单拍 ≈121µs 的硬耗时
证据链:无 CAN 设定 0.25 → 0.0625ms 五档,实测循环率在 ≤0.125ms (8kHz)前跟随设定,再快就被钉死在 8196–8238Hz;与此同时主站单拍耗时 cyc p50 在所有档位(含 2ms 慢档)恒为 117–126µs,1/121µs ≈ 8.2kHz,与顶墙实测严丝合缝。即:每拍有一段不可压缩的「发送→从站处理→回读+主站处理」往返时间,周期设定小于它时就堵在这里。
排除项:不是主机 CPU 不够 —— 顶墙档主机 CPU 只有 ~85%(自旋等待占比变小);不是从站拒答 —— wkc 全程 3/3。load_perm 在 ≥4kHz 全饱和 99.x%,既定位不了拐点也喂不准模型,早先「两项模型 load=F/T+R → 2.7/3.5kHz」的外推已被满速补测证伪,仅留档。
未闭合的一处:121µs 里主站协议栈与从站 ESC/固件各占多少,本轮数据分不开 —— 需给固件 fillRxWindow 加直接计时(固件改动,需批准)或抓总线帧时序才能归因。
四、判据口径与踩过的坑
仪表陷阱(每条都实际踩过)
- 【本次真正翻车处】把两个同速计数器相除当成比率 ——
drop/rx_count都约 4000/s,相除恒得 100%,看着像"全丢",其实只是同速量之比。判丢帧必须有独立的分子与分母:本次真正的独立证据是「STATUS 实测 4001 帧/s 正常刷新」+「CONFIG 事件流去重后仅约 92 帧/s」,两者一起否掉了 100% 丢帧。任何"A/B 比率"指标,先问 A 与 B 是否独立计数。 - 从站停报后
load_perm保留残值 —— 实测age_ms=214338时仍报 99.2%。任何取 load_perm 的脚本必须先过age_ms闸。扫描脚本已加该闸并新增gd32_load_samples/gd32_load_stale两列。 load_perm不是整机 CPU 占用率 —— 固件sys/cpu_load.h定义收窄为"中断+非延迟处理段",排除 SSC MainLoop 轮询;当前 free-run(dc=0)下 PDO 应用走主循环轮询路径、不计入。所以不能把"load→100%"读成"CPU 耗尽"。OVF每拍被s_rxWinOvf=0清零 —— GD32 侧没有可用的累计丢帧计数。读它恒 0 不能当"无丢帧"证据(我据此误判bridge.drop是死字段,结论已收回)。/api/can/cadence只存最近 512 拍 —— 0.25ms 档 =128ms、0.125ms=64ms,环没装满时取到的count/lost是行数不是每拍帧数。对应 CSV 列已从结论排除。- u16 回绕:
drop在 4000/s 下 16 秒绕一圈 → 10Hz 逐次累加 + 取模,单次增量>32768 记为作废。本次作废点数=0。 - 「seq 值域 − distinct」不是丢帧率:报 99.686%,方向与 drop≈λ 一致但分母含 STATUS 全流量,口径不干净 → 只作旁证。
- 周期可被 localStorage 劫持(
transport-ecat.js:227-228)→ 每档下发后回读校验。本次六档hijacked=no、native PID 全程 29284 未变。 cyc_us.late/lost恒 0(遗留占位,未接线),未使用。
五、可信度边界
这份数据不能证明什么
- 板载固件版本无法确认:烧写台账
201832b5a8e258d6…与在盘 .hex 展平哈希(63d6ab9b…、e6a6e75f…)均不匹配,且产物时间戳晚于台账。→ 本报告不复用任何历史固件结论。 - 主站在测量期间又重编过(源码 02:39 / exe 02:40,PID 起于 02:32),无法证明运行中的进程就是盘上那份。
rx_torn与pdo_reentry整段恒零 = 无检出功效,不是"无撕裂"。二者分别需固件 1.6.6/1.6.7+,版本未知 → 无法区分"没发生"与"字段根本没实现、恒读 0"。- λ≈3993/s 是本次 CAN 工况。λ 变则负担变,0.5ms 这个数不可外搬到别流量。
- 单台单次,未做重复性与温漂验证。
六、下一步
按优先级
- 查明
drop口径(当前最高优先,唯一未决的承重项)。必须用一对独立可比的计数,不能再拿同速量相除:从 WiFi 侧读固件 u32rx_drop_count(app_spi_cmd_can_feedback.cpp:99)对速率,同时用 CAN 监控页统计实收 CONFIG 帧数作分母。只有「独立到达计数 − 独立交付计数」才是丢帧数。 - 台子现状:断电重启后重新 open+start 成功(OP、wkc=3),无 CAN 的 0.25→0.0625ms 五档补测完成,周期已自动恢复 1ms。CAN 设备当前处于拔除状态,电机数据相关指标要插回 CAN 才能再测。
- 插回 CAN 后补测 0.35 / 0.4 / 0.5ms 三档的电机数据刷新率,把带 CAN 工况的可用上限定位出来。load_perm 已被证明在快档饱和,不能再用它定位拐点;扫描脚本的
motors_online/online_frac/motor_refresh_per_s三列才是可用性判据。 - 给
fillRxWindow加直接计时,把 121µs 硬耗时里的主/从占比拆开(属固件改动,需批准)。 - 0.5ms 档实跑电机页完整命令序列 —— 本调研只测了计数器,没测界面实际可用性。
本报告由
scripts/build_ecat_report_html.py 从 lab/*.csv 与 lab/ecat_calib.json 现场渲染,图中每个数值均从原始数据计算,未手抄。native 实时:op=1 loop=1 wkc=3 period=1.0ms。Markdown 版(含同源结论):ECAT_RATE_TRADEOFF.md。