- 为 ExecuteFlyShotTraj 补齐 wait 语义,并让 move_to_start 先完成临时 PTP 运动后再启动正式飞拍轨迹 - 将 J519 命令发送改为由机器人 UDP status sequence 驱动, 避免在未收到状态包时主动发周期命令 - 将 10010 状态通道关节字段统一按 JointRadians 命名, 同步更新运行时读取逻辑与协议测试 - 新增 FANUC 10010 状态帧、流运动手册和 Python client 逆向文档,并更新 README 与兼容需求说明 - 补充兼容层编排测试与 HTTP 集成测试,覆盖 wait 和 move_to_start 串行化行为
14 KiB
FANUC Stream Motion 文档要点与实现差异
本文记录 ../FANUC_stream_motion.pdf 中与本仓库 Flyshot.Runtime.Fanuc 直接相关的重点,并对照当前实现状态。
读取时间:2026-05-03
1. 文档定位
FANUC_stream_motion.pdf 对应 FANUC Stream motion 功能,选项号为 A05B-2600-J519。它描述的是外部设备通过以太网实时发送期望位置,让机器人按外部生成路径运动的控制方式。
文档明确要求外部设备自行生成满足机器人约束的路径,包括速度、加速度、jerk、可达性、姿态连续性等。FANUC 不提供完整运动学、逆解和碰撞检测公式。
2. 使用前提与示教程序
- 机器人侧需要安装 J519 stream motion 选项。
- 物理网口通过
$STMO.$PHYS_PORT选择,1表示CD38A,2表示CD38B。 - 机器人程序必须包含成对的
IBGN start[*]和IBGN end[*]指令,二者编号必须一致,start必须在end前一行。 IBGN start[*]执行期间,机器人根据外部设备发来的期望位置运动;IBGN end[*]之后程序继续执行。- 执行时要求
AUTO模式和100% OVERRIDE。
当前实现中的 FanucControllerRuntime.EnableRobot() 会按现场抓包流程启动 RVBUSTSM 程序,并随后允许 J519 在收到机器人 UDP status 包后回发命令。是否满足 AUTO / 100% OVERRIDE / IBGN start 已到位,当前只通过 J519 状态位和现场程序行为间接判断,没有在代码里读取或设置这些控制器状态。
3. UDP 60015 协议结构
协议使用 UDP,大端字节序,机器人侧端口为 60015。通信周期通常为 8ms,部分机型支持 4ms。状态包输出可以在任意时间通过 start/stop 控制包启停,不要求已经进入 IBGN start[*]。
3.1 状态输出 start 包
外部设备发给机器人:
| 字段 | 长度 | 值 |
|---|---|---|
| Packet type | 4B | 0 |
| Version | 4B | 1 |
当前实现:FanucJ519Protocol.PackInitPacket() 已按 8B 大端控制包实现,FanucJ519Client.ConnectAsync() 连接后立即发送。
3.2 状态包
机器人发给外部设备,长度为 132B:
| 偏移 | 字段 | 含义 |
|---|---|---|
0x00 |
Packet type | 0 |
0x04 |
Version | 1 |
0x08 |
Sequence No. | 状态包序号,发送 start 包后从 1 重新开始 |
0x0c |
Status | bit0 接受命令、bit1 已收到命令、bit2 SYSRDY、bit3 运动中 |
0x0d..0x12 |
Read I/O 回显和值 | 回显命令包中的读取 IO 类型、索引、掩码,并返回 16 点 IO 值 |
0x14 |
Timestamp | ms 单位,2ms 分辨率 |
0x18..0x38 |
Cartesian / external axis | X/Y/Z/W/P/R 加 3 个扩展轴 |
0x3c..0x5c |
Joint | J1..J9,单位 degree |
0x60..0x80 |
Motor current | J1..J9 电流,单位 A |
当前实现:FanucJ519Protocol.ParseResponse() 已解析 132B 状态包,并暴露 AcceptsCommand、ReceivedCommand、SystemReady、RobotInMotion 四个状态位。FanucControllerRuntime.GetSnapshot() 也会把最新 J519 状态写进快照。
3.3 命令包
外部设备发给机器人,长度为 64B:
| 偏移 | 字段 | 含义 |
|---|---|---|
0x00 |
Packet type | 1 |
0x04 |
Version | 1 |
0x08 |
Sequence No. | 第一包应等于刚收到的状态包序号,后续逐包递增 |
0x0c |
Last data | 正常为 0;结束外部控制时最后一包设为 1 |
0x0d..0x11 |
Reading I/O | 可读取最多 16 个连续 IO 点 |
0x12 |
Data format | 0 笛卡尔,1 关节 |
0x13..0x19 |
Writing I/O | 可写入最多 16 个连续 IO 点 |
0x1a |
unused | 2B |
0x1c..0x3c |
target[9] | 9 个 f32 目标值;关节格式时单位 degree |
当前实现:FanucJ519Protocol.PackCommandPacket() 已按上述布局打包,默认 dataStyle=1,也就是关节格式。运行时会把规划输出的弧度制关节轨迹转换为 degree 后下发。
3.4 状态输出 stop 包
外部设备发给机器人:
| 字段 | 长度 | 值 |
|---|---|---|
| Packet type | 4B | 2 |
| Version | 4B | 1 |
文档把它定义为“停止状态包输出”的控制包,不是命令流正常终止的首选动作。命令流结束应通过 command packet 的 Last data=1 表达。
当前实现:FanucJ519Client.StopMotionAsync() 当前会停止状态包驱动发送并发送 packet type 2,而稠密轨迹执行期间保持 LastData=0。这是与 FANUC 文档最明显的语义差异之一;已有多数 UTTC 抓包显示主运行窗口 LastData=0,但 ../j519 协议.pcap 中存在 1 个 LastData=1 后紧跟 packet type 2 的样本,后续应单独校准停止语义。
4. 通信时序重点
文档推荐的时序是:
- 外部设备发送状态输出 start 包。
- 机器人每个通信周期输出状态包。
- 机器人程序执行到
IBGN start[*]后,状态包 bit0 变为1,表示等待命令包。 - 外部设备收到 bit0 为
1的状态包后,立即发送第一帧命令包,第一帧命令序号应等于刚收到的状态包序号。 - 后续每收到一个状态包,外部设备应立即发送下一帧命令包。
- 结束命令通信时,发送
Last data=1的最后一帧命令包。
当前实现对照:
FanucJ519Client已改为收到机器人 132B status 包后立即回发当前命令,不再由上位机本地固定 8ms 发送循环主动发包。- 命令包 sequence 已按刚收到的 status packet sequence 写入,避免第一帧从本地
0起步。 FanucControllerRuntime.ExecuteTrajectory()启动前会检查已有 J519 响应中的AcceptsCommand和SystemReady;但如果还没收到状态包,则会放行,后续命令仍要等第一帧 status 到达才会发出。- 当前稠密轨迹结束不发送
LastData=1,而是依赖停止 J519 状态包驱动发送和 packet type2stop 控制包。
序号和节拍已经按手册方向校准;停止语义仍需在真实 R30iB 联调中继续确认。
5. 命令缓冲
文档说明机器人可以缓冲提前到达的 command packet。默认启用,缓冲上限为 $STMO.$PKT_STACK - 1,$PKT_STACK 默认 10,可配置范围 2..10。$STMO.$START_MOVE 决定积累多少未处理命令包后开始运动,默认 1。
注意事项:
- 只有 command packet 会进入缓冲。
- status output stop packet 会立即处理。
- 如果 command buffer 中还有未处理包,不应发送 status output stop packet。
- 使用
Last data=1时,机器人会先处理完缓冲里的命令包,再结束外部控制。
当前实现没有显式预填 $PKT_STACK 缓冲,也没有读取 $START_MOVE。FanucJ519Client 只保存一个“当前命令”,由后台循环持续发送;FanucControllerRuntime.SendDenseTrajectory() 另一个 8ms 循环负责按轨迹时间更新这条当前命令。这与文档的“按状态包响应并可提前发多包缓冲”模型不同。
6. 可执行运动条件
文档列出的主要运动约束:
- 目标点必须可达。
- 笛卡尔格式下目标点对应的关节解必须唯一,且 configuration 要与
IBGN start开始时一致。 - 各轴必须满足上下限。
- 不能发生自碰撞。
- 必须考虑 FANUC J3 轴定义:J3 不是相对 J2 臂的夹角,而是机器人视角下相对水平面的 J3 臂角度。
- 外部设备必须控制每轴速度、加速度、jerk 不超过
$STMO_GRP下的限制。 - 状态包中的当前位置是 servo feedback position,不是 command position。轨迹起点应平滑连接到机器人 command position,而不能简单用当前 servo position 直接起步。
- reducer load 超限也会导致停机,负载相关计算不公开。
当前实现对这些条件的覆盖:
| 条件 | 当前状态 |
|---|---|
| 关节格式下发 | 已实现,当前现场链路默认只使用关节格式 |
rad -> deg |
已实现,并由 UTTC J519 golden tests 覆盖 |
speed_ratio 下发时间轴缩放 |
已实现,规则为 t_traj = k * 0.008 * speed_ratio |
| IO 触发嵌入 J519 命令包 | 已实现,使用 write_io_type/index/mask/value |
| 速度、加速度、jerk 约束 | 规划层有 acc_limit / jerk_limit 等兼容参数,但未从 FANUC $STMO_GRP 在线读取,也未实现手册附录中的 20 档速度/负载插值 |
| J3 轴定义 | 当前文档未见专门处理;需要确认 .robot 模型与现场导出轨迹是否已经采用 FANUC J3 定义 |
| command position 起步 | MoveJoint 会用当前运行时记录的关节作为起点生成 PTP 稠密轨迹;但没有通过 FANUC HMI 通信读取 command position |
| reducer load | 未建模,依赖保守规划和现场报警反馈 |
| 笛卡尔格式限制 | 运行时不走笛卡尔 J519 目标格式,暂不覆盖 configuration 变化报警 |
7. 系统变量
与本仓库后续最相关的变量:
| 变量 | 默认/含义 |
|---|---|
$STMO.$PHYS_PORT |
物理口,1=CD38A,2=CD38B |
$STMO.$COM_INT |
通信周期,单位 ms,通常 8,只读 |
$STMO.$PKT_STACK |
command buffer 最大保留量,默认 10 |
$STMO.$START_MOVE |
缓冲中积累多少未处理命令后开始运动,默认 1 |
$STMO_GRP.$JNT_VEL_LIM[*] |
各轴速度上限,degree/s,只读 |
$STMO_GRP.$JNT_ACC_LIM[*] |
各轴加速度上限,degree/s^2,只读 |
$STMO_GRP.$JNT_JRK_LIM[*] |
各轴 jerk 上限,degree/s^3,只读 |
$STMO_GRP.$LMT_MODE |
加速度/jerk 限制计算模式 |
$STMO_GRP.$WARN_LIM |
接近限制时报警阈值,默认 80% |
$STMO_GRP.$FLTR_LN |
命令目标移动平均滤波窗口 |
$STMO_GRP.$MAX_SPD |
用于限制计算的 flange center 最大速度 |
当前实现没有读取或设置上述系统变量。RobotProfile.ServoPeriod 当前决定运行时发送周期;对当前现场而言应继续确认它与 $COM_INT 一致。
8. 附录 B:加速度和 jerk 限制
文档说明,在 $STMO_GRP[].$LMT_MODE=0 时,加速度和 jerk 的允许上限会根据 flange center speed 与 payload 计算:
- 以
$MAX_SPD分成 20 档速度区间。 - 每个轴、每种限制类型都有无负载和最大负载两张 20 档表。
- 实际 payload 通过线性插值得到限制表。
- 实际 flange center speed 在相邻速度档之间线性插值。
- 限制值不是每个通信周期都更新,而是在超过
Vmax/20到再次跌回阈值的整段时间内,以该段观测到的Vpeak决定。 - 如果长时间不跌回阈值,会按中间检查时间做临时判断。
文档还提供了 packet type 3 的限制表查询协议:
| 包 | 方向 | 重点字段 |
|---|---|---|
| 请求 | 外部设备 -> 机器人 | packet type 3、version 1、axis 1..9、limit type 0=velocity/1=acceleration/2=jerk |
| 响应 | 机器人 -> 外部设备 | packet type 3、version 1、axis、limit type、Vmax、中间检查时间、无负载 20 档、最大负载 20 档 |
当前实现没有 packet type 3 查询,也没有实现手册描述的动态限制表算法。现阶段规划时长和保守程度主要依赖 replacement 自身参数与现场抓包对齐。
9. 报警与诊断
文档中与实现最相关的报警:
| 报警 | 含义 |
|---|---|
MOTN-600 |
命令序号与机器人期望不一致 |
MOTN-602 |
data format 非法 |
MOTN-603 |
后续命令包未在通信周期内到达 |
MOTN-604 |
命令包过多,超出缓冲 |
MOTN-605 |
目标位置包含 NaN 或 infinity |
MOTN-606 |
非 AUTO 或 override 不是 100% |
MOTN-607 |
协议版本不匹配 |
MOTN-609/610/611 |
速度、加速度、jerk 超限 |
MOTN-612/613/614 |
接近速度、加速度、jerk 限制 |
MOTN-617 |
目标点与当前位置不连续 |
MOTN-619 |
当前机型不支持笛卡尔目标格式 |
PRIO-023 |
读写的 IO 类型或索引未分配 |
当前 ControllerStateSnapshot.ActiveAlarms 仍为空,Web 状态页也尚未接入 FANUC 报警列表。后续现场联调如果出现报警,应优先按上述表格关联 J519 包序号、目标数据、IO 字段、发送间隔和状态包 bit。
10. 与当前代码的结论
已基本对齐:
- UDP 60015、大端、start/stop 控制包、64B command packet、132B status packet 的基础二进制布局。
Data format=1的关节目标下发。- 状态位 bit0..bit3 的解析和快照暴露。
- 规划输出
rad转 J519deg。 - 根据
speed_ratio做运行期时间轴缩放,而不是改变规划文件时间。 - 飞拍 IO 触发通过 command packet 的写 IO 字段下发。
- 命令发送按机器人 UDP status 包驱动,并使用最新 status sequence 回发。
主要差异/风险:
- 当前未实现命令缓冲预填,也未读取
$PKT_STACK / $START_MOVE。 - 当前停止运动依赖 packet type
2stop 控制包,没有稳定发送LastData=1的最后 command packet;这与手册标准结束语义不同。 - 当前未实现 packet type
3的速度/加速度/jerk 限制表查询,也未实现 payload/speed 20 档动态限制算法。 - 当前没有自动校验
AUTO / 100% OVERRIDE / brake control / resume offset / payload等控制器前置状态。 - 当前没有报警码读取和
MOTN-* / PRIO-*映射。
建议后续联调优先级:
- 验证运动结束是否必须补
LastData=1;如果当前 stop 控制包能稳定工作,也应在文档中标为现场兼容路径,而不是手册标准路径。 - 抓一次报警现场包,确认
MOTN-600/603/617等是否能从包序号与状态位直接定位。 - 如果后续追求更稳的真实机运行,补 packet type
3限制表查询,并把规划器的速度、加速度、jerk 校验与 FANUC 手册算法靠近。