# 2026042802-1 抓包触发点与示教点对比记录 ## 1. 目的 - 使用 `../Rvbust/uttc-20260428/2026042802-1.pcap` 确认当前仓库是否已经导出过这份抓包对应的真实运动轨迹发包记录。 - 如果现有导出不包含 IO 触发信息,则补一份“真实 J519 发包 + IO 标记”导出。 - 将抓包里真实触发 IO 的关节坐标,与 `Config/RobotConfig.json` 中 `flying_shots.UTTC_MS11` 的示教点逐点对比。 - 把结论固定成仓库文档,后续继续和新程序抓包、`ShotEvents.json`、运行时导出结果做同口径比较。 ## 2. 数据来源与口径 - 抓包文件:`../Rvbust/uttc-20260428/2026042802-1.pcap` - 旧目录配置文件:`../Rvbust/uttc-20260428/RobotConfig.json` - 新版程序运行时配置文件:`Config/RobotConfig.json` - 对比对象:两份配置中的 `flying_shots.UTTC_MS11` - 协议口径:只看 `192.168.10.10 -> 192.168.10.11` 的 `UDP 60015` 64B J519 命令帧 - 触发判定:`write_io_value > 0` 视为 IO 置位 - 去重规则:由于 `io_keep_cycles=2`,每次触发只记录首个高电平帧 - 关节单位:抓包 J519 目标关节为 `deg`;`RobotConfig.json` `traj_waypoints` 为 `rad`,分析时先转成 `deg` 补充说明: - `../Rvbust/uttc-20260428/` 目录中的内容代表旧抓包及其同目录资料。 - `Config/Data/UTTC_MS11/` 目录中的内容代表现在新版程序的运行时导出。 - 本文不会把两者混成同一次运行的数据;这里只拿新版程序目录里的文件做“现状参照”,核心分析对象仍然是旧抓包 `2026042802-1.pcap`。 ## 3. 现状核对 ### 3.1 仓库里已经存在什么 - 旧抓包目录已有原始抓包:`../Rvbust/uttc-20260428/2026042802-1.pcap` - 旧抓包目录已有按运动段拆分的导出:`../Rvbust/uttc-20260428/2026042802-1_joint_segments/` - 旧抓包目录已有速度档位整理结果:`../Rvbust/uttc-20260428/1倍速度 角度坐标点/真实轨迹JointDetialTraj.txt` - 新版程序目录已有运行时导出:`Config/Data/UTTC_MS11/ActualSendJointTraj.txt` - 新版程序目录已有运行时导出:`Config/Data/UTTC_MS11/ActualSendTiming.txt` - 新版程序目录已有理论触发时间轴:`Config/Data/UTTC_MS11/ShotEvents.json` ### 3.2 当前缺的是什么 - 新版程序目录中的 `ActualSendJointTraj.txt` 是现在新版程序落盘的真实发送关节轨迹,但不带 IO 列,也不属于 `2026042802-1.pcap` 这次旧抓包本身。 - 旧抓包目录中的 `2026042802-1_joint_segments/` 是按运动段拆开的 `JointDetialTraj.txt` 风格导出,也不包含整条抓包的 IO 标记。 - 旧抓包目录中的 `1倍速度 角度坐标点/真实轨迹JointDetialTraj.txt` 提供了真实轨迹文本,但同样不带 IO 触发列。 - 本次分析前,没有发现一份专门对应 `2026042802-1.pcap` 的“整条真实 J519 发包记录 + IO 标记 + 触发帧筛选”组合导出。 结论: 旧抓包目录和新版程序目录里都已经有各自的轨迹资料,但在本次分析前,还没有一份专门对应 `2026042802-1.pcap` 的、带 IO 触发信息的整条实际发包记录。 ## 4. 本次新增导出 本次补充生成了以下文件: - `analysis/2026042802-1/2026042802-1_j519_actual_send_all_with_io.csv` - `1788` 条客户端真实下发 J519 命令 - 包含 `frame_number / time_relative_s / sequence / write_io_value / io_addrs / j1..j6` - `analysis/2026042802-1/2026042802-1_j519_trigger_frames.csv` - 只保留每次 IO 高电平脉冲的第一帧 - 共 `17` 条,和 `ShotEvents.json` 的触发次数一致 - `analysis/2026042802-1/2026042802-1_trigger_vs_teach_points.csv` - 每个真实触发帧与 `UTTC_MS11` 示教点的逐点偏差表 - `analysis/2026042802-1/2026042802-1_analysis_summary.json` - 本文档引用的汇总统计 对应复现脚本: - `analysis/analyze_2026042802_1_trigger_vs_teach_points.py` ## 5. 触发与地址核对 - 抓包提取到的真实触发次数:`17` - `ShotEvents.json` 中的触发次数:`17` - `write_io_value` 解码出的 `io_addrs` 与 `UTTC_MS11.addr` 没有真实内容不一致 - 其中有 `14` 处列表顺序不同,例如抓包里是 `[2, 3, 4]`,配置里写的是 `[3, 4, 2]` - 这 `14` 处只是顺序差异,不是触发地址集合差异 - 真正的地址集合不匹配数:`0` 结论: `2026042802-1.pcap` 中真实发出的 IO 地址集合,与 `UTTC_MS11` 配置的 `addr` 集合是一致的。 ## 6. 触发点与示教点误差统计 - 平均单点最大单轴误差:`4.241584 deg` - 最大单轴误差:`11.130744 deg` - 平均 RMS 误差:`2.540614 deg` - 最大误差轴分布:`J6=9`、`J4=5`、`J5=1`、`J2=1`、`J1=1` 这说明: - 这份旧抓包里的真实触发点,并不是严格贴在配置示教点上触发 - 误差主要集中在 `J6` 和 `J4` - 由于 `../Rvbust/uttc-20260428/RobotConfig.json` 和当前仓库 `Config/RobotConfig.json` 的 `UTTC_MS11` 四组关键字段完全一致,因此这里的“示教点”对旧抓包和当前配置是同一组数据 - 如果用户的“对焦准确示教点”就是这组 `traj_waypoints`,那么旧程序实际触发时已经存在几度量级的偏移,而不是亚角度级别的完全重合 ## 7. 最大偏差点 按最大单轴误差从大到小列出前 5 个触发点: | 触发序号 | waypoint_index | 实际 io_addrs | 最大误差轴 | 最大误差(deg) | RMS误差(deg) | 主要差值 | | --- | --- | --- | --- | ---: | ---: | --- | | 3 | 3 | `[2, 3, 4]` | `J6` | `11.130744` | `5.955608` | `J4 +8.811537`,`J6 -11.130744` | | 7 | 7 | `[3, 4]` | `J6` | `7.194107` | `4.618545` | `J2 -5.441249`,`J4 -6.792278`,`J6 +7.194107` | | 17 | 18 | `[3, 4]` | `J4` | `6.848522` | `4.036140` | `J2 +1.835940`,`J4 -6.848522`,`J6 +6.320424` | | 13 | 13 | `[3, 4]` | `J1` | `6.656209` | `4.109282` | `J1 +6.656209`,`J3 -3.848692`,`J4 +3.795032` | | 8 | 8 | `[2, 4]` | `J6` | `5.939884` | `3.533133` | `J2 -2.961069`,`J4 -5.456660`,`J6 +5.939884` | ## 8. 逐点结果 完整逐点数据已写入: - `analysis/2026042802-1/2026042802-1_trigger_vs_teach_points.csv` 这里保留简表,方便直接看每次触发的最大偏差: | 触发序号 | waypoint_index | frame | seq | time(s) | io_addrs | 最大误差轴 | 最大误差(deg) | RMS误差(deg) | | --- | --- | ---: | ---: | ---: | --- | --- | ---: | ---: | | 1 | 1 | 1955 | 1381124 | 5.888271 | `[2, 4]` | `J6` | `3.096909` | `2.341857` | | 2 | 2 | 2151 | 1381198 | 6.480252 | `[2, 3, 4]` | `J4` | `2.737488` | `1.612807` | | 3 | 3 | 2223 | 1381226 | 6.704225 | `[2, 3, 4]` | `J6` | `11.130744` | `5.955608` | | 4 | 4 | 2336 | 1381269 | 7.048266 | `[2, 4]` | `J6` | `2.373383` | `1.439206` | | 5 | 5 | 2477 | 1381321 | 7.464289 | `[2, 4]` | `J5` | `1.973330` | `1.273254` | | 6 | 6 | 2665 | 1381391 | 8.024277 | `[3, 4]` | `J2` | `1.265078` | `0.786940` | | 7 | 7 | 2811 | 1381447 | 8.472317 | `[3, 4]` | `J6` | `7.194107` | `4.618545` | | 8 | 8 | 2908 | 1381483 | 8.760361 | `[2, 4]` | `J6` | `5.939884` | `3.533133` | | 9 | 9 | 3112 | 1381561 | 9.384332 | `[2, 4]` | `J4` | `2.414749` | `1.565121` | | 10 | 10 | 3170 | 1381583 | 9.560358 | `[2, 4]` | `J4` | `3.629631` | `2.268783` | | 11 | 11 | 3350 | 1381651 | 10.104322 | `[2, 4]` | `J6` | `3.113037` | `2.115595` | | 12 | 12 | 3470 | 1381697 | 10.472333 | `[2, 4]` | `J6` | `4.018547` | `2.003201` | | 13 | 13 | 3627 | 1381756 | 10.944371 | `[3, 4]` | `J1` | `6.656209` | `4.109282` | | 14 | 14 | 3710 | 1381787 | 11.192339 | `[2, 4]` | `J6` | `2.834801` | `1.514764` | | 15 | 15 | 3796 | 1381820 | 11.456361 | `[2, 4]` | `J4` | `2.496544` | `1.640818` | | 16 | 16 | 3870 | 1381848 | 11.680362 | `[2, 4]` | `J6` | `3.782349` | `2.176883` | | 17 | 18 | 4138 | 1381950 | 12.496365 | `[3, 4]` | `J4` | `6.848522` | `4.036140` | ## 9. UTTC_MS11 示教点角度表 为了便于和导出的触发序列直接手工比对,下面把当前 `UTTC_MS11` 示教点从 `rad` 转成 `deg`。 说明: - `waypoint_index` 使用配置里的原始下标。 - `shot_flag=true` 表示该点在配置里是拍照点候选。 - `addr` 保留原配置里的触发地址列表,便于和 `io_addrs` 对照。 | waypoint_index | shot_flag | addr | J1(deg) | J2(deg) | J3(deg) | J4(deg) | J5(deg) | J6(deg) | | --- | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | | 0 | `false` | `[]` | 60.546226 | 0.668344 | -1.025155 | -0.869105 | 1.231405 | 0.548197 | | 1 | `true` | `[2, 4]` | 48.886810 | 2.198985 | -11.021017 | 0.410210 | 6.248381 | 2.294991 | | 2 | `true` | `[3, 4, 2]` | 55.347755 | 11.807040 | -7.009095 | -71.014331 | 6.012065 | 74.249532 | | 3 | `true` | `[3, 4, 2]` | 55.109808 | 8.759497 | -8.518217 | -41.117250 | 10.108488 | 41.956933 | | 4 | `true` | `[4, 2]` | 43.653593 | -1.629660 | -17.715754 | 5.995209 | 32.171718 | -22.573973 | | 5 | `true` | `[4, 2]` | 64.582445 | 4.262979 | -15.669217 | -29.952927 | 29.850440 | 45.626527 | | 6 | `true` | `[3, 4]` | 60.479483 | 23.068782 | -5.011264 | 36.106855 | 5.525681 | -31.302564 | | 7 | `true` | `[3, 4]` | 70.475837 | 16.393849 | -13.456948 | -27.892319 | 14.535662 | 31.711933 | | 8 | `true` | `[4, 2]` | 69.582464 | -17.105713 | -8.710590 | -58.475694 | 7.630590 | 64.437733 | | 9 | `true` | `[4, 2]` | 73.571259 | -6.429845 | -9.628580 | -128.808647 | 14.671083 | 140.002048 | | 10 | `true` | `[4, 2]` | 75.569386 | -14.679306 | -7.294156 | -130.923033 | 17.688361 | 141.893508 | | 11 | `true` | `[4, 2]` | 86.093498 | -14.498333 | -13.681511 | -69.868296 | 26.742682 | 88.999414 | | 12 | `true` | `[4, 2]` | 61.720733 | -4.232789 | -9.784423 | -108.384083 | 22.160772 | 118.142064 | | 13 | `true` | `[4, 3]` | 79.757083 | 4.640231 | -15.311186 | -56.035312 | 26.133138 | 52.152257 | | 14 | `true` | `[4, 2]` | 108.955551 | 1.370760 | -33.594425 | -48.367361 | 43.963404 | 85.039261 | | 15 | `true` | `[4, 2]` | 110.584848 | -3.860900 | -32.396407 | -51.112591 | 44.229160 | 87.624000 | | 16 | `true` | `[4, 2]` | 118.095952 | -17.376387 | -31.069001 | -59.560538 | 48.732576 | 94.134771 | | 17 | `false` | `[4, 2]` | 62.573787 | -22.938069 | -10.333288 | 77.491373 | 35.583412 | -69.668648 | | 18 | `true` | `[4, 3]` | 60.282482 | -22.938081 | -10.333248 | 77.491642 | 35.583378 | -69.668981 | | 19 | `false` | `[]` | 60.546226 | 0.668344 | -1.025155 | -0.869105 | 1.231405 | 0.548197 | ## 10. 结论 1. `../Rvbust/uttc-20260428/` 目录里已有旧抓包相关轨迹资料,`Config/Data/UTTC_MS11/` 目录里已有新版程序运行时导出,但在本次分析前,没有一份专门对应 `2026042802-1.pcap` 的“整条真实 J519 发包记录 + IO 标记”导出。 2. 本次已经补齐该导出,文件位于 `analysis/2026042802-1/2026042802-1_j519_actual_send_all_with_io.csv`。 3. `2026042802-1.pcap` 的真实 IO 触发次数是 `17`,与 `ShotEvents.json` 一致。 4. 抓包中真实触发时使用的 IO 地址集合,与 `UTTC_MS11.addr` 一致;只有列表顺序差异,没有地址集合差异。 5. 抓包中真实触发点相对示教点的平均最大单轴误差约 `4.24 deg`,最大达到 `11.13 deg`,主要偏在 `J6` 和 `J4`。 6. 因此,如果后续要分析“为什么新程序拍照位置偏了”,不能把 `2026042802-1.pcap` 视为“与当前示教点完全重合的零误差基准”;它本身相对当前配置示教点就存在明显偏差。 ## 11. 触发点前 6 个周期的实际关节坐标与示教点差值 在这份旧抓包里,17 个真实触发点对应的“最接近示教点”的实际采样点,统一出现在触发前 `6` 个周期,也就是前 `48ms`。 为了方便手工比对,这里把每个触发点前 `6` 个周期的实际关节坐标单独列出来,并和对应示教点逐轴做差。 说明: - `pre6_frame` / `pre6_time` 是触发帧前 `6` 个 `8ms` 周期的实际 J519 发包点。 - `pre6_j*` 是该时刻的实际关节角度,单位 `deg`。 - `diff_j*` 定义为 `pre6_actual - teach_point`,单位 `deg`。 - 这张表更适合你直接和导出的触发序列逐点人工核对。 | 触发序号 | waypoint_index | pre6_frame | pre6_time(s) | pre6_J1 | pre6_J2 | pre6_J3 | pre6_J4 | pre6_J5 | pre6_J6 | diff_J1 | diff_J2 | diff_J3 | diff_J4 | diff_J5 | diff_J6 | max_abs_diff(deg) | rms_diff(deg) | | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | | 1 | 1 | 1941 | 5.840207 | 49.036247 | 2.169637 | -10.893022 | 0.469577 | 6.183248 | 2.196172 | +0.149437 | -0.029348 | +0.127996 | +0.059368 | -0.065133 | -0.098818 | 0.149437 | 0.097560 | | 2 | 2 | 2135 | 6.432175 | 55.402473 | 11.806953 | -6.980721 | -70.955170 | 6.007696 | 74.193436 | +0.054718 | -0.000086 | +0.028375 | +0.059162 | -0.004369 | -0.056096 | 0.059162 | 0.041764 | | 3 | 3 | 2207 | 6.656264 | 55.185375 | 8.828770 | -8.461165 | -41.609406 | 9.991771 | 42.555542 | +0.075567 | +0.069272 | +0.057051 | -0.492155 | -0.116717 | +0.598609 | 0.598609 | 0.323506 | | 4 | 4 | 2321 | 7.000291 | 43.639214 | -1.593877 | -17.673382 | 5.988685 | 32.060623 | -22.587208 | -0.014380 | +0.035782 | +0.042372 | -0.006524 | -0.111095 | -0.013234 | 0.111095 | 0.051385 | | 5 | 5 | 2457 | 7.416267 | 64.456329 | 4.153820 | -15.749991 | -29.951208 | 30.008862 | 45.422455 | -0.126115 | -0.109159 | -0.080774 | +0.001719 | +0.158422 | -0.204073 | 0.204073 | 0.129802 | | 6 | 6 | 2649 | 7.976283 | 60.479656 | 23.070845 | -5.011842 | 36.106964 | 5.525600 | -31.304708 | +0.000174 | +0.002063 | -0.000578 | +0.000109 | -0.000081 | -0.002145 | 0.002145 | 0.001241 | | 7 | 7 | 2795 | 8.424315 | 70.489815 | 16.270372 | -13.455009 | -28.079985 | 14.531018 | 31.910471 | +0.013978 | -0.123476 | +0.001939 | -0.187666 | -0.004644 | +0.198538 | 0.198538 | 0.122545 | | 8 | 8 | 2892 | 8.712310 | 69.566750 | -17.333443 | -8.667440 | -58.787853 | 7.566183 | 64.777092 | -0.015715 | -0.227729 | +0.043149 | -0.312159 | -0.064407 | +0.339359 | 0.339359 | 0.212417 | | 9 | 9 | 3096 | 9.336375 | 73.577042 | -6.447144 | -9.620103 | -128.858627 | 14.686206 | 140.051056 | +0.005783 | -0.017298 | +0.008477 | -0.049980 | +0.015123 | +0.049008 | 0.049980 | 0.030367 | | 10 | 10 | 3154 | 9.512325 | 75.498016 | -14.523483 | -7.317341 | -131.103271 | 17.629053 | 142.061157 | -0.071370 | +0.155823 | -0.023185 | -0.180238 | -0.059308 | +0.167649 | 0.180238 | 0.125181 | | 11 | 11 | 3334 | 10.056378 | 86.017281 | -14.446944 | -13.689492 | -69.926414 | 26.744770 | 89.069130 | -0.076217 | +0.051389 | -0.007981 | -0.058119 | +0.002088 | +0.069716 | 0.076217 | 0.052846 | | 12 | 12 | 3455 | 10.424364 | 61.554295 | -4.126511 | -9.723098 | -108.476807 | 22.091070 | 117.986275 | -0.166438 | +0.106278 | +0.061325 | -0.092724 | -0.069702 | -0.155789 | 0.166438 | 0.115819 | | 13 | 13 | 3611 | 10.896324 | 80.232681 | 4.624288 | -15.574081 | -55.685375 | 26.385595 | 52.279755 | +0.475598 | -0.015943 | -0.262896 | +0.349937 | +0.252457 | +0.127497 | 0.475598 | 0.288100 | | 14 | 14 | 3694 | 11.144347 | 108.790268 | 1.405766 | -33.485344 | -48.378551 | 43.857502 | 84.771103 | -0.165283 | +0.035006 | +0.109081 | -0.011190 | -0.105902 | -0.268158 | 0.268158 | 0.143580 | | 15 | 15 | 3780 | 11.408361 | 110.543327 | -3.739877 | -32.432842 | -50.983562 | 44.216587 | 87.566269 | -0.041521 | +0.121023 | -0.036435 | +0.129028 | -0.012573 | -0.057731 | 0.129028 | 0.079412 | | 16 | 16 | 3854 | 11.632360 | 118.081940 | -17.294281 | -31.087101 | -59.614193 | 48.711964 | 94.208046 | -0.014013 | +0.082106 | -0.018100 | -0.053655 | -0.020612 | +0.073275 | 0.082106 | 0.051540 | | 17 | 18 | 4121 | 12.448375 | 60.258179 | -22.880474 | -10.315624 | 77.249168 | 35.501560 | -69.439224 | -0.024304 | +0.057606 | +0.017624 | -0.242474 | -0.081817 | +0.229756 | 0.242474 | 0.142884 | 这张表说明: - 如果把旧程序的真实 IO 触发点整体前移 `6` 个周期,再去看对应关节坐标,它们和示教点已经非常接近。 - 17 个点里,大多数前 `6` 周期采样点与示教点的最大单轴误差都在 `0.05 ~ 0.34 deg`。 - 误差较大的两个点是: - `trigger 3`:`max_abs_diff = 0.598609 deg` - `trigger 13`:`max_abs_diff = 0.475598 deg` - 这进一步支持一个很强的现象:旧抓包里的真实 IO 触发时刻,相对“最接近示教点”的实际运动位置,整体滞后约 `48ms`。 ## 12. 复现命令 在仓库根目录执行: ```powershell python analysis/analyze_2026042802_1_trigger_vs_teach_points.py python analysis/analyze_2026042802_1_status_feedback_vs_teach_points.py ``` 脚本默认读取: - `../Rvbust/uttc-20260428/2026042802-1.pcap` - `Config/RobotConfig.json` - `D:\Zyx\Downloads\WiresharkPortable32\App\Wireshark\tshark.exe` 如果后续 Wireshark 路径变化,需要先同步修改脚本中的 `DEFAULT_TSHARK`。 ## 13. 60015 状态反馈视角复核 上面第 11 节的“前 6 个周期最接近示教点”,只是在看: - `192.168.10.10 -> 192.168.10.11` 的 64B J519 下发命令序列 它说明的是“命令目标流”和示教点之间的相对时序,不能直接等价成“机械臂实时反馈已经过了示教点才触发”。 针对你补充的 `buffer_size` 线索,这里进一步直接解析了: - `192.168.10.11 -> 192.168.10.10` 的 132B `UDP 60015` 状态包 - 解码口径与仓库运行时代码 `src/Flyshot.Runtime.Fanuc/Protocol/FanucJ519Protocol.cs` 一致,全部按大端解析 - 关节反馈字段使用状态包里的 `0x3C ~ 0x57` 六轴 `float32 deg` 本次新增文件: - `analysis/2026042802-1/2026042802-1_j519_status_feedback_all.csv` - `analysis/2026042802-1/2026042802-1_trigger_status_feedback_vs_teach_points.csv` - `analysis/2026042802-1/2026042802-1_trigger_manual_compare.csv` - `analysis/2026042802-1/2026042802-1_status_feedback_summary.json` - `analysis/analyze_2026042802_1_status_feedback_vs_teach_points.py` ### 13.1 序列号关系先确认 整条抓包里共提取到: - 状态包 `1789` 条 - 命令包 `1788` 条 对每一条命令帧,取它前一个状态帧做对齐,结果是: - `命令 sequence - 前一条状态 sequence = 8` - 统计结果:`1788 / 1788` 全部成立 这说明在这份旧抓包里,`buffer_size=8` 不是偶发现象,而是整条 J519 流都稳定成立。 换句话说: - 触发命令包上的 `sequence=1381124` - 它对应“当前实时反馈”的状态包 `sequence=1381116` - 中间固定隔着 `8` 个状态周期,也就是约 `64ms` ### 13.2 触发当下的实时反馈,并不在示教点附近 如果直接取“每个触发命令帧对应的当前状态包”,也就是: - `paired_status_sequence = trigger_sequence - 8` 那么 17 个点相对示教点的误差反而更大: - 平均最大单轴误差:`6.469219 deg` - 最大单轴误差:`16.123934 deg` - 最大误差轴分布:`J6=9`、`J2=4`、`J1=2`、`J4=2` 这说明: - 机械臂在触发命令真正下发的那个时刻,对应的实时反馈位置,大多数情况下还没有跑到示教点附近 - 所以“最接近示教点都出现在触发前 6 个周期”这个现象,不能理解成“机器人已经过了示教点才开始触发” - 那只是命令流相对示教点的时序关系;实时反馈要再往后看 ### 13.3 实时反馈最接近示教点,出现在触发后 9 到 10 个状态周期 对每个触发点,以 `paired_status_sequence = trigger_sequence - 8` 为起点,在前后 `±20` 个状态周期里搜索“与示教点最接近”的状态反馈帧,结果统一得到: - 最佳点相对 `paired_status` 晚 `9` 或 `10` 个状态周期 - 统计分布:`9 周期 = 9` 次,`10 周期 = 8` 次 折算成触发命令时刻: - 最佳点相对触发命令晚 `1` 或 `2` 个状态周期 - 统计分布:`+1 周期 = 9` 次,`+2 周期 = 8` 次 - 对应抓包时间差约 `71.838 ~ 79.964 ms` - 平均约 `75.661 ms` 这里看起来像是 `1~2` 周期,但要注意基准不同: - 触发命令本身领先当前状态 `8` 个周期 - 所以“触发后约 `72~80ms` 才最接近示教点”与“状态流从当前反馈再往后走 `9~10` 个周期才到位”本质上是同一件事 ### 13.4 实时反馈视角的逐点结果 | 触发序号 | waypoint_index | trigger_frame | trigger_seq | paired_status_frame | paired_status_seq | 触发当下最大误差轴 | 触发当下最大误差(deg) | 触发当下RMS(deg) | 最佳状态seq | 最佳状态相对当前反馈晚几周期 | 最佳状态相对触发时间(ms) | 最佳状态最大误差(deg) | 最佳状态RMS(deg) | | --- | --- | ---: | ---: | ---: | ---: | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | | 1 | 1 | 1955 | 1381124 | 1954 | 1381116 | J1 | 6.588242 | 3.822544 | 1381126 | 10 | 79.853 | 0.207590 | 0.122397 | | 2 | 2 | 2151 | 1381198 | 2150 | 1381190 | J1 | 2.335315 | 1.382754 | 1381199 | 9 | 71.912 | 0.082134 | 0.046161 | | 3 | 3 | 2223 | 1381226 | 2222 | 1381218 | J6 | 16.123934 | 8.852377 | 1381228 | 10 | 79.929 | 0.569692 | 0.323964 | | 4 | 4 | 2336 | 1381269 | 2335 | 1381261 | J6 | 4.901883 | 3.244056 | 1381271 | 10 | 79.909 | 0.094704 | 0.065805 | | 5 | 5 | 2477 | 1381321 | 2476 | 1381313 | J6 | 9.219438 | 4.621321 | 1381323 | 10 | 79.833 | 0.056622 | 0.033575 | | 6 | 6 | 2665 | 1381391 | 2664 | 1381383 | J6 | 6.146461 | 3.047841 | 1381392 | 9 | 71.904 | 0.176974 | 0.085631 | | 7 | 7 | 2811 | 1381447 | 2810 | 1381439 | J6 | 13.151718 | 7.782024 | 1381448 | 9 | 71.873 | 0.306587 | 0.182107 | | 8 | 8 | 2908 | 1381483 | 2907 | 1381475 | J6 | 7.436279 | 5.127041 | 1381484 | 9 | 71.838 | 0.060748 | 0.027507 | | 9 | 9 | 3112 | 1381561 | 3111 | 1381553 | J6 | 5.585605 | 3.277206 | 1381562 | 9 | 71.873 | 0.152260 | 0.089150 | | 10 | 10 | 3170 | 1381583 | 3169 | 1381575 | J2 | 4.165077 | 2.128984 | 1381585 | 10 | 79.856 | 0.073440 | 0.043286 | | 11 | 11 | 3350 | 1381651 | 3349 | 1381643 | J2 | 1.830953 | 1.169340 | 1381652 | 9 | 71.898 | 0.077668 | 0.042465 | | 12 | 12 | 3470 | 1381697 | 3469 | 1381689 | J4 | 4.822353 | 2.861125 | 1381698 | 9 | 71.950 | 0.021077 | 0.014936 | | 13 | 13 | 3627 | 1381756 | 3626 | 1381748 | J4 | 8.152508 | 5.168659 | 1381757 | 9 | 71.855 | 0.145543 | 0.069960 | | 14 | 14 | 3710 | 1381787 | 3709 | 1381779 | J6 | 10.404419 | 5.679811 | 1381789 | 10 | 79.922 | 0.119453 | 0.058360 | | 15 | 15 | 3796 | 1381820 | 3795 | 1381812 | J2 | 2.616108 | 1.591175 | 1381822 | 10 | 79.926 | 0.149803 | 0.087169 | | 16 | 16 | 3870 | 1381848 | 3869 | 1381840 | J2 | 4.682474 | 2.206740 | 1381850 | 10 | 79.964 | 0.172956 | 0.104194 | | 17 | 18 | 4138 | 1381950 | 4137 | 1381942 | J6 | 1.813960 | 1.153699 | 1381951 | 9 | 71.948 | 0.074923 | 0.038345 | ### 13.4.1 手工核对专用三时刻对照 如果你后面要拿着导出的触发序列人工对比,最推荐直接看下面这张表。 对应单独导出文件: - `analysis/2026042802-1/2026042802-1_trigger_manual_compare.csv` 它把每个触发点压缩成 3 个时刻: - `触发命令时刻` - 你真正发出去、同时带着 IO 触发的那条命令 - `触发当下实时反馈` - 按 `buffer_size=8` 回推到当时机器人真实反馈所对应的状态包 - `最接近示教点的实时反馈` - 在状态流中真正最贴近示教点的那一帧 可以直接把它理解成一句话: - “我在第几条命令上触发了” - “触发那一刻机器人实际还差多少” - “再过多久机器人才真正到最接近示教点的位置” | 触发序号 | waypoint_index | 触发命令seq | 触发命令frame | 触发当下状态seq | 触发当下状态frame | 命令领先状态(周期) | 触发当下最大误差(deg) | 最接近示教点状态seq | 最接近示教点状态frame | 从触发当下反馈再晚几周期 | 相对触发命令再晚几周期 | 相对触发命令再晚多久(ms) | 最接近示教点最大误差(deg) | | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | | 1 | 1 | 1381124 | 1955 | 1381116 | 1954 | 8 | 6.588242 | 1381126 | 1977 | 10 | 2 | 79.853 | 0.207590 | | 2 | 2 | 1381198 | 2151 | 1381190 | 2150 | 8 | 2.335315 | 1381199 | 2174 | 9 | 1 | 71.912 | 0.082134 | | 3 | 3 | 1381226 | 2223 | 1381218 | 2222 | 8 | 16.123934 | 1381228 | 2248 | 10 | 2 | 79.929 | 0.569692 | | 4 | 4 | 1381269 | 2336 | 1381261 | 2335 | 8 | 4.901883 | 1381271 | 2361 | 10 | 2 | 79.909 | 0.094704 | | 5 | 5 | 1381321 | 2477 | 1381313 | 2476 | 8 | 9.219438 | 1381323 | 2502 | 10 | 2 | 79.833 | 0.056622 | | 6 | 6 | 1381391 | 2665 | 1381383 | 2664 | 8 | 6.146461 | 1381392 | 2687 | 9 | 1 | 71.904 | 0.176974 | | 7 | 7 | 1381447 | 2811 | 1381439 | 2810 | 8 | 13.151718 | 1381448 | 2833 | 9 | 1 | 71.873 | 0.306587 | | 8 | 8 | 1381483 | 2908 | 1381475 | 2907 | 8 | 7.436279 | 1381484 | 2930 | 9 | 1 | 71.838 | 0.060748 | | 9 | 9 | 1381561 | 3112 | 1381553 | 3111 | 8 | 5.585605 | 1381562 | 3134 | 9 | 1 | 71.873 | 0.152260 | | 10 | 10 | 1381583 | 3170 | 1381575 | 3169 | 8 | 4.165077 | 1381585 | 3195 | 10 | 2 | 79.856 | 0.073440 | | 11 | 11 | 1381651 | 3350 | 1381643 | 3349 | 8 | 1.830953 | 1381652 | 3372 | 9 | 1 | 71.898 | 0.077668 | | 12 | 12 | 1381697 | 3470 | 1381689 | 3469 | 8 | 4.822353 | 1381698 | 3492 | 9 | 1 | 71.950 | 0.021077 | | 13 | 13 | 1381756 | 3627 | 1381748 | 3626 | 8 | 8.152508 | 1381757 | 3649 | 9 | 1 | 71.855 | 0.145543 | | 14 | 14 | 1381787 | 3710 | 1381779 | 3709 | 8 | 10.404419 | 1381789 | 3735 | 10 | 2 | 79.922 | 0.119453 | | 15 | 15 | 1381820 | 3796 | 1381812 | 3795 | 8 | 2.616108 | 1381822 | 3821 | 10 | 2 | 79.926 | 0.149803 | | 16 | 16 | 1381848 | 3870 | 1381840 | 3869 | 8 | 4.682474 | 1381850 | 3895 | 10 | 2 | 79.964 | 0.172956 | | 17 | 18 | 1381950 | 4138 | 1381942 | 4137 | 8 | 1.813960 | 1381951 | 4160 | 9 | 1 | 71.948 | 0.074923 | 这张表最适合回答两个实际问题: 1. 触发到底是不是已经“跑过点”才发生的 - 不是 - 因为触发当下对应的实时反馈误差仍然明显存在 2. 真正最接近示教点的实时位置,距离触发还差多久 - 大部分点还要再过 `1` 或 `2` 个命令周期 - 如果换成状态反馈基准,就是再过 `9` 或 `10` 个状态周期 - 时间量级稳定在 `72~80ms` ### 13.4.2 可以优先人工盯的异常点 如果你不想一开始就看 17 个点,建议优先盯下面几类: - `trigger 3` - 触发当下实时反馈最大误差 `16.123934 deg` - 即便到“最佳反馈时刻”,最大误差也还有 `0.569692 deg` - 它是 17 个点里最值得优先复核的一个 - `trigger 7` - 触发当下实时反馈最大误差 `13.151718 deg` - 到位后仍有 `0.306587 deg` - `trigger 14` - 触发当下实时反馈最大误差 `10.404419 deg` - 属于第二档明显偏大的点 - `trigger 1` - 触发当下误差 `6.588242 deg`,而且最大误差轴是 `J1` - 和前面很多 `J6` 主导的点不一样,适合拿来对比不同类型 如果你想最快抓住旧程序补偿逻辑,建议先人工对照: - `trigger 3` - `trigger 7` - `trigger 14` - `trigger 1` 这 4 个点已经能覆盖: - `J6` 主导的大偏差 - `J1` 主导的特殊点 - `+1` 周期到位与 `+2` 周期到位两种情况 ### 13.5 对“为什么触发时间看起来在我运动到这个位置之后”的解释 现在可以把两种视角彻底分开: 1. 命令流视角 - 触发命令帧前 `6` 个周期的目标点,和示教点已经非常接近 - 所以如果只看“发给机器人什么目标”,会误以为触发发生在“经过示教点之后” 2. 实时反馈视角 - 由于 `buffer_size=8`,触发命令帧本身就领先当前状态 `8` 个周期 - 触发时刻对应的实时反馈,其实还没到示教点附近 - 再往后走 `9~10` 个状态周期,反馈才最接近示教点 因此更准确的表述应该是: - 旧程序不是“机器人已经跑过示教点才开始触发” - 而是“触发命令是按一个提前缓存的目标序列发出去的,命令序列本身比实时反馈领先 8 个周期” - 叠加旧程序里触发标志在目标序列上的布置位置,就会出现: - 命令流看起来:最接近示教点在触发前 `6` 个周期 - 实时反馈看起来:最接近示教点在触发后 `9~10` 个状态周期 两者并不矛盾,基准不同而已。 ### 13.6 本节结论 1. 这份旧抓包的 `60015` 状态流已经直接证明:下发命令序列稳定领先当前状态序列 `8` 个周期。 2. 因此不能再把“命令流中的前 6 周期最接近示教点”误解成“机械臂真实位置已经过点后才触发”。 3. 按实时反馈看,17 个触发点在触发当下都还没有到示教点附近;最接近示教点的反馈统一出现在触发后约 `72~80ms`。 4. 如果后续要反推旧程序拍照补偿逻辑,优先应该建模成: - `命令目标序列` 提前缓存 `8` 个周期 - `触发标志` 在目标序列上又相对示教点提前约 `6` 个周期 - 两者叠加后,实时反馈到位时刻落在触发之后约 `1~2` 个命令周期 ## 14. TriggerSampleIndexOffsetCycles 取值 6 / 7 / 8 的对照 在代码里把触发绑定后移做成显式配置之后,进一步对 `6 / 7 / 8` 三档做了离线对照。 本节的比较口径是: - 先找“最接近示教点”的最佳命令 sample - 再分别向后偏移 `6 / 7 / 8` 个命令周期 - 比较这三档各自落到的命令帧,与旧抓包真实触发帧的误差大小 新增文件: - `analysis/2026042802-1/2026042802-1_trigger_offset_6_7_8_compare.csv` - `analysis/2026042802-1/2026042802-1_trigger_offset_6_7_8_summary.json` - `analysis/build_trigger_offset_compare_6_7_8.py` ### 14.1 汇总结论 三档比较结果非常明确: - `6` 周期胜出 `17 / 17` - `7` 周期胜出 `0 / 17` - `8` 周期胜出 `0 / 17` 平均误差也呈现单调变差: | 偏移周期 | 平均最大单轴误差(deg) | 平均RMS误差(deg) | | --- | ---: | ---: | | `6` | `4.241584` | `2.540614` | | `7` | `5.068092` | `3.021297` | | `8` | `5.931684` | `3.520176` | 这说明: - 如果目标是“尽量复刻旧抓包里的真实命令触发点”,`TriggerSampleIndexOffsetCycles` 应该选 `6` - 当前写入配置的 `7`,更偏向“再往后推一拍”,但它并不更接近这份旧抓包 ### 14.2 逐点对照表 | 触发序号 | waypoint_index | 最佳sample frame | 偏移6 frame | 偏移6最大误差 | 偏移6RMS | 偏移7 frame | 偏移7最大误差 | 偏移7RMS | 偏移8 frame | 偏移8最大误差 | 偏移8RMS | 本点最优偏移 | | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | | 1 | 1 | 1941 | 1955 | 3.406527 | 2.468439 | 1958 | 4.180932 | 2.935578 | 1961 | 5.004552 | 3.418447 | `6` | | 2 | 2 | 2135 | 2151 | 2.816723 | 1.656246 | 2153 | 3.526760 | 2.060948 | 2155 | 4.301479 | 2.503393 | `6` | | 3 | 3 | 2207 | 2223 | 11.130744 | 5.955608 | 2227 | 13.165517 | 7.035338 | 2229 | 15.212756 | 8.119717 | `6` | | 4 | 4 | 2321 | 2336 | 2.311684 | 1.373158 | 2339 | 3.083186 | 1.743718 | 2341 | 3.952016 | 2.158078 | `6` | | 5 | 5 | 2457 | 2477 | 2.171828 | 1.404704 | 2479 | 2.585320 | 1.667010 | 2483 | 3.004922 | 1.950746 | `6` | | 6 | 6 | 2649 | 2665 | 1.408994 | 0.842403 | 2667 | 1.770170 | 1.066919 | 2669 | 2.303903 | 1.330975 | `6` | | 7 | 7 | 2795 | 2811 | 7.194107 | 4.618545 | 2814 | 8.248680 | 5.331516 | 2817 | 9.275559 | 6.035874 | `6` | | 8 | 8 | 2892 | 2908 | 5.939884 | 3.533133 | 2910 | 6.922169 | 4.090128 | 2912 | 7.916950 | 4.649087 | `6` | | 9 | 9 | 3096 | 3112 | 2.524071 | 1.630593 | 3114 | 2.830116 | 1.857618 | 3118 | 3.101311 | 2.072647 | `6` | | 10 | 10 | 3154 | 3170 | 3.392997 | 2.105986 | 3172 | 4.172522 | 2.562682 | 3175 | 4.999007 | 3.042228 | `6` | | 11 | 11 | 3334 | 3350 | 2.984824 | 2.013493 | 3352 | 3.668105 | 2.458222 | 3355 | 4.397696 | 2.932320 | `6` | | 12 | 12 | 3455 | 3470 | 4.000958 | 1.990246 | 3474 | 4.929097 | 2.393668 | 3476 | 5.929425 | 2.827363 | `6` | | 13 | 13 | 3611 | 3627 | 6.656209 | 4.109282 | 3629 | 7.722974 | 4.798702 | 3633 | 8.793828 | 5.501948 | `6` | | 14 | 14 | 3694 | 3710 | 2.935180 | 1.567983 | 3712 | 3.224395 | 1.721797 | 3714 | 3.454963 | 1.844232 | `6` | | 15 | 15 | 3780 | 3796 | 2.573147 | 1.686690 | 3798 | 3.074261 | 2.037358 | 3801 | 3.579433 | 2.398059 | `6` | | 16 | 16 | 3854 | 3870 | 3.810522 | 2.197783 | 3872 | 4.756826 | 2.710410 | 3874 | 5.783033 | 3.263711 | `6` | | 17 | 18 | 4121 | 4138 | 6.848522 | 4.036140 | 4140 | 8.296536 | 4.890438 | 4142 | 9.827793 | 5.794164 | `6` | ### 14.3 推荐 因此这里可以分成两个不同目标: 1. 如果目标是“尽量复刻旧程序抓包” - 推荐:`TriggerSampleIndexOffsetCycles = 6` 2. 如果目标是“主观上想往实时反馈更靠后推一点,再试效果” - 可以继续保留 `7` - 但要明确:这已经不是“最接近旧抓包”的取值,而是主动做新的补偿尝试 当前基于旧抓包证据,我更推荐: - 先把配置从 `7` 改回 `6` - 再拿新程序实际导出的 `ActualSendJointTraj.txt + ShotEvents.json + 现场图像效果` 做下一轮闭环