Files
FlyShotHost/docs/python-client-interface-reverse-engineering.md
yunxiao.zhu af65ca03a0 feat(compat): 补齐飞拍执行等待与 FANUC 状态驱动链路
- 为 ExecuteFlyShotTraj 补齐 wait 语义,并让 move_to_start
  先完成临时 PTP 运动后再启动正式飞拍轨迹
- 将 J519 命令发送改为由机器人 UDP status sequence 驱动,
  避免在未收到状态包时主动发周期命令
- 将 10010 状态通道关节字段统一按 JointRadians 命名,
  同步更新运行时读取逻辑与协议测试
- 新增 FANUC 10010 状态帧、流运动手册和 Python client
  逆向文档,并更新 README 与兼容需求说明
- 补充兼容层编排测试与 HTTP 集成测试,覆盖 wait 和
  move_to_start 串行化行为
2026-05-03 19:29:31 +08:00

8.7 KiB
Raw Blame History

Python ControllerClient 接口逆向记录

背景

本记录用于确认旧 PyControllerClient 对 Python 暴露了哪些接口,尤其确认是否能通过 Python client 直接查询或设置旧服务端运行态 JointLimits

复核对象:

../flyshot-uaes-interface/lib/PyControllerClient.cpython-37m-x86_64-linux-gnu.so
../FlyingShot/FlyingShot/Lib/PyControllerClient.cpython-37m-x86_64-linux-gnu.so
../flyshot-uaes-interface/lib/libControllerClient.so
../FlyingShot/FlyingShot/Lib/libControllerClient.so
../FlyingShot/FlyingShot/Include/ControllerClient/ControllerClient.h
../FlyingShot/FlyingShot/Include/ControllerClient/Types.h
../flyshot-uaes-interface/UseControllerClient.py
../flyshot-uaes-interface/main.py

两份 Python 扩展与两份底层 client 库哈希一致:

PyControllerClient.cpython-37m-x86_64-linux-gnu.so
SHA256=648CC23CBC6DF83822B58AC4A10211EE1DF8029AD8933D31032187748DF7F4BC

libControllerClient.so
SHA256=6D6FD3F20F0791F1CF11EEE5B1D479E2DCB6A1A2C8AB00A1165575BAB4B62813

因此 flyshot-uaes-interface/libFlyingShot/FlyingShot/Lib 中的 Python client 可视为同一份接口。

暴露的 Python 类型

PyControllerClient 暴露以下类型:

类型 来源 说明
ControllerClient ControllerClient.h TCP JSON client高层控制入口
JointPositions Types.h 关节位置容器,可用 6 维列表构造,也支持下标读写
Pose Types.h TCP/末端位姿容器C++ 侧为 7 元数组
JointLimits Types.h 关节上下限、速度、加速度、jerk 容器
IOType Types.h IO 枚举

IOType 的枚举值:

IOType.kIOTypeDI = 1
IOType.kIOTypeDO = 2
IOType.kIOTypeRI = 8
IOType.kIOTypeRO = 9

ControllerClient 暴露方法

二进制字符串和 C++ 公开头文件交叉确认Python client 暴露的方法为:

Python 方法 典型调用 返回形态 说明
ConnectServer c.ConnectServer(server_ip="127.0.0.1", port=50001) bool 连接旧 50001/TCP+JSON 服务端
GetServerVersion c.GetServerVersion() str Python 包装层把 C++ out 参数折叠成返回值
GetClientVersion c.GetClientVersion() str 获取 client 版本
SetUpRobot c.SetUpRobot("FANUC_LR_Mate_200iD") bool 按机器人名称初始化服务端机器人模型
SetUpRobotFromEnv c.SetUpRobotFromEnv(env_file) bool 从环境文件初始化
IsSetUp c.IsSetUp() bool 判断服务端是否已经初始化机器人
SetShowTCP c.SetShowTCP(is_show=True, axis_length=0.1, axis_size=2) bool 仿真显示 TCP 坐标系
GetName c.GetName() str 获取机器人名称
GetDoF c.GetDoF() int 获取自由度
SetActiveController c.SetActiveController(sim=True) bool 切换仿真/真实控制器
Connect c.Connect("192.168.10.101") bool 连接机器人控制器
Disconnect c.Disconnect() bool 断开机器人控制器
EnableRobot c.EnableRobot() / c.EnableRobot(8) bool 使能机器人,参数为 buffer size
DisableRobot c.DisableRobot() bool 下使能
GetSpeedRatio c.GetSpeedRatio() float 获取执行速度倍率
SetSpeedRatio c.SetSpeedRatio(0.8) bool 设置执行速度倍率
GetTCP res, tcp = c.GetTCP() (bool, Pose) 获取 TCP
SetTCP c.SetTCP(tcp) bool 设置 TCP
GetIO res, value = c.GetIO(port=1, io_type=IOType.kIOTypeDI) (bool, bool) 读取 IO
SetIO c.SetIO(port=1, value=True, io_type=IOType.kIOTypeDO) bool 写 IO
StopMove c.StopMove() bool 停止运动
GetJointPosition res, joints = c.GetJointPosition() (bool, JointPositions) 获取当前关节角
GetPose res, pose = c.GetPose() (bool, Pose) 获取当前末端位姿
GetNearestIK res, ik = c.GetNearestIK(pose, joint_seed=joints) (bool, JointPositions) 按 seed 求最近 IK
MoveJoint c.MoveJoint(joint_positions) bool 关节运动
ExecuteTrajectory c.ExecuteTrajectory(waypoints=[...], method="icsp", save_traj=True) bool 执行普通轨迹
UploadFlyShotTraj c.UploadFlyShotTraj(name, waypoints, shot_flags, offset_values, addrs) bool 上传飞拍轨迹
DeleteFlyShotTraj c.DeleteFlyShotTraj(name) bool 删除飞拍轨迹
ListFlyShotTraj c.ListFlyShotTraj() list[str] 列出已上传飞拍轨迹
ExecuteFlyShotTraj c.ExecuteFlyShotTraj(name, move_to_start=True, method="icsp", save_traj=True) bool 执行飞拍轨迹
SaveTrajInfo c.SaveTrajInfo(name, method="icsp") bool 保存规划结果到 ~/Rvbust/Data
IsFlyShotTrajValid valid, time = c.IsFlyShotTrajValid(name, method="icsp", save_traj=True) (bool, float) 检查飞拍轨迹是否合法并返回规划时长

没有暴露的关键接口

本轮重点确认Python client 暴露方法中没有看到:

GetJointLimits
SetJointLimits
_GetJointLimits
_SetJointLimits

虽然 PyControllerClient 绑定了 JointLimits 类型,并且 libControllerClient.so 中存在 JointLimits 的输出运算符符号,但公开 ControllerClient 方法表中没有任何接收或返回 JointLimits 的 client 入口。

这和旧服务端二进制不同。旧服务端 ControllerServer.cpython-37m-x86_64-linux-gnu.so 中能看到:

ControllerServer.ControllerServer._GetJointLimits
ControllerServer.ControllerServer._SetJointLimits
ControllerServer.ControllerServer._IsWaypointInJointLimits
ControllerServer.ControllerServer._IsTrajInJointLimits
ControllerServer.ControllerServer._IsTrajInJerkLimits

因此当前判断是:

Python client 公开 API 不能直接抓 runtime JointLimits
runtime JointLimits 查询能力存在于旧服务端内部,而不是 PyControllerClient 公开接口中。

UAES Python 服务实际使用的接口

../flyshot-uaes-interface/main.py 只使用了公开 client 方法:

  • ConnectServer
  • SetUpRobot
  • IsSetUp
  • EnableRobot
  • DisableRobot
  • SetActiveController
  • Connect
  • GetName
  • GetServerVersion
  • GetDoF
  • GetSpeedRatio
  • SetTCP
  • GetTCP
  • SetIO
  • GetJointPosition
  • MoveJoint
  • ListFlyShotTraj
  • UploadFlyShotTraj
  • ExecuteFlyShotTraj
  • SetSpeedRatio
  • DeleteFlyShotTraj
  • GetPose

其中 /execute_flyshot/ 调用:

c.ExecuteFlyShotTraj(name=name, move_to_start=True, method="icsp", save_traj=True)

/set_speedRatio/ 调用:

c.SetSpeedRatio(speed)

没有看到 UAES 服务通过 Python client 设置或查询 JointLimits

2026-04-30 追加 50001/TCP+JSON 抓包复核后,这个判断进一步收敛。all-50001-plan.pcap 中已经抓到两次真实规划/执行请求:

{"cmd":"SetSpeedRatio","ratio":0.5}
{"cmd":"ExecuteFlyShotTraj","method":"icsp","move_to_start":true,"name":"UTTC_MS11_TEST01","save_traj":true,"use_cache":false,"wait":true}
{"cmd":"SetSpeedRatio","ratio":1.0}
{"cmd":"ExecuteFlyShotTraj","method":"icsp","move_to_start":true,"name":"UTTC_MS11_TEST01","save_traj":true,"use_cache":false,"wait":true}

请求中仍没有 JointLimits / acc_limit / jerk_limit / velocity / acceleration / jerk。因此公开 Python client 与公开 50001 JSON 都没有把规划限制作为参数传给 ExecuteFlyShotTraj

另外,main.py/execute_trajectory/ 中出现:

c.yrxm(waypoints=joint_positions, method='icsp', save_traj=True)

yrxm 不在 PyControllerClient 暴露方法表中,按上下文应是 ExecuteTrajectory 的笔误;这条不影响飞拍主路径 /execute_flyshot/

对当前时长差异调查的含义

如果要抓旧系统规划时使用的 effective vel/acc/jerk,优先级应调整为:

  1. 在旧服务端进程内直接调用或插桩 _GetJointLimits
  2. 或者逆向 50001/TCP+JSON 的 hidden command envelope再尝试发送 GetJointLimits / _GetJointLimits
  3. 不应指望现有 PyControllerClient.ControllerClient 直接提供 GetJointLimits

如果短期内无法进入旧服务端内部,新系统不再继续等待这份不可见状态;设计上使用 replacement-only 的内部规划约束参数补齐,优先限制规划加速度,例如 planning_acceleration_scale。该参数必须标注为新系统校准值,不能写成旧 Python client 或旧 50001 JSON 的公开字段。

最小现场验证脚本可以先确认 Python client 暴露面:

from PyControllerClient import ControllerClient

c = ControllerClient()
names = [x for x in dir(c) if "Limit" in x or "limit" in x]
print(names)

按当前二进制逆向,预期不会出现 GetJointLimits / SetJointLimits