robonix.robot.agilex.piper_linkerhand_mujoco
robot
v0.1.0
AgileX Piper 6-DOF arm + LinkerHand O6 dexterous hand (right), MuJoCo simulation body package for Robonix. The skill drives the sim process via its HTTP API (control/state/sim endpoints).
Piper + LinkerHand O6 MuJoCo 仿真本体接入 Robonix
按 Robonix《接入 MuJoCo 仿真本体》指南
将 piper_linker_sim/ (Piper 6 轴臂 + LinkerHand O6 右灵巧手) 接入 Robonix。
场景是一个工作台式抓放单元: 台面上放 3 个任务物体 (红色方块 / 绿色长方体 /
蓝色圆柱) 和 3 处放置区标记 (左/中/右), 两台相机看着工作区。通过 rbnx chat
可以直接问"你有哪些技能"、"桌面上有什么", 并说"抓起红色方块"、"放到右边"。
双层架构(ROS 2 走 Docker Humble,本机是 Ubuntu 24.04):
浏览器 viewer ← MJPEG │ piper_linker_sim (原生, Python 3.13, :8010)
│ ├── manipulation.py 逆解 + 抓取/放置状态机
│ └── /objects /camera/<n>/frame.jpg /manipulation/*
│ WebSocket 客户端 (bridge_client.py, 30Hz state)
▼ ws://127.0.0.1:8775 (8765 被本机其它项目占用)
robonix_ros_wyh 容器 (ROS 2 Humble, ROS_DOMAIN_ID=23)
├── bridge_node.py sim/bridge_node.py → /joint_states /arm/end_pose /clock
│ /arm/joint_command /arm/pos_command /sim/reset
│ /sim/task_objects
└── robot_state_publisher → /tf (urdf/piper_linker_o6_right.urdf 全树)
▲
rbnx boot: skill piper_linker + primitive piper_linker_arm
用户可调用的能力
| 说什么 | 走哪个 capability | 类型 |
|---|---|---|
| 你有哪些技能 / 你能做什么 | robonix/skill/manipulation/list_skills |
同步 |
| 桌面上有什么 | robonix/skill/manipulation/scene_query |
同步 |
| 抓起红色方块 | robonix/skill/manipulation/pick_object |
异步 |
| 放到右边 | robonix/skill/manipulation/place_object |
异步 |
| 握拳 / 前伸 / 把 joint2 转到 2 弧度 / 查状态 / 复位 | hand_gesture / arm_pose / set_joint / sim_state / sim_reset |
同步 |
pick_object 与 place_object 各自注册 <id>/status 和 <id>/cancel, Executor
据此把它们判定为异步能力: 调用立即返回 run_id, 后台跑完整段运动, 每 2s 轮询
状态直到终态。一次完整"抓起来→放到右边"约 10s 仿真时间。
详细能力说明、抓取实现细节和已知边界见
skills/piper_linker/CAPABILITY.md。
目录结构
<仓库根>/ # 容器挂载点 /workspace/mujoco
├── robonix_manifest.yaml # 部署清单 (system + primitive + skill)
├── soma.yaml # 本体语义结构 (arm + dexterous_hand + 9 个能力)
├── start.sh / stop.sh / build.sh
├── .env.example # VLM 配置模板
├── LICENSE # MulanPSL-2.0
├── LICENSES/ # AgileX MIT / LinkerHand Apache-2.0
├── UPSTREAM.md # 上游 commit、许可、本仓库分发了哪些文件
├── sim/
│ └── bridge_node.py # WebSocket↔ROS 2 bridge (参考包裁剪: 仅臂)
├── ros/
│ └── run_bridge.sh # 容器内启动 bridge + robot_state_publisher
├── urdf/
│ ├── make_urdf.py # 合并 URDF 生成脚本 (Piper 去夹爪 + 手挂 link6+17.5mm)
│ └── piper_linker_o6_right.urdf
├── primitives/
│ ├── arm/ # piper_linker_arm 原语 (ROS topic 能力)
│ └── common/ # 参考包的通用 provider 模块
├── scripts/ # primitive 编排 (build/start/stop, docker exec 进容器)
├── tests/ # 确定性集成场景 (Robonix fake VLM 驱动, 见 tests/README.md)
├── skills/
│ └── piper_linker/ # 仿真本体 skill (MCP ↔ 仿真 HTTP API)
│ ├── capabilities/*.toml # 14 个 contract 声明
│ ├── capabilities/lib/… # srv IDL
│ └── piper_linker/main.py
└── piper_linker_sim/ # MuJoCo 仿真本体 (场景 + 机器人模型 + HTTP API)
├── scene.xml # 台面 + 3 个任务物体 + 3 个放置区 + 2 相机
├── piper/ # Piper MJCF + 84 个网格 (AgileX 上游, 未改)
├── linkerhand_o6_right/ # O6 右手 MJCF + 12 个网格
├── server.py # HTTP API + bridge 客户端 + 渲染
├── manipulation.py # 抓放状态机 + IK
├── tools/ # 回归/标定工具 (pick_place_matrix 等)
├── PROVENANCE.md # 逐文件溯源与修改清单
└── README.md
reference/(syswonder 参考包副本)与上游 cloneagx_arm_sim/、linkerhand-urdf/仅保留在本地工作目录,已由.gitignore排除,见UPSTREAM.md。
使用
cd <本仓库根目录>
cp .env.example .env && vim .env # 填 VLM_API_KEY
bash build.sh # rbnx build (codegen, 生成 MCP 类型)
bash start.sh # 容器 bridge → 仿真 → rbnx boot
# 另开终端
rbnx chat
rbnx chat 示例指令:
- "你有哪些技能"
- "桌面上有什么" / "桌子上有几个东西"
- "把红色方块抓起来" / "抓起蓝色圆柱"
- "放到右边" / "放到左边" / "放到中间"
- "张开手掌" / "握拳" / "比个 OK" / "点赞"
- "机械臂前伸" / "回到初始姿态" / "把 joint2 转到 2 弧度"
- "查询仿真状态" / "复位仿真"
仿真画面: 浏览器打开 http://<本机IP>:8010 (MJPEG viewer)。
场景与相机
台面正好在 z=0, 与机械臂底座同一平面(臂坐在台面上,不是站在地上);
范围 x∈[-0.12,0.78] y∈[-0.36,0.36]。台面上分两排:
- 3 个任务物体在
x=0.555一排:y=-0.255 / -0.075 / +0.105,间距 18cm; 尺寸 5×5×9cm(蓝色圆柱 Ø5×9cm),质量 0.05~0.06kg; - 3 个放置区在
x=0.40一排:y=+0.20 / 0.00 / -0.20,间距 20cm; 标记是 6×6cm 的纯视觉几何 (contype=0),不参与物理。
尺寸、间距、位置都是照着这只手定的(平抓姿态下张开的拇指会向掌侧扫出约 12cm,
排得太密会把旁边的物体扫倒),推导过程见
piper_linker_sim/README.md。
| 相机 | 位置 | 用途 |
|---|---|---|
table_cam |
台面左前上方, 注视台面中心 | 工作区俯视, 桌面物体一览 |
front_cam |
台面正前方 | 正面视角 |
curl -o /tmp/table.jpg http://127.0.0.1:8010/camera/table_cam/frame.jpg
curl http://127.0.0.1:8010/camera/table_cam/info # 内参 K + 外参
真机上这两个相机对应 robonix/primitive/camera/{rgb,snapshot,intrinsics,extrinsics};
本部署暂未接 ROS 相机 primitive (见"已知边界")。
ROS 2 链路验证 (已通过)
# 容器内查看话题
docker exec robonix_ros_wyh bash -c 'source /opt/ros/humble/setup.bash && \
export ROS_DOMAIN_ID=23 && ros2 topic list'
# 下发关节命令 (12 驱动关节按名映射, 联动关节自动跟随)
docker exec robonix_ros_wyh bash -c 'source /opt/ros/humble/setup.bash && \
export ROS_DOMAIN_ID=23 && ros2 topic pub --rate 20 /arm/joint_command \
sensor_msgs/msg/JointState "{name: [joint2, rh_index_mcp_pitch], position: [2.4, 1.2]}"'
- /joint_states: 17 关节 (6 臂 + 11 手) @30Hz, sim 时间戳
- /arm/joint_command → 仿真执行器 (验证: joint2→2.4, 食指→1.2, DIP=0.89×MCP 联动)
- /tf: base_link→…→rh_hand_base_link→手指 全树
关节映射 (ctrl 数组, 12 通道)
| 下标 | 通道 | 限位 (rad) |
|---|---|---|
| 0-5 | joint1..joint6 (臂) | 见 MJCF piper.xml |
| 6 | rh_thumb_cmc_yaw (拇指旋转) | 0 – 1.36 |
| 7 | rh_thumb_cmc_pitch (拇指弯曲) | 0 – 0.58 |
| 8-11 | rh_{index,middle,ring,pinky}_mcp_pitch | 0 – 1.6 |
手部 0=张开, 上限=最大弯曲; DIP/IP 联动由仿真内 equality 约束实现 (拇指 1.86×, 四指 0.89×)。 qpos 顺序(38): joint1-6, 手 11 关节, 之后是 3 个任务物体的 freejoint (7 维/个)。
URDF mesh 包位置
URDF 使用 package:// 引用网格 (robot_state_publisher 只需运动学, 不加载网格):
package://piper_description/→piper_linker_sim/piper/package://linkerhand_o6_right/→linkerhand-urdf/O6/right/(该 clone 未随仓库分发,见 UPSTREAM.md)
重新生成 URDF: python3 urdf/make_urdf.py (默认右手; 加 --side left 可生成左手合并 URDF)
端到端验证
两条路都跑通了:
真实 VLM(rbnx ask,与 rbnx chat 同一条 gRPC 链路):
| 用户说法 | 结果 |
|---|---|
| 你有哪些技能 | ✅ 自己去读了 CAPABILITY.md,返回 8 项技能 |
| 桌面上有什么 | ✅ 3 个物体的名称/形状/尺寸/坐标 |
| 把红色方块放到右边 | ✅ pick → place → 又用 scene_query 复核落点,报"已完成" |
真值坐标以当前场景为准:上面这次记录产生于早期布局,坐标已失效。 可复现的核对方式是跑
tools/pick_place_matrix.py,它直接比对落点与放置区中心的 水平误差(最近一次实跑见下)。
确定性 fake VLM:tests/ 下的三个场景(跑法见
tests/README.md),用于把 VLM 这一层排除掉、只验证 skill →
仿真链路。另有 3 物体 × 3 放置区的全遍历:tools/pick_place_matrix.py。最近一次实跑(当前布局):9/9 成功,
落点误差 1.1~2.1cm,单次任务 26.7~36.1s 仿真时间。
已知边界 (下一步)
- VLM 要选对(.env 里的
VLM_*)。踩过的三个坑,按报错对号入座: couldn't produce a valid robot plan after retrying once+ 模型回复"我是 Kiro…" → 网关把请求转到了带自带人设的后端(本机api.ikuncode.cc的claude-opus-*就是这套),系统提示被顶掉。换网关或换模型。VLM stream error: open VLM chat stream→ 传输层失败,不是协议问题。 先curl一下${VLM_BASE_URL}/chat/completions看端点通不通。- 推理模型别设
max_tokens:deepseek-flash是推理模型,token 预算会先被 reasoning 吃光,content返回空字符串,Pilot 拿到空回复。Robonix 不传max_tokens,所以直接用它没问题;自己写探针时容易踩。 - 本机验证通过的配置:
VLM_BASE_URL=https://api.deepseek.com/v1+VLM_MODEL=deepseek-flash(或deepseek-chat)。 备选:CC Switch 代理http://10.29.0.196:15721/v1+gpt-6-astra(该代理只声明这一个模型,实测也能出合规 RTDL)。 - 一个 provider 只能有一个异步能力: Executor 靠
<contract_id>/status+/cancel判定异步,而 MCP 工具名取 contract_id 的最后一段,所以同一个 skill 里两个异步能力的 status 工具同名。这里 pick/place 各注册一份、实现共用 (任务号在仿真侧全局),行为一致;要接第三个异步能力就得拆成独立 provider。 - build 需要 grpcio-tools:
rbnx codegen生成 Python proto 桩代码时要求 PATH 里的python3能 importgrpc_tools。build.sh会自动在 仓库根.venv/bin/python3(其次$HOME/miniconda3/bin/python3)等位置找一个装了的; 若都没有会跳过桩代码生成。 - 相机只到 HTTP, 未接 ROS 相机 primitive: 仿真侧有内参/外参/JPEG 快照,
但没有发布
sensor_msgs/Image。要接 Robonix 的system/scene语义地图服务, 需要在 bridge 里加一路图像发布 + 起 scene 容器 (重, 需 GPU + YOLO/MobileSAM)。 当前"桌面上有什么"由 skill 直接返回仿真真值, 比视觉更准, 但不走 scene 服务。 - 抓持是真实的手指摩擦夹持,没有吸附或约束兜底: 指关节位置伺服合拢时被物体挡住、 停在半路压出法向力,靠摩擦把物体提起来;物理解算与碰撞检测全程开启,抓不住就是真失败。 判定阈值(接触点数、法向力、抬起跟随)见 CAPABILITY.md 的"抓取是怎么实现的"。
- 容器重建流程: 镜像
robonix-ros:humble-wyh已含全部依赖 (commit 持久化),docker run -d --name robonix_ros_wyh --net host \ -v "$PWD:/workspace/mujoco" -v <robonix 源码路径>/pylib:/robonix/pylib:ro \ robonix-ros:humble-wyh sleep infinity - 完整参考包克隆 (GitHub 网络) 失败过一次, 关键文件已存于本地
reference/(该目录 未随仓库分发,见UPSTREAM.md)
许可与来源
- 本项目自身代码:MulanPSL-2.0,见
LICENSE。 - 随仓库分发的第三方模型数据(AgileX Piper 的网格与 MJCF、LinkerHand O6 的网格):
许可文本在
LICENSES/;上游 commit、逐文件字节级校验结果、以及本仓库 实际分发了哪些文件,见UPSTREAM.md;逐文件修改清单见piper_linker_sim/PROVENANCE.md。 - Robonix catalog 身份:
robonix.robot.agilex.piper_linkerhand_mujoco(声明在robonix_manifest.yaml的catalog:块)。
⚠️ 上游
agx_arm_sim只在 README 里写了 "MIT License" 而没有 LICENSE 文件;LICENSES/AgileX-MIT.txt的版权行取自 AgileX 同厂商仓库piper_sdk。如需权威 署名请向 AgileX 确认,详见UPSTREAM.md。