feat(motor): 反馈刷新率 Hz 适配(0x0400/0x0401 扩展格式)+ ECAT 密排槽与判活自适应
反馈刷新率(配套电机固件 v0.4.1:0x0400 DLC4/0x0401 DLC2 扩展格式, 选择器 0x0100=STATUS · 0x0101=CONFIG 帧,协议值为周期 ms): - 「节点 ID」页签新增 STATUS/CONFIG 刷新率读取/写入;界面单位一律 Hz (写入按 周期ms=round(1000/Hz) 换算下发,读取反算 Hz 并附原始周期, 取整有损如实展示),0=停发,写入掉电保存;配置值以「读取」回读电机为准 - WiFi 应答回执走 0xC1 per-ID 快照(/can/feedback 全量 entries): GD32 0xC0 ACK 仅 1 字节状态、不携带电机回帧;同 ID 回帧快照只存最新 一条,故一次快照只配一次请求(STATUS/CONFIG 分开读写) - ECAT 回执走批量接收区 takeNodeIdReport,按长度分派 2B 旧格式/4B GET/5B SET ACK - GET_NODE_ID 迁移到扩展选择器 0x0000,兼容旧固件 2B 节点 ID 应答 (SET DLC2 旧格式行为不变) - transport-wifi 新增 fetchFeedbackFull(全量 CAN 反馈快照) EtherCAT/基础设施(密排槽固件 1.6.21 配套): - 快照槽位 5→9 一拍整轮(扫描周期数 6→1);判活阈值随实测 seq 间隔 自适应 clamp(3×EWMA, 2s, 30s),秒级回报档不再被固定 2s 误判离线; STATUS 面板 Hz 明确为网页到达口径(按槽 seq 实测电机回报率的方案已否掉) - esp32_server.py:ecat_native 单实例收紧(后起独占 + taskkill 残留实例, 双实例并存会抢 Npcap 致后起者抓不到从站)+ native stdout 落盘 - flash_gd32.py:刷写后校验「GD32 真的在跑新代码」(版本变化或 uptime 归零), 防"写入≠在跑"假台账 - lab:新增 LAN9252 电源诊断台、PDO 布局页;SPI 测速并入 EtherCAT 接收看板 并更名「SPI/ECAT 通信测速」;移除 continuity_test.html;新增 ECAT 单拍耗时 优化报告;/_local/ 排除入库;测试同步更新(62+58 项全过)
This commit is contained in:
@@ -60,7 +60,7 @@ ul{padding-left:22px}
|
||||
<h2>三、机理与上限推算</h2>
|
||||
<div class="card"><h3>上限已实测:≈8.2kHz,墙是单拍 ≈121µs 的硬耗时</h3><p><b>证据链</b>:无 CAN 设定 0.25 → 0.0625ms 五档,实测循环率在 ≤0.125ms (8kHz)前跟随设定,再快就被钉死在 8196–8238Hz;与此同时主站单拍耗时 <code>cyc p50</code> 在<b>所有档位</b>(含 2ms 慢档)恒为 117–126µs,1/121µs ≈ 8.2kHz,与顶墙实测严丝合缝。即:每拍有一段不可压缩的「发送→从站处理→回读+主站处理」往返时间,周期设定小于它时就堵在这里。</p><p><b>排除项</b>:不是主机 CPU 不够 —— 顶墙档主机 CPU 只有 ~85%(自旋等待占比变小);不是从站拒答 —— wkc 全程 3/3。<code>load_perm</code> 在 ≥4kHz 全饱和 99.x%,既定位不了拐点也喂不准模型,早先「两项模型 load=F/T+R → 2.7/3.5kHz」的外推已被满速补测证伪,仅留档。</p><p><b>未闭合的一处</b>:121µs 里主站协议栈与从站 ESC/固件各占多少,本轮数据分不开 —— 需给固件 <code>fillRxWindow</code> 加直接计时(固件改动,需批准)或抓总线帧时序才能归因。</p></div>
|
||||
<h2>四、判据口径与踩过的坑</h2>
|
||||
<div class="card warn"><h3>仪表陷阱(每条都实际踩过)</h3><ul><li><b>【本次真正翻车处】把两个同速计数器相除当成比率</b> —— <code>drop</code>/<code>rx_count</code> 都约 4000/s,相除恒得 100%,看着像"全丢",其实只是<b>同速量之比</b>。判丢帧必须有<b>独立</b>的分子与分母:本次真正的独立证据是「STATUS 实测 4001 帧/s 正常刷新」+「CONFIG 事件流去重后仅约 92 帧/s」,两者一起否掉了 100% 丢帧。<b>任何"A/B 比率"指标,先问 A 与 B 是否独立计数。</b></li><li><b>从站停报后 <code>load_perm</code> 保留残值</b> —— 实测 <code>age_ms=214338</code> 时仍报 99.2%。任何取 load_perm 的脚本必须先过 <code>age_ms</code> 闸。<b>扫描脚本已加该闸</b>并新增 <code>gd32_load_samples</code>/<code>gd32_load_stale</code> 两列。</li><li><b><code>load_perm</code> 不是整机 CPU 占用率</b> —— 固件 <code>sys/cpu_load.h</code> 定义收窄为"中断+非延迟处理段",<b>排除</b> SSC MainLoop 轮询;当前 free-run(<code>dc=0</code>)下 PDO 应用走主循环轮询路径、<b>不计入</b>。所以不能把"load→100%"读成"CPU 耗尽"。</li><li><b><code>OVF</code> 每拍被 <code>s_rxWinOvf=0</code> 清零</b> —— 从站侧没有可用的累计丢帧计数。读它恒 0 <b>不能</b>当"无丢帧"证据(我据此误判 <code>bridge.drop</code> 是死字段,结论已收回)。</li><li><b><code>/api/can/cadence</code> 只存最近 512 拍</b> —— 0.25ms 档 =128ms、0.125ms=64ms,环没装满时取到的 <code>count</code>/<code>lost</code> 是<b>行数</b>不是每拍帧数。对应 CSV 列已从结论排除。</li><li><b>u16 回绕</b>:<code>drop</code> 在 4000/s 下 16 秒绕一圈 → 10Hz 逐次累加 + 取模,单次增量>32768 记为作废。<b>本次作废点数=0</b>。</li><li><b>「seq 值域 − distinct」不是丢帧率</b>:报 99.686%,方向与 drop≈λ 一致但分母含 STATUS 全流量,口径不干净 → 只作旁证。</li><li><b>周期可被 localStorage 劫持</b>(<code>transport-ecat.js:227-228</code>)→ 每档下发后回读校验。本次六档 <code>hijacked=no</code>、native PID 全程 29284 未变。</li><li><b><code>cyc_us.late</code>/<code>lost</code> 恒 0</b>(遗留占位,未接线),未使用。</li></ul></div>
|
||||
<div class="card warn"><h3>仪表陷阱(每条都实际踩过)</h3><ul><li><b>【本次真正翻车处】把两个同速计数器相除当成比率</b> —— <code>drop</code>/<code>rx_count</code> 都约 4000/s,相除恒得 100%,看着像"全丢",其实只是<b>同速量之比</b>。判丢帧必须有<b>独立</b>的分子与分母:本次真正的独立证据是「STATUS 实测 4001 帧/s 正常刷新」+「CONFIG 事件流去重后仅约 92 帧/s」,两者一起否掉了 100% 丢帧。<b>任何"A/B 比率"指标,先问 A 与 B 是否独立计数。</b></li><li><b>从站停报后 <code>load_perm</code> 保留残值</b> —— 实测 <code>age_ms=214338</code> 时仍报 99.2%。任何取 load_perm 的脚本必须先过 <code>age_ms</code> 闸。<b>扫描脚本已加该闸</b>并新增 <code>gd32_load_samples</code>/<code>gd32_load_stale</code> 两列。</li><li><b><code>load_perm</code> 不是整机 CPU 占用率</b> —— 固件 <code>sys/cpu_load.h</code> 定义收窄为"中断+非延迟处理段",<b>排除</b> SSC MainLoop 轮询;当前 free-run(<code>dc=0</code>)下 PDO 应用走主循环轮询路径、<b>不计入</b>。所以不能把"load→100%"读成"CPU 耗尽"。</li><li><b><code>OVF</code> 每拍被 <code>s_rxWinOvf=0</code> 清零</b> —— GD32 侧没有可用的累计丢帧计数。读它恒 0 <b>不能</b>当"无丢帧"证据(我据此误判 <code>bridge.drop</code> 是死字段,结论已收回)。</li><li><b><code>/api/can/cadence</code> 只存最近 512 拍</b> —— 0.25ms 档 =128ms、0.125ms=64ms,环没装满时取到的 <code>count</code>/<code>lost</code> 是<b>行数</b>不是每拍帧数。对应 CSV 列已从结论排除。</li><li><b>u16 回绕</b>:<code>drop</code> 在 4000/s 下 16 秒绕一圈 → 10Hz 逐次累加 + 取模,单次增量>32768 记为作废。<b>本次作废点数=0</b>。</li><li><b>「seq 值域 − distinct」不是丢帧率</b>:报 99.686%,方向与 drop≈λ 一致但分母含 STATUS 全流量,口径不干净 → 只作旁证。</li><li><b>周期可被 localStorage 劫持</b>(<code>transport-ecat.js:227-228</code>)→ 每档下发后回读校验。本次六档 <code>hijacked=no</code>、native PID 全程 29284 未变。</li><li><b><code>cyc_us.late</code>/<code>lost</code> 恒 0</b>(遗留占位,未接线),未使用。</li></ul></div>
|
||||
<h2>五、可信度边界</h2>
|
||||
<div class="card bad"><h3>这份数据不能证明什么</h3><ul><li><b>板载固件版本无法确认</b>:烧写台账 <code>201832b5a8e258d6…</code> 与在盘 .hex 展平哈希(63d6ab9b…、e6a6e75f…)<b>均不匹配</b>,且产物时间戳晚于台账。→ 本报告不复用任何历史固件结论。</li><li>主站在测量期间又重编过(源码 02:39 / exe 02:40,PID 起于 02:32),无法证明运行中的进程就是盘上那份。</li><li><b><code>rx_torn</code> 与 <code>pdo_reentry</code> 整段恒零 = 无检出功效</b>,不是"无撕裂"。二者分别需固件 1.6.6/1.6.7+,版本未知 → 无法区分"没发生"与"字段根本没实现、恒读 0"。</li><li>λ≈3993/s 是本次 CAN 工况。<b>λ 变则负担变</b>,0.5ms 这个数不可外搬到别流量。</li><li>单台单次,未做重复性与温漂验证。</li></ul></div>
|
||||
<h2>六、下一步</h2>
|
||||
|
||||
Reference in New Issue
Block a user