Add G20 calibration assets, L20 compare, and refresh README.

Sync calibrated G20 URDF/MJCF/JSON lookup, L20 V10.1 dual-compare scripts,
joint GUI, and document O6/G20/L20 startup on the Gitea repo.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-07 09:49:55 +08:00
parent 246b48b1fb
commit 325dff6870
53 changed files with 33379 additions and 651 deletions
+13
View File
@@ -25,3 +25,16 @@ dist/
# Experiment outputs (regenerable)
reports/
tools/reports/
# Runtime / local junk
MUJOCO_LOG.TXT
**/MUJOCO_LOG.TXT
# Local backup / intermediate calibration dumps (keep named releases, drop *.bak*)
*.bak
*.bak_*
**/*.bak
**/*.bak_*
**/*.urdf.bak*
**/*.xml.bak*
**/*.json.bak*
+139 -207
View File
@@ -1,15 +1,22 @@
# LinkerHand O6 MuJoCoROS2启动说明
# Mujoco LinkerBotROS 2
本机跑 MuJoCo 左手仿真,可用本机或另一台电脑通过 ROS2 话题控制。
Linker Hand 的 MuJoCo + ROS 2 仿真工作区。支持:
- **O6**:经典 6 通道 0~255 控制(launch
- **G20 左手**PLAN 桥接、标定查表、MJCF/URDF 零位同步
- **L20 V10.1 左手**:与 G20 双窗对比
- 关节弧度监视 GUI、曲线录制与实验脚本
远程仓库:<https://gitea.robotquan.com/sunxianghui/mujoco_linkerbot.git>
---
## 环境要求
- Ubuntu + ROS 2 **Jazzy**
- 工作区路径:`~/linker_hand_mujoco_ros2`
- Python 依赖在 `venv`(含 `mujoco``PyQt5`
- **注意**`ros2 launch` 使用系统 `/usr/bin/python3`,仅 `source venv` **不够**,需用下文的启动脚本或设置 `PYTHONPATH`
- 工作区路径示例`~/linker_hand_mujoco_ros2`
- Python 依赖在本地 `venv`(含 `mujoco` 等;**不要**把 `venv` 提交进仓库
- `ros2 launch` 使用系统 Python,仅 `source venv` 不够,请用仓库里的启动脚本(会把 `venv/site-packages` 加入 `PYTHONPATH`
---
@@ -22,53 +29,19 @@ colcon build --symlink-install --packages-select linker_hand_mujoco_ros2
source install/setup.bash
```
`venv` 目录已放 `COLCON_IGNORE`,不会被 colcon 误扫。
`venv/` 已放 `COLCON_IGNORE`,不会被 colcon 误扫。
---
## 二、本机启动仿真(推荐
## 二、O6 仿真(原有流程
```bash
cd ~/linker_hand_mujoco_ros2
./run_sim.sh
```
脚本会自动:
- `source` ROS2 与本工作区
- `ROS_DOMAIN_ID=30``ROS_LOCALHOST_ONLY=0`(便于多机)
-`venv``site-packages` 加入 `PYTHONPATH`(解决找不到 `mujoco`
成功后应出现:
1. MuJoCo 3D 窗口(左手 O6
2. Joint 进度条窗口(实时关节角)
### 手动启动(等效)
```bash
cd ~/linker_hand_mujoco_ros2
source /opt/ros/jazzy/setup.bash
source install/setup.bash
export PYTHONPATH="$PWD/venv/lib/python3.12/site-packages:$PYTHONPATH"
ros2 launch linker_hand_mujoco_ros2 linker_hand_mujoco_ros2.launch.py
```
当前 launch 默认:
| 参数 | 值 |
|------|-----|
| `hand_type` | `left` |
| `hand_joint` | `O6` |
| 控制话题 | `/cb_left_hand_control_cmd` |
---
## 三、控制话题说明
- 话题:`/cb_left_hand_control_cmd`
- 控制话题:`/cb_left_hand_control_cmd`
- 类型:`sensor_msgs/msg/JointState`
- `position`**6 个数**,范围**0255**
- `position`6 个数**0~255****255≈张开,0≈握紧**
| 索引 | 含义 |
|------|------|
@@ -79,9 +52,119 @@ ros2 launch linker_hand_mujoco_ros2 linker_hand_mujoco_ros2.launch.py
| 4 | 无名指弯曲 |
| 5 | 小指弯曲 |
约定(当前映射):约 **255 = 张开,0 = 握紧**。远指节由 mimic 跟随近指节
更多 O6 / 多机 DDS / 曲线录制说明见下文「附录:O6 与多机」
### 本机发一帧(握拳示例)
---
## 三、G20 左手仿真(推荐)
### 3.1 一键启动
```bash
./run_g20_mujoco_plan.sh
```
默认行为:
| 项 | 值 |
|----|-----|
| 模型 | `urdf/G20/linker_hand_g20_left/linker_hand_g20_left.xml` |
| 控制话题 | `/g20/cb_left_hand_control_cmd` |
| 状态话题 | `/sim/mujoco/g20/left/joint_state` |
| 指令 | 20 维 0~255(与真机 SDK 通道对齐) |
| 映射 | 标定 JSON 查表 + URDF 被动 mimic |
| 速度 | 约 1.35× 墙钟,可用 `--speed` / `--slew-rad-s` 调整 |
常用参数示例:
```bash
./run_g20_mujoco_plan.sh --speed 1.0
./run_g20_mujoco_plan.sh --cmd-topic /g20/cb_left_hand_control_cmd
./run_g20_mujoco_plan.sh --mapping g20_calibrated
```
### 3.2 关节弧度 GUI
```bash
./run_g20_joint_gui.sh
```
默认订阅 `/sim/mujoco/g20/left/joint_state`,按手指分组显示 hinge 弧度(默认 6 位小数)。
### 3.3 G20 + L20 双窗对比
```bash
./run_dual_mujoco_compare.sh
```
- 左窗:当前标定 **G20**`g20_calibrated`
- 右窗:**L20 V10.1** 原版 URDF 映射(`l20_urdf`,默认关 collision project,避免自碰卡住)
- 共用同一条 0~255 控制话题(默认 `/g20/cb_left_hand_control_cmd`
---
## 四、标定与模型文件(G20
目录:
`src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/G20/linker_hand_g20_left/`
| 文件 | 说明 |
|------|------|
| `linkerhand_g20_left.urdf` | 当前使用的 URDF(含投影零位修正) |
| `linker_hand_g20_left.xml` | 当前使用的 MJCF(与 URDF 原点/限位同步) |
| `g20_left_G20_LEFT_001_calibration.json` | 运行时 0~255→弧度查表(schema 3 |
| `meshes/` | 网格 |
映射实现:`utils/g20_joints.py`
要点:
1. **主动关节**:用标定 JSON 的 `angle_rad[0..255]` 查表。
2. **零位**:用 `zero_angles.table_projected_zero_rad` 相对夹具参考修正 URDF/MJCF `origin`(相机投影零位,非完整外参优化)。
3. **被动关节**`thumb_ip`、四指 DIP 走 URDF mimicJSON 里 `thumb_ip` 是累计投影角,**不能**直接当相对关节角。
4. **四指侧摆**:运行时符号与真机约定见 `G20_FLIP_JOINT_SIGN`
当前已知:张开大姿态已基本对齐;拇指 CMC 零位仍可能有小量前探/拧向残差。更稳妥的做法是解耦重测三个 CMC 投影零位,或导出 TAG 全位姿后做原点优化,而不是肉眼加减角度。
---
## 五、主要源码入口
| 路径 | 说明 |
|------|------|
| `linker_hand_mujoco_ros2/g20_plan_bridge.py` | G20/L20 MuJoCo PLAN 桥 |
| `linker_hand_mujoco_ros2/g20_joint_monitor_gui.py` | 关节弧度监视 GUI |
| `linker_hand_mujoco_ros2/utils/g20_joints.py` | G20 标定查表与映射 |
| `launch/linker_hand_mujoco_ros2.launch.py` | O6 launch |
| `urdf/O6/` | O6 MJCF |
| `urdf/G20/` | G20 URDF/MJCF + 标定 |
| `urdf/L20_V10.1/` | L20 V10.1 URDF/MJCF |
| `tools/` | 曲线录制、实验脚本 |
Console 入口(`setup.py`):`g20_plan_bridge``g20_joint_monitor_gui` 等。
---
## 六、克隆与推送
```bash
git clone https://gitea.robotquan.com/sunxianghui/mujoco_linkerbot.git
cd mujoco_linkerbot
# 自建 venv 并安装 mujoco 等依赖后,再 colcon build
```
本仓库 `origin` 指向上述 Gitea 地址。提交后:
```bash
git push -u origin main
```
---
## 附录:O6 与多机
### 本机发一帧(O6 握拳示例)
```bash
source /opt/ros/jazzy/setup.bash
@@ -97,187 +180,36 @@ ros2 topic pub --once /cb_left_hand_control_cmd sensor_msgs/msg/JointState "{
}"
```
张开:
### 两台电脑共用 ROS 2
```bash
ros2 topic pub --once /cb_left_hand_control_cmd sensor_msgs/msg/JointState "{
header: {stamp: {sec: 0, nanosec: 0}, frame_id: ''},
name: [],
position: [255.0, 255.0, 255.0, 255.0, 255.0, 255.0],
velocity: [],
effort: []
}"
```
---
## 四、两台电脑共用 ROS2
ROS2 **没有** ROS1 的 `roscore`。两边约定同一个 Domain 即可。
### 两边都设置
两边约定同一 Domain
```bash
export ROS_DOMAIN_ID=30
export ROS_LOCALHOST_ONLY=0
```
### 电脑 A:仿真机
仿真机跑 `./run_sim.sh``./run_g20_mujoco_plan.sh`;控制机 `ros2 topic list` / `hz` / `echo` 检查。
```bash
cd ~/linker_hand_mujoco_ros2
./run_sim.sh
```
### 电脑 B:控制机(发指令 / handretarget / SDK
### 通用曲线录制
```bash
source /opt/ros/jazzy/setup.bash
export ROS_DOMAIN_ID=30
export ROS_LOCALHOST_ONLY=0
# 确认话题与收发端
ros2 topic list
ros2 topic info /cb_left_hand_control_cmd -v
```
`topic info -v` 中应能看到:
- **Publisher**:例如 `handretarget_node`
- **Subscription**:至少包含 `linker_hand_mujoco_ros2_node`
### 查是否真的在发数据
有 Publisher ≠ 正在发数据。请用:
```bash
ros2 topic hz /cb_left_hand_control_cmd
ros2 topic echo /cb_left_hand_control_cmd
```
- `hz` 无输出 → 对端没在 publish
- `echo --once` 会一直等到有一帧;无数据时像卡住,优先用上面两条
### 跨机能看见话题但收不到数据时
1. 确认两边 `ROS_DOMAIN_ID``ROS_LOCALHOST_ONLY=0` 一致,并重启节点
2. 互相 `ping` 通;尽量同一网段 / 有线;关闭路由器 **AP 隔离**
3. 本机 `ufw` 可先确认:`sudo ufw status`(不活动则不是防火墙)
4. 需要时可配置 CycloneDDS 静态 Peer(两边 IP 写入 `~/cyclonedds.xml`),然后:
```bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export CYCLONEDDS_URI=file://$HOME/cyclonedds.xml
```
---
## 五、常用自检命令
```bash
# 节点是否在
ros2 node list
# 话题列表(仿真未启动时可能还没有 /cb_left_hand_control_cmd
ros2 topic list
# 订阅/发布数量与节点名
ros2 topic info /cb_left_hand_control_cmd -v
# 频率 / 内容
ros2 topic hz /cb_left_hand_control_cmd
ros2 topic echo /cb_left_hand_control_cmd
```
---
## 六、常见问题
### `ModuleNotFoundError: No module named 'mujoco'`
`ros2 launch` 没用到 venv。请用:
```bash
./run_sim.sh
```
或设置 `PYTHONPATH`(见上文「手动启动」)。
### `topic list` 只有 `/rosout`,没有左手话题
仿真节点未运行。先 `./run_sim.sh`,再 `ros2 topic list`
### 改左右手
编辑:
`src/linkerhand-sim/linker_hand_mujoco_ros2/launch/linker_hand_mujoco_ros2.launch.py`
- 左手:`hand_type: 'left'` → 话题 `/cb_left_hand_control_cmd`
- 右手:`hand_type: 'right'` → 话题 `/cb_right_hand_control_cmd`
改完后若未用 symlink,再执行一次 `colcon build --symlink-install`
---
## 七、通用曲线录制(仿真 / 真机同一套话题)
**只传一个文件给对端即可:**
`tools/hand_curve_recorder_standalone.py`
两边都要:已 `source` ROS2、`numpy``matplotlib`
```bash
source /opt/ros/jazzy/setup.bash
export ROS_DOMAIN_ID=30
export ROS_LOCALHOST_ONLY=0
# 仿真端
python3 tools/hand_curve_recorder_standalone.py --hand left --channel 3 --label sim
# 真机端(把该 py 拷过去后)
python3 hand_curve_recorder_standalone.py --hand left --channel 3 --label real
```
对端发 `/cb_left_hand_control_cmd`;本机 **Ctrl+C**`reports/hand_curves/` 下生成 csv + 三曲线图。
### 常见问题
默认还订 `/cb_left_hand_state``position`=0255`effort` 可放电流)。话题名不同时:
**`ModuleNotFoundError: No module named 'mujoco'`**
`./run_sim.sh` / `./run_g20_mujoco_plan.sh`,或手动把 `venv/lib/python3.12/site-packages` 加入 `PYTHONPATH`
```bash
python3 hand_curve_recorder_standalone.py --state-topic /你的状态话题 --channel 3 --label real
```
通道:`0拇指弯 1拇指横摆 2食指 3中指 4无名指 5小指`
**L20 双窗对比时手不动**
自碰投影会把目标打回;`run_dual_mujoco_compare.sh` 已对 L20 默认 `--no-collision-project`
---
要「ROS 发指令 + 仿真里出 q/v/τ 图」:
## 许可与归属
```bash
cd ~/linker_hand_mujoco_ros2
source /opt/ros/jazzy/setup.bash
export ROS_DOMAIN_ID=30
export ROS_LOCALHOST_ONLY=0
# 自测:订话题 + 本地 MuJoCo + 自动发中指慢扫 + 可选 viewer
./venv/bin/python tools/ros_mujoco_record_qvt.py \
--hand left --finger middle --viewer --self-sweep
# 对端发指令时:去掉 --self-sweep
./venv/bin/python tools/ros_mujoco_record_qvt.py \
--hand left --finger middle --viewer
# Ctrl+C → reports/O6_middle_ros/*_qvt.png
```
---
## 八、文件与模型位置
| 路径 | 说明 |
|------|------|
| `./run_sim.sh` | 本机一键启动 |
| `src/.../launch/linker_hand_mujoco_ros2.launch.py` | 左右手 / 型号 |
| `src/.../urdf/O6/` | O6 左右手 MJCF(来自 mujoco_testwork 验证模型) |
| `src/.../utils/joint_monitor.py` | Joint 进度条 UI |
| `venv/` | Python 依赖(勿放进 colcon 扫描,已 IGNORE |
包内模型与标定数据来自 Linker Hand / 本机标定流程;请按公司内部仓库规范使用与分发。
+8 -279
View File
@@ -1,283 +1,12 @@
# LinkerHand O6 MuJoCoROS2启动说明
# 启动说明
本机跑 MuJoCo 左手仿真,可用本机或另一台电脑通过 ROS2 话题控制
主文档已合并到仓库根目录 [README.md](./README.md),请以该文件为准
---
快捷入口:
## 环境要求
- Ubuntu + ROS 2 **Jazzy**
- 工作区路径:`~/linker_hand_mujoco_ros2`
- Python 依赖在 `venv` 中(含 `mujoco``PyQt5`
- **注意**`ros2 launch` 使用系统 `/usr/bin/python3`,仅 `source venv` **不够**,需用下文的启动脚本或设置 `PYTHONPATH`
---
## 一、编译(首次或改代码后)
```bash
cd ~/linker_hand_mujoco_ros2
source /opt/ros/jazzy/setup.bash
colcon build --symlink-install --packages-select linker_hand_mujoco_ros2
source install/setup.bash
```
`venv` 目录已放 `COLCON_IGNORE`,不会被 colcon 误扫。
---
## 二、本机启动仿真(推荐)
```bash
cd ~/linker_hand_mujoco_ros2
./run_sim.sh
```
脚本会自动:
- `source` ROS2 与本工作区
- `ROS_DOMAIN_ID=30``ROS_LOCALHOST_ONLY=0`(便于多机)
-`venv``site-packages` 加入 `PYTHONPATH`(解决找不到 `mujoco`
成功后应出现:
1. MuJoCo 3D 窗口(左手 O6
2. Joint 进度条窗口(实时关节角)
### 手动启动(等效)
```bash
cd ~/linker_hand_mujoco_ros2
source /opt/ros/jazzy/setup.bash
source install/setup.bash
export PYTHONPATH="$PWD/venv/lib/python3.12/site-packages:$PYTHONPATH"
ros2 launch linker_hand_mujoco_ros2 linker_hand_mujoco_ros2.launch.py
```
当前 launch 默认:
| 参数 | 值 |
|------|-----|
| `hand_type` | `left` |
| `hand_joint` | `O6` |
| 控制话题 | `/cb_left_hand_control_cmd` |
---
## 三、控制话题说明
- 话题:`/cb_left_hand_control_cmd`
- 类型:`sensor_msgs/msg/JointState`
- `position`**6 个数**,范围约 **0255**
| 索引 | 含义 |
| 场景 | 命令 |
|------|------|
| 0 | 拇指弯曲 |
| 1 | 拇指横摆 |
| 2 | 食指弯曲 |
| 3 | 中指弯曲 |
| 4 | 无名指弯曲 |
| 5 | 小指弯曲 |
约定(当前映射):约 **255 = 张开,0 = 握紧**。远指节由 mimic 跟随近指节。
### 本机发一帧(握拳示例)
```bash
source /opt/ros/jazzy/setup.bash
export ROS_DOMAIN_ID=30
export ROS_LOCALHOST_ONLY=0
ros2 topic pub --once /cb_left_hand_control_cmd sensor_msgs/msg/JointState "{
header: {stamp: {sec: 0, nanosec: 0}, frame_id: ''},
name: [],
position: [0.0, 100.0, 0.0, 0.0, 0.0, 0.0],
velocity: [],
effort: []
}"
```
张开:
```bash
ros2 topic pub --once /cb_left_hand_control_cmd sensor_msgs/msg/JointState "{
header: {stamp: {sec: 0, nanosec: 0}, frame_id: ''},
name: [],
position: [255.0, 255.0, 255.0, 255.0, 255.0, 255.0],
velocity: [],
effort: []
}"
```
---
## 四、两台电脑共用 ROS2
ROS2 **没有** ROS1 的 `roscore`。两边约定同一个 Domain 即可。
### 两边都设置
```bash
export ROS_DOMAIN_ID=30
export ROS_LOCALHOST_ONLY=0
```
### 电脑 A:仿真机
```bash
cd ~/linker_hand_mujoco_ros2
./run_sim.sh
```
### 电脑 B:控制机(发指令 / handretarget / SDK
```bash
source /opt/ros/jazzy/setup.bash
export ROS_DOMAIN_ID=30
export ROS_LOCALHOST_ONLY=0
# 确认话题与收发端
ros2 topic list
ros2 topic info /cb_left_hand_control_cmd -v
```
`topic info -v` 中应能看到:
- **Publisher**:例如 `handretarget_node`
- **Subscription**:至少包含 `linker_hand_mujoco_ros2_node`
### 查是否真的在发数据
有 Publisher ≠ 正在发数据。请用:
```bash
ros2 topic hz /cb_left_hand_control_cmd
ros2 topic echo /cb_left_hand_control_cmd
```
- `hz` 无输出 → 对端没在 publish
- `echo --once` 会一直等到有一帧;无数据时像卡住,优先用上面两条
### 跨机能看见话题但收不到数据时
1. 确认两边 `ROS_DOMAIN_ID``ROS_LOCALHOST_ONLY=0` 一致,并重启节点
2. 互相 `ping` 通;尽量同一网段 / 有线;关闭路由器 **AP 隔离**
3. 本机 `ufw` 可先确认:`sudo ufw status`(不活动则不是防火墙)
4. 需要时可配置 CycloneDDS 静态 Peer(两边 IP 写入 `~/cyclonedds.xml`),然后:
```bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export CYCLONEDDS_URI=file://$HOME/cyclonedds.xml
```
---
## 五、常用自检命令
```bash
# 节点是否在
ros2 node list
# 话题列表(仿真未启动时可能还没有 /cb_left_hand_control_cmd
ros2 topic list
# 订阅/发布数量与节点名
ros2 topic info /cb_left_hand_control_cmd -v
# 频率 / 内容
ros2 topic hz /cb_left_hand_control_cmd
ros2 topic echo /cb_left_hand_control_cmd
```
---
## 六、常见问题
### `ModuleNotFoundError: No module named 'mujoco'`
`ros2 launch` 没用到 venv。请用:
```bash
./run_sim.sh
```
或设置 `PYTHONPATH`(见上文「手动启动」)。
### `topic list` 只有 `/rosout`,没有左手话题
仿真节点未运行。先 `./run_sim.sh`,再 `ros2 topic list`
### 改左右手
编辑:
`src/linkerhand-sim/linker_hand_mujoco_ros2/launch/linker_hand_mujoco_ros2.launch.py`
- 左手:`hand_type: 'left'` → 话题 `/cb_left_hand_control_cmd`
- 右手:`hand_type: 'right'` → 话题 `/cb_right_hand_control_cmd`
改完后若未用 symlink,再执行一次 `colcon build --symlink-install`
---
## 七、通用曲线录制(仿真 / 真机同一套话题)
**只传一个文件给对端即可:**
`tools/hand_curve_recorder_standalone.py`
两边都要:已 `source` ROS2、`numpy``matplotlib`
```bash
source /opt/ros/jazzy/setup.bash
export ROS_DOMAIN_ID=30
export ROS_LOCALHOST_ONLY=0
# 仿真端
python3 tools/hand_curve_recorder_standalone.py --hand left --channel 3 --label sim
# 真机端(把该 py 拷过去后)
python3 hand_curve_recorder_standalone.py --hand left --channel 3 --label real
```
对端发 `/cb_left_hand_control_cmd`;本机 **Ctrl+C**`reports/hand_curves/` 下生成 csv + 三曲线图。
默认还订 `/cb_left_hand_state``position`=0255`effort` 可放电流)。话题名不同时:
```bash
python3 hand_curve_recorder_standalone.py --state-topic /你的状态话题 --channel 3 --label real
```
通道:`0拇指弯 1拇指横摆 2食指 3中指 4无名指 5小指`
---
要「ROS 发指令 + 仿真里出 q/v/τ 图」:
```bash
cd ~/linker_hand_mujoco_ros2
source /opt/ros/jazzy/setup.bash
export ROS_DOMAIN_ID=30
export ROS_LOCALHOST_ONLY=0
# 自测:订话题 + 本地 MuJoCo + 自动发中指慢扫 + 可选 viewer
./venv/bin/python tools/ros_mujoco_record_qvt.py \
--hand left --finger middle --viewer --self-sweep
# 对端发指令时:去掉 --self-sweep
./venv/bin/python tools/ros_mujoco_record_qvt.py \
--hand left --finger middle --viewer
# Ctrl+C → reports/O6_middle_ros/*_qvt.png
```
---
## 八、文件与模型位置
| 路径 | 说明 |
|------|------|
| `./run_sim.sh` | 本机一键启动 |
| `src/.../launch/linker_hand_mujoco_ros2.launch.py` | 左右手 / 型号 |
| `src/.../urdf/O6/` | O6 左右手 MJCF(来自 mujoco_testwork 验证模型) |
| `src/.../utils/joint_monitor.py` | Joint 进度条 UI |
| `venv/` | Python 依赖(勿放进 colcon 扫描,已 IGNORE |
| O6 | `./run_sim.sh` |
| G20 | `./run_g20_mujoco_plan.sh` |
| G20 关节 GUI | `./run_g20_joint_gui.sh` |
| G20 + L20 对比 | `./run_dual_mujoco_compare.sh` |
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
# 同时开两个 MuJoCo:当前标定 G20 + L20 V10.1 原版 URDF
# 共用同一条 0~255 控制话题(默认 /g20/cb_left_hand_control_cmd
#
# 用法:
# ./run_dual_mujoco_compare.sh
# ./run_dual_mujoco_compare.sh --cmd-topic /cb_left_hand_control_cmd
# ./run_dual_mujoco_compare.sh --speed 1.0
set -eo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$ROOT"
PKG_SRC="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2"
G20_XML="$PKG_SRC/urdf/G20/linker_hand_g20_left/linker_hand_g20_left.xml"
L20_XML="$PKG_SRC/urdf/L20_V10.1/linker_hand_l20v101_left/linker_hand_l20v101_left.xml"
if [[ ! -f "$G20_XML" ]]; then
echo "[error] missing G20 MJCF: $G20_XML" >&2
exit 1
fi
if [[ ! -f "$L20_XML" ]]; then
echo "[error] missing L20 MJCF: $L20_XML" >&2
exit 1
fi
EXTRA=("$@")
cleanup() {
[[ -n "${PID_G20:-}" ]] && kill "$PID_G20" 2>/dev/null || true
[[ -n "${PID_L20:-}" ]] && kill "$PID_L20" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
echo "[dual] G20 calibrated xml=$G20_XML"
"$ROOT/run_g20_mujoco_plan.sh" \
--xml "$G20_XML" \
--node-name g20_calibrated \
--window-title "G20 calibrated (current)" \
--sim-state-topic /sim/mujoco/g20/left/joint_state \
--mapping g20_calibrated \
"${EXTRA[@]}" &
PID_G20=$!
# 错开一点,避免两个 GLFW 同时抢显示
sleep 1
echo "[dual] L20 V10.1 stock xml=$L20_XML (URDF-native mapping, no G20 corrections)"
# L20 mesh 弯曲时自碰多,默认 collision_project 会把运动全部打回;对比时关闭。
"$ROOT/run_g20_mujoco_plan.sh" \
--xml "$L20_XML" \
--node-name l20v101_stock \
--window-title "L20 V10.1 stock (URDF only)" \
--sim-state-topic /sim/mujoco/l20v101/left/joint_state \
--mapping l20_urdf \
--no-collision-project \
"${EXTRA[@]}" &
PID_L20=$!
echo "[dual] both started. same cmd topic (default /g20/cb_left_hand_control_cmd)."
echo "[dual] L20: collision_project=off (mesh self-overlap would freeze motion)."
echo "[dual] Ctrl+C stops both."
wait "$PID_G20" "$PID_L20"
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# G20 关节弧度监视 GUI(默认 6 位小数)
# 订阅 /sim/mujoco/g20/left/joint_state
set -eo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$ROOT"
set +u
# shellcheck disable=SC1091
source /opt/ros/jazzy/setup.bash
if [[ -f "$ROOT/install/setup.bash" ]]; then
# shellcheck disable=SC1091
source "$ROOT/install/setup.bash"
fi
set -u
VENV_SITE="$ROOT/venv/lib/python3.12/site-packages"
if [[ -d "$VENV_SITE" ]]; then
export PYTHONPATH="$VENV_SITE${PYTHONPATH:+:$PYTHONPATH}"
fi
PKG_SRC="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2"
export PYTHONPATH="$PKG_SRC${PYTHONPATH:+:$PYTHONPATH}"
exec python3 -m linker_hand_mujoco_ros2.g20_joint_monitor_gui "$@"
@@ -0,0 +1,238 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""G20 关节弧度监视 GUI(比 MuJoCo 面板更高精度)。
默认订阅 /sim/mujoco/g20/left/joint_state,显示每个 hinge 的弧度(默认 6 位小数)。
"""
from __future__ import annotations
import argparse
import threading
import tkinter as tk
from tkinter import ttk
from linker_hand_mujoco_ros2.utils.g20_joints import G20_MIMIC_OF
DEFAULT_TOPIC = "/sim/mujoco/g20/left/joint_state"
DEFAULT_DIGITS = 6
UI_HZ = 20.0
# 按手指分组,方便对照标定
JOINT_GROUPS: tuple[tuple[str, tuple[str, ...]], ...] = (
(
"Thumb",
(
"thumb_cmc_roll",
"thumb_cmc_yaw",
"thumb_cmc_pitch",
"thumb_mcp",
"thumb_ip",
),
),
(
"Index",
(
"index_mcp_roll",
"index_mcp_pitch",
"index_pip",
"index_dip",
),
),
(
"Middle",
(
"middle_mcp_roll",
"middle_mcp_pitch",
"middle_pip",
"middle_dip",
),
),
(
"Ring",
(
"ring_mcp_roll",
"ring_mcp_pitch",
"ring_pip",
"ring_dip",
),
),
(
"Pinky",
(
"pinky_mcp_roll",
"pinky_mcp_pitch",
"pinky_pip",
"pinky_dip",
),
),
)
def parse_args() -> argparse.Namespace:
p = argparse.ArgumentParser(description="G20 joint radian monitor GUI")
p.add_argument("--topic", default=DEFAULT_TOPIC, help="JointState 话题")
p.add_argument(
"--digits",
type=int,
default=DEFAULT_DIGITS,
help=f"小数位数(默认 {DEFAULT_DIGITS}",
)
return p.parse_args()
class JointMonitorGui:
def __init__(self, topic: str, digits: int) -> None:
self.topic = topic
self.digits = max(2, int(digits))
self._lock = threading.Lock()
self._latest: dict[str, float] = {}
self._stamp_text = "waiting..."
self._msg_count = 0
self.root = tk.Tk()
self.root.title(f"G20 Joint Radians · {topic}")
self.root.geometry("520x720")
self.root.minsize(420, 480)
style = ttk.Style(self.root)
if "clam" in style.theme_names():
style.theme_use("clam")
top = ttk.Frame(self.root, padding=8)
top.pack(fill=tk.X)
self.status = ttk.Label(top, text=f"topic: {topic} | {self._stamp_text}")
self.status.pack(anchor=tk.W)
hint = ttk.Label(
top,
text=f"精度: {self.digits} 位小数 · 灰色=mimic 从动关节",
foreground="#555",
)
hint.pack(anchor=tk.W, pady=(2, 0))
canvas = tk.Canvas(self.root, highlightthickness=0)
scroll = ttk.Scrollbar(self.root, orient=tk.VERTICAL, command=canvas.yview)
self.body = ttk.Frame(canvas, padding=8)
self.body.bind(
"<Configure>",
lambda e: canvas.configure(scrollregion=canvas.bbox("all")),
)
canvas.create_window((0, 0), window=self.body, anchor=tk.NW)
canvas.configure(yscrollcommand=scroll.set)
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
self.labels: dict[str, ttk.Label] = {}
mono = ("Menlo", 12) if self.root.tk.call("tk", "windowingsystem") == "aqua" else (
"DejaVu Sans Mono",
11,
)
for group_name, joints in JOINT_GROUPS:
box = ttk.LabelFrame(self.body, text=group_name, padding=6)
box.pack(fill=tk.X, pady=4)
box.columnconfigure(1, weight=1)
for row, name in enumerate(joints):
is_mimic = name in G20_MIMIC_OF
tag = f"{name} (mimic)" if is_mimic else name
name_lbl = ttk.Label(box, text=tag, foreground="#666" if is_mimic else "#111")
name_lbl.grid(row=row, column=0, sticky=tk.W, padx=(0, 12), pady=2)
val_lbl = ttk.Label(box, text="", font=mono, width=16, anchor=tk.E)
if is_mimic:
val_lbl.configure(foreground="#666")
val_lbl.grid(row=row, column=1, sticky=tk.E, pady=2)
self.labels[name] = val_lbl
other = ttk.LabelFrame(self.body, text="Other / unlisted", padding=6)
other.pack(fill=tk.X, pady=4)
other.columnconfigure(1, weight=1)
self.other_frame = other
self._other_row = 0
self.root.protocol("WM_DELETE_WINDOW", self._on_close)
self.root.after(int(1000 / UI_HZ), self._tick_ui)
def update_state(self, names: list[str], positions: list[float], stamp: str) -> None:
with self._lock:
self._latest = {
str(n): float(p) for n, p in zip(names, positions, strict=False)
}
self._stamp_text = stamp
self._msg_count += 1
def _tick_ui(self) -> None:
with self._lock:
latest = dict(self._latest)
stamp = self._stamp_text
count = self._msg_count
self.status.configure(
text=f"topic: {self.topic} | msgs={count} | {stamp}"
)
fmt = f"{{:.{self.digits}f}}"
known = set(self.labels)
for name, lbl in self.labels.items():
if name in latest:
lbl.configure(text=fmt.format(latest[name]))
else:
lbl.configure(text="")
extras = sorted(set(latest) - known)
# 动态补未分组关节
for name in extras:
if name in self.labels:
continue
name_lbl = ttk.Label(self.other_frame, text=name)
name_lbl.grid(row=self._other_row, column=0, sticky=tk.W, padx=(0, 12), pady=2)
val_lbl = ttk.Label(
self.other_frame, text=fmt.format(latest[name]), font=("DejaVu Sans Mono", 11), width=16, anchor=tk.E
)
val_lbl.grid(row=self._other_row, column=1, sticky=tk.E, pady=2)
self.labels[name] = val_lbl
self._other_row += 1
if not self.root.winfo_exists():
return
self.root.after(int(1000 / UI_HZ), self._tick_ui)
def _on_close(self) -> None:
self.root.destroy()
def run(self) -> None:
self.root.mainloop()
def main() -> int:
args = parse_args()
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import JointState
gui = JointMonitorGui(topic=args.topic, digits=args.digits)
stop = threading.Event()
rclpy.init()
node = Node("g20_joint_monitor_gui")
def on_msg(msg: JointState) -> None:
stamp = f"{msg.header.stamp.sec}.{msg.header.stamp.nanosec:09d}"
gui.update_state(list(msg.name), list(msg.position), stamp)
node.create_subscription(JointState, args.topic, on_msg, 50)
node.get_logger().info(f"joint monitor GUI sub {args.topic}")
def spin_ros() -> None:
while not stop.is_set() and rclpy.ok():
rclpy.spin_once(node, timeout_sec=0.05)
t = threading.Thread(target=spin_ros, daemon=True)
t.start()
try:
gui.run()
finally:
stop.set()
if rclpy.ok():
node.destroy_node()
rclpy.shutdown()
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -23,12 +23,9 @@ from pathlib import Path
import numpy as np
from linker_hand_mujoco_ros2.utils.g20_joints import (
complete_g20_mimic_positions,
g20_actuated_joint_names,
g20_required_joint_names,
half_fist_positions,
open_hand_positions,
sdk_range_to_full_urdf_positions,
get_mapping_fns,
)
PKG_DIR = Path(__file__).resolve().parent
@@ -49,12 +46,12 @@ DEFAULT_SLEW_RAD_S = 5.0
PENETRATION_TOL_M = 5e-4
COLLISION_PROJECT_ITERS = 12
# 与 Isaac G20 共用的对比视角:正对掌心(法向约 -X)、手指朝上(+Z)
# lookat = 手几何中心(本地);相机从 +X 看向 -X
# 正交正对掌心:从 +X 看向 -X,仰角 0(无俯仰)
COMPARE_LOOKAT = (0.04, -0.04, 0.12)
COMPARE_DISTANCE = 0.50
COMPARE_DISTANCE = 0.55
COMPARE_AZIMUTH = 180.0 # MuJoCo180 = 从 +X 看向掌心
COMPARE_ELEVATION = -12.0
COMPARE_ELEVATION = 0.0
COMPARE_ORTHOGRAPHIC = True
def parse_args() -> argparse.Namespace:
@@ -114,6 +111,22 @@ def parse_args() -> argparse.Namespace:
p.add_argument("--nominal-topic", default=TOPIC_NOMINAL)
p.add_argument("--cmd-topic", default=TOPIC_LEGACY_CMD, help="legacy SDK 控制话题")
p.add_argument("--sim-state-topic", default=TOPIC_SIM_STATE)
p.add_argument(
"--node-name",
default="g20_mujoco_plan_bridge",
help="ROS2 节点名(双开时必须不同)",
)
p.add_argument(
"--window-title",
default="",
help="MuJoCo 窗口标题(空则用节点名)",
)
p.add_argument(
"--mapping",
default="g20_calibrated",
choices=("g20_calibrated", "l20_urdf"),
help="命令映射:g20_calibrated=当前标定/分段;l20_urdf=仅 L20 URDF 限位+线性 mimic",
)
return p.parse_args()
@@ -180,6 +193,7 @@ class G20PlanBridge:
collision_project: bool = True,
slew_rad_s: float = DEFAULT_SLEW_RAD_S,
sim_dt: float = 0.002,
mapping: str = "g20_calibrated",
):
from sensor_msgs.msg import JointState
@@ -193,6 +207,13 @@ class G20PlanBridge:
self.slew_rad_s = max(0.0, float(slew_rad_s))
self.sim_dt = float(sim_dt)
self.lock = threading.Lock()
(
self.sdk_to_full,
self.complete_mimic,
self.open_hand_fn,
self.half_fist_fn,
) = get_mapping_fns(mapping)
self.mapping = str(mapping)
(
self.joint_qpos,
@@ -210,7 +231,7 @@ class G20PlanBridge:
self.actuated = g20_actuated_joint_names()
self.ordered_names = sorted(self.joint_qpos.keys())
open_full = open_hand_positions()
open_full = self.open_hand_fn()
self.goal: dict[str, float] = dict(open_full)
self.applied: dict[str, float] = dict(open_full)
self.track_nominal: dict[str, float] | None = None
@@ -229,12 +250,16 @@ class G20PlanBridge:
self.sub = node.create_subscription(
JointState, cmd_topic, self._on_legacy_cmd, 10
)
node.get_logger().info(f"G20: sub {cmd_topic} (SDK 0~255)")
node.get_logger().info(
f"G20: sub {cmd_topic} (SDK 0~255), mapping={self.mapping}"
)
else:
self.sub = node.create_subscription(
JointState, nominal_topic, self._on_nominal, 10
)
node.get_logger().info(f"G20 PLAN: sub {nominal_topic} (rad by name)")
node.get_logger().info(
f"G20 PLAN: sub {nominal_topic} (rad by name), mapping={self.mapping}"
)
self.pub = node.create_publisher(JointState, sim_state_topic, 10)
self._state_timer = node.create_timer(1.0 / STATE_HZ, self._publish_state)
@@ -243,7 +268,8 @@ class G20PlanBridge:
node.get_logger().info(
f"G20: pub {sim_state_topic}, joints={len(self.ordered_names)}, "
f"actuated={len(self.actuated)}, drive={mode}, "
f"collision_project={proj}, slew={self.slew_rad_s:.3f} rad/s"
f"collision_project={proj}, slew={self.slew_rad_s:.3f} rad/s, "
f"mapping={self.mapping}"
)
def _clamp_name(self, name: str, val: float) -> float:
@@ -253,7 +279,7 @@ class G20PlanBridge:
def _set_goal_only(self, positions: dict[str, float]) -> None:
"""仅更新 goal(线程安全)。禁止在 ROS 回调里碰 MjData。"""
full = complete_g20_mimic_positions(positions)
full = self.complete_mimic(positions)
with self.lock:
self.goal = full
self.track_nominal = {
@@ -314,7 +340,7 @@ class G20PlanBridge:
self, prev: dict[str, float]
) -> None:
"""按主动关节分别投影:某关节撞到时不拖死其它手指(仍硬写)。"""
cand = complete_g20_mimic_positions(dict(self.applied))
cand = self.complete_mimic(dict(self.applied))
self._hard_write_pose_unlocked(cand)
if not self._is_penetrating_unlocked():
self.applied = cand
@@ -323,9 +349,9 @@ class G20PlanBridge:
self._hard_write_pose_unlocked(prev)
if self._is_penetrating_unlocked():
base = complete_g20_mimic_positions(dict(self._last_safe_pose))
base = self.complete_mimic(dict(self._last_safe_pose))
else:
base = complete_g20_mimic_positions(dict(prev))
base = self.complete_mimic(dict(prev))
# 变化大的关节优先尝试完整步进,减少“拇指撞掌心把四指一起卡住”
order = sorted(
@@ -339,7 +365,7 @@ class G20PlanBridge:
cur = float(base.get(name, target))
if abs(target - cur) < 1e-12:
continue
trial = complete_g20_mimic_positions({**base, name: target})
trial = self.complete_mimic({**base, name: target})
self._hard_write_pose_unlocked(trial)
if not self._is_penetrating_unlocked():
base = trial
@@ -348,7 +374,7 @@ class G20PlanBridge:
best = dict(base)
for _ in range(COLLISION_PROJECT_ITERS):
mid = 0.5 * (lo + hi)
trial = complete_g20_mimic_positions({**base, name: mid})
trial = self.complete_mimic({**base, name: mid})
self._hard_write_pose_unlocked(trial)
if self._is_penetrating_unlocked():
hi = mid
@@ -357,7 +383,7 @@ class G20PlanBridge:
best = trial
base = best
self.applied = complete_g20_mimic_positions(base)
self.applied = self.complete_mimic(base)
self._hard_write_pose_unlocked(self.applied)
if self._is_penetrating_unlocked():
self.applied = dict(self._last_safe_pose)
@@ -421,7 +447,7 @@ class G20PlanBridge:
vals = [float(x) for x in msg.position[:20]]
while len(vals) < 20:
vals.append(255.0)
full = sdk_range_to_full_urdf_positions(vals)
full = self.sdk_to_full(vals)
self.set_nominal_targets(full)
def _warn_missing(self, detail: str) -> None:
@@ -520,7 +546,7 @@ def main(argv: list[str] | None = None) -> int:
slew_rad_s = 0.0 if args.track_test else float(args.slew_rad_s)
rclpy.init()
node = Node("g20_mujoco_plan_bridge")
node = Node(str(args.node_name))
bridge = G20PlanBridge(
node,
model,
@@ -530,10 +556,13 @@ def main(argv: list[str] | None = None) -> int:
cmd_topic=args.cmd_topic,
sim_state_topic=args.sim_state_topic,
use_pd_drive=bool(args.pd_drive) and not bool(args.track_test),
# L20 原版 mesh 在张开侧摆/握拳时易自碰;collision_project 会把 qpos 卡死。
collision_project=(not bool(args.no_collision_project))
and (not bool(args.track_test)),
and (not bool(args.track_test))
and (str(args.mapping) != "l20_urdf"),
slew_rad_s=slew_rad_s,
sim_dt=dt,
mapping=str(args.mapping),
)
node.get_logger().info(
f"timing: realtime={use_realtime} speed={speed:.3f} dt={dt:.4f}s"
@@ -558,7 +587,7 @@ def main(argv: list[str] | None = None) -> int:
track_phase = 0
track_switch_step = 200
if args.track_test:
bridge.set_nominal_targets(open_hand_positions())
bridge.set_nominal_targets(bridge.open_hand_fn())
node.get_logger().info("track-test: phase0 open")
step = 0
@@ -592,7 +621,7 @@ def main(argv: list[str] | None = None) -> int:
if not args.track_test:
return False
if step == track_switch_step:
bridge.set_nominal_targets(half_fist_positions())
bridge.set_nominal_targets(bridge.half_fist_fn())
track_phase = 1
node.get_logger().info("track-test: phase1 half-fist")
if track_phase == 1 and step >= track_switch_step + 400:
@@ -628,11 +657,23 @@ def main(argv: list[str] | None = None) -> int:
break
else:
viewer = mujoco.viewer.launch_passive(model, data)
# 初始对比视角正对掌心(与 Isaac 同 lookat/距离)
# 正交视角正对掌心
viewer.cam.lookat[:] = COMPARE_LOOKAT
viewer.cam.distance = COMPARE_DISTANCE
viewer.cam.azimuth = COMPARE_AZIMUTH
viewer.cam.elevation = COMPARE_ELEVATION
viewer.cam.orthographic = 1 if COMPARE_ORTHOGRAPHIC else 0
title = str(args.window_title or args.node_name)
try:
# mujoco>=3: glfw window handle
if hasattr(viewer, "_gui") and hasattr(viewer._gui, "title"):
viewer._gui.title = title
elif hasattr(viewer, "wnd") and viewer.wnd is not None:
import glfw
glfw.set_window_title(viewer.wnd, title)
except Exception:
pass
node.get_logger().info(f"viewer title={title}")
while running and viewer.is_running():
with bridge.lock:
bridge.step_simulation_unlocked()
@@ -45,27 +45,29 @@
<geom class="visual" mesh="hand_base_link"/>
<geom class="collision" type="mesh" mesh="hand_base_link"/>
</body>
<body name="thumb_metacarpals_base1" pos="0.01916 -0.01 0.0436">
<!-- from linkerhand_g20_left_zero_calibrated_from_calibration4_20260806.urdf -->
<body name="thumb_metacarpals_base1" pos="0.01916 -0.01 0.0436" quat="0.9998102 0.019482388 0 0">
<inertial pos="0.0115155 0.00398069 0.000272888" quat="0.982721 -0.180506 -0.00245546 0.0408729" mass="0.0160352" diaginertia="4.05464e-06 2.4729e-06 2.02979e-06"/>
<joint name="thumb_cmc_roll" pos="0 0 0" axis="-1 0 0" range="0 1.47" actuatorfrcrange="-100 100"/>
<joint name="thumb_cmc_roll" pos="0 0 0" axis="-1 0 0" range="0 1.38358796" actuatorfrcrange="0.0 1.38358796"/>
<geom class="visual" mesh="thumb_metacarpals_base1"/>
<body name="thumb_metacarpals_base2" pos="0.019845 0 0" quat="0.926488 0.376324 0 0">
<body name="thumb_metacarpals_base2" pos="0.019845 0 0" quat="0.92648294 0.37632145 0.0012783055 -0.0031471185">
<inertial pos="0.00286715 -0.00615727 0.00472605" quat="0.751727 0.570385 0.0649353 -0.32458" mass="0.00183265" diaginertia="1.63035e-07 1.63035e-07 1.63035e-07"/>
<joint name="thumb_cmc_yaw" pos="0 0 0" axis="0 0 1" range="0 1.57" actuatorfrcrange="-100 100"/>
<joint name="thumb_cmc_yaw" pos="0 0 0" axis="0 0 1" range="0 1.61019336" actuatorfrcrange="0.0 1.61019336"/>
<geom class="visual" mesh="thumb_metacarpals_base2"/>
<body name="thumb_metacarpals" pos="0.0088218 -0.024245 0.0005" quat="0.49419 0.415774 -0.291116 0.705804">
<!-- pitch zero: rpy=0 -0.862423299352741 1.9199 -->
<body name="thumb_metacarpals" pos="0.0088218 -0.024245 0.0005" quat="0.52169594 0.34039715 -0.23833906 0.745089">
<inertial pos="0.00670658 8.60759e-05 0.0331413" quat="0.705208 -0.00142811 -0.0031017 0.708992" mass="0.028716" diaginertia="1.35288e-05 1.13359e-05 3.37772e-06"/>
<joint name="thumb_cmc_pitch" pos="0 0 0" axis="0 1 0" range="0 0.84" actuatorfrcrange="-100 100"/>
<joint name="thumb_cmc_pitch" pos="0 0 0" axis="0 1 0" range="0 0.82302235" actuatorfrcrange="0.0 0.82302235"/>
<geom class="visual" mesh="thumb_metacarpals"/>
<geom class="collision" type="mesh" mesh="thumb_metacarpals"/>
<body name="thumb_proximal" pos="0.0127018 0 0.0627256">
<body name="thumb_proximal" pos="0.0127018 0 0.0627256" quat="0.99974477 0 0.022591981 0">
<inertial pos="-0.00268734 0.000568245 0.0118502" quat="0.705679 -0.0691221 -0.0785141 0.700767" mass="0.00549016" diaginertia="6.2328e-07 4.89863e-07 2.23372e-07"/>
<joint name="thumb_mcp" pos="0 0 0" axis="0 1 0" range="0 1.26" actuatorfrcrange="-100 100"/>
<joint name="thumb_mcp" pos="0 0 0" axis="0 1 0" range="0 1.20296658" actuatorfrcrange="0.0 1.20296658"/>
<geom class="visual" mesh="thumb_proximal"/>
<geom class="collision" type="mesh" mesh="thumb_proximal"/>
<body name="thumb_distal" pos="-0.00483652 0 0.0342226">
<inertial pos="-0.000710022 -0.00267576 0.0124763" quat="0.561846 0.213938 0.139751 0.786785" mass="0.00382047" diaginertia="2.69897e-07 2.52035e-07 8.26387e-08"/>
<joint name="thumb_ip" pos="0 0 0" axis="0 1 0" range="0 1.29" actuatorfrcrange="-100 100"/>
<joint name="thumb_ip" pos="0 0 0" axis="0 1 0" range="0 1.29" actuatorfrcrange="0 1.29"/>
<geom class="visual" mesh="thumb_distal"/>
<geom class="collision" type="mesh" mesh="thumb_distal"/>
</body>
@@ -75,21 +77,21 @@
</body>
<body name="index_metacarpals" pos="0.0038 -0.03525 0.1481">
<inertial pos="-0.007943 -2.93674e-07 0.00220106" quat="0.599922 0.599922 -0.374291 0.374291" mass="0.000895516" diaginertia="4.67031e-08 3.17351e-08 2.85435e-08"/>
<joint name="index_mcp_roll" pos="0 0 0" axis="1 0 0" range="-0.23 0.23" actuatorfrcrange="-100 100"/>
<joint name="index_mcp_roll" pos="0 0 0" axis="1 0 0" range="-0.25155263 0.25155263" actuatorfrcrange="-0.25155263 0.25155263"/>
<geom class="visual" mesh="index_metacarpals"/>
<body name="index_proximal" pos="-0.0021302 0 0">
<body name="index_proximal" pos="-0.0021302 0 0" quat="0.99974787 0 0.022454266 0">
<inertial pos="-0.00114185 3.47316e-05 0.0228928" quat="0.706728 0.0167051 0.0169055 0.707086" mass="0.00450921" diaginertia="6.74196e-07 6.67877e-07 1.34555e-07"/>
<joint name="index_mcp_pitch" pos="0 0 0" axis="0 1 0" range="0 1.22" actuatorfrcrange="-100 100"/>
<joint name="index_mcp_pitch" pos="0 0 0" axis="0 1 0" range="0 1.22215668" actuatorfrcrange="0.0 1.22215668"/>
<geom class="visual" mesh="index_proximal"/>
<geom class="collision" type="mesh" mesh="index_proximal"/>
<body name="index_middle" pos="-0.0041946 0 0.044819">
<body name="index_middle" pos="-0.0041946 0 0.044819" quat="0.99998681 0 0.0051352458 0">
<inertial pos="-0.000206992 2.60617e-06 0.0167065" quat="0.99852 7.15582e-05 0.054381 -3.89717e-06" mass="0.00237827" diaginertia="2.31148e-07 1.76761e-07 9.46702e-08"/>
<joint name="index_pip" pos="0 0 0" axis="0 1 0" range="0 1.74" actuatorfrcrange="-100 100"/>
<joint name="index_pip" pos="0 0 0" axis="0 1 0" range="0 1.7412882" actuatorfrcrange="0.0 1.7412882"/>
<geom class="visual" mesh="index_middle"/>
<geom class="collision" type="mesh" mesh="index_middle"/>
<body name="index_distal" pos="0.0028245 0 0.031696">
<inertial pos="-0.00816247 2.27059e-05 0.00811807" quat="0.942747 0.00109864 0.333492 0.00313565" mass="0.0033417" diaginertia="2.07631e-07 1.78094e-07 8.6082e-08"/>
<joint name="index_dip" pos="0 0 0" axis="0 1 0" range="0 1.55" actuatorfrcrange="-100 100"/>
<joint name="index_dip" pos="0 0 0" axis="0 1 0" range="0 1.49304833" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="index_distal"/>
<geom class="collision" type="mesh" mesh="index_distal"/>
</body>
@@ -98,21 +100,21 @@
</body>
<body name="middle_metacarpals" pos="0.0038 -0.01175 0.1526">
<inertial pos="-0.007943 -2.93007e-07 0.00220106" quat="0.599922 0.599922 -0.374291 0.374291" mass="0.000895517" diaginertia="4.67031e-08 3.17351e-08 2.85436e-08"/>
<joint name="middle_mcp_roll" pos="0 0 0" axis="1 0 0" range="-0.23 0.23" actuatorfrcrange="-100 100"/>
<joint name="middle_mcp_roll" pos="0 0 0" axis="1 0 0" range="-0.25155263 0.25155263" actuatorfrcrange="-0.25155263 0.25155263"/>
<geom class="visual" mesh="middle_metacarpals"/>
<body name="middle_proximal" pos="-0.0021302 0 0">
<body name="middle_proximal" pos="-0.0021302 0 0" quat="0.99974787 0 0.022454266 0">
<inertial pos="-0.00114185 3.47316e-05 0.0228928" quat="0.706728 0.0167051 0.0169055 0.707086" mass="0.00450921" diaginertia="6.74196e-07 6.67877e-07 1.34555e-07"/>
<joint name="middle_mcp_pitch" pos="0 0 0" axis="0 1 0" range="0 1.22" actuatorfrcrange="-100 100"/>
<joint name="middle_mcp_pitch" pos="0 0 0" axis="0 1 0" range="0 1.22215668" actuatorfrcrange="0.0 1.22215668"/>
<geom class="visual" mesh="middle_proximal"/>
<geom class="collision" type="mesh" mesh="middle_proximal"/>
<body name="middle_middle" pos="-0.00419458 0 0.0448194">
<body name="middle_middle" pos="-0.00419458 0 0.0448194" quat="0.99998681 0 0.0051352458 0">
<inertial pos="-0.000206992 2.60625e-06 0.0167065" quat="0.99852 7.15718e-05 0.054381 -3.89791e-06" mass="0.00237828" diaginertia="2.31148e-07 1.76761e-07 9.46702e-08"/>
<joint name="middle_pip" pos="0 0 0" axis="0 1 0" range="0 1.74" actuatorfrcrange="-100 100"/>
<joint name="middle_pip" pos="0 0 0" axis="0 1 0" range="0 1.7412882" actuatorfrcrange="0.0 1.7412882"/>
<geom class="visual" mesh="middle_middle"/>
<geom class="collision" type="mesh" mesh="middle_middle"/>
<body name="middle_distal" pos="0.0028245 0 0.031696">
<inertial pos="-0.00816247 2.27059e-05 0.00811807" quat="0.942747 0.00109865 0.333492 0.00313567" mass="0.0033417" diaginertia="2.07631e-07 1.78094e-07 8.6082e-08"/>
<joint name="middle_dip" pos="0 0 0" axis="0 1 0" range="0 1.55" actuatorfrcrange="-100 100"/>
<joint name="middle_dip" pos="0 0 0" axis="0 1 0" range="0 1.49304833" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="middle_distal"/>
<geom class="collision" type="mesh" mesh="middle_distal"/>
</body>
@@ -121,21 +123,21 @@
</body>
<body name="ring_metacarpals" pos="0.0038 0.01175 0.1481">
<inertial pos="-0.007943 -2.94513e-07 0.00220106" quat="0.599922 0.599922 -0.374291 0.374291" mass="0.000895517" diaginertia="4.67031e-08 3.17351e-08 2.85436e-08"/>
<joint name="ring_mcp_roll" pos="0 0 0" axis="1 0 0" range="-0.23 0.23" actuatorfrcrange="-100 100"/>
<joint name="ring_mcp_roll" pos="0 0 0" axis="1 0 0" range="-0.25155263 0.25155263" actuatorfrcrange="-0.25155263 0.25155263"/>
<geom class="visual" mesh="ring_metacarpals"/>
<body name="ring_proximal" pos="-0.0021302 0 0">
<body name="ring_proximal" pos="-0.0021302 0 0" quat="0.99974787 0 0.022454266 0">
<inertial pos="-0.00114185 3.47316e-05 0.0228928" quat="0.706728 0.0167051 0.0169055 0.707086" mass="0.00450921" diaginertia="6.74196e-07 6.67877e-07 1.34555e-07"/>
<joint name="ring_mcp_pitch" pos="0 0 0" axis="0 1 0" range="0 1.22" actuatorfrcrange="-100 100"/>
<joint name="ring_mcp_pitch" pos="0 0 0" axis="0 1 0" range="0 1.22215668" actuatorfrcrange="0.0 1.22215668"/>
<geom class="visual" mesh="ring_proximal"/>
<geom class="collision" type="mesh" mesh="ring_proximal"/>
<body name="ring_middle" pos="-0.00419458 0 0.0448194">
<body name="ring_middle" pos="-0.00419458 0 0.0448194" quat="0.99998681 0 0.0051352458 0">
<inertial pos="-0.000206993 2.60747e-06 0.0167065" quat="0.99852 7.16159e-05 0.054381 -3.90032e-06" mass="0.00237827" diaginertia="2.31148e-07 1.76761e-07 9.46701e-08"/>
<joint name="ring_pip" pos="0 0 0" axis="0 1 0" range="0 1.74" actuatorfrcrange="-100 100"/>
<joint name="ring_pip" pos="0 0 0" axis="0 1 0" range="0 1.7412882" actuatorfrcrange="0.0 1.7412882"/>
<geom class="visual" mesh="ring_middle"/>
<geom class="collision" type="mesh" mesh="ring_middle"/>
<body name="ring_distal" pos="0.0028245 0 0.031696">
<inertial pos="-0.00816247 2.27057e-05 0.00811807" quat="0.942747 0.00109865 0.333492 0.00313572" mass="0.0033417" diaginertia="2.07631e-07 1.78094e-07 8.6082e-08"/>
<joint name="ring_dip" pos="0 0 0" axis="0 1 0" range="0 1.55" actuatorfrcrange="-100 100"/>
<joint name="ring_dip" pos="0 0 0" axis="0 1 0" range="0 1.49304833" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="ring_distal"/>
<geom class="collision" type="mesh" mesh="ring_distal"/>
</body>
@@ -144,21 +146,21 @@
</body>
<body name="pinky_metacarpals" pos="0.0038 0.03525 0.1436">
<inertial pos="-0.007943 -2.93007e-07 0.00220106" quat="0.599922 0.599922 -0.374291 0.374291" mass="0.000895517" diaginertia="4.67031e-08 3.17351e-08 2.85436e-08"/>
<joint name="pinky_mcp_roll" pos="0 0 0" axis="1 0 0" range="-0.23 0.23" actuatorfrcrange="-100 100"/>
<joint name="pinky_mcp_roll" pos="0 0 0" axis="1 0 0" range="-0.25155263 0.25155263" actuatorfrcrange="-0.25155263 0.25155263"/>
<geom class="visual" mesh="pinky_metacarpals"/>
<body name="pinky_proximal" pos="-0.0021302 0 0">
<body name="pinky_proximal" pos="-0.0021302 0 0" quat="0.99974787 0 0.022454266 0">
<inertial pos="-0.00114185 3.47316e-05 0.0228928" quat="0.706728 0.0167051 0.0169055 0.707086" mass="0.00450921" diaginertia="6.74196e-07 6.67877e-07 1.34555e-07"/>
<joint name="pinky_mcp_pitch" pos="0 0 0" axis="0 1 0" range="0 1.22" actuatorfrcrange="-100 100"/>
<joint name="pinky_mcp_pitch" pos="0 0 0" axis="0 1 0" range="0 1.22215668" actuatorfrcrange="0.0 1.22215668"/>
<geom class="visual" mesh="pinky_proximal"/>
<geom class="collision" type="mesh" mesh="pinky_proximal"/>
<body name="pinky_middle" pos="-0.0041946 0 0.044819">
<body name="pinky_middle" pos="-0.0041946 0 0.044819" quat="0.99998681 0 0.0051352458 0">
<inertial pos="-0.000206993 2.60749e-06 0.0167065" quat="0.99852 7.16152e-05 0.054381 -3.90028e-06" mass="0.00237827" diaginertia="2.31148e-07 1.76761e-07 9.46701e-08"/>
<joint name="pinky_pip" pos="0 0 0" axis="0 1 0" range="0 1.74" actuatorfrcrange="-100 100"/>
<joint name="pinky_pip" pos="0 0 0" axis="0 1 0" range="0 1.7412882" actuatorfrcrange="0.0 1.7412882"/>
<geom class="visual" mesh="pinky_middle"/>
<geom class="collision" type="mesh" mesh="pinky_middle"/>
<body name="pinky_distal" pos="0.0028245 0 0.031696">
<inertial pos="-0.00816247 2.27054e-05 0.00811807" quat="0.942747 0.00109863 0.333492 0.00313569" mass="0.0033417" diaginertia="2.07631e-07 1.78094e-07 8.6082e-08"/>
<joint name="pinky_dip" pos="0 0 0" axis="0 1 0" range="0 1.55" actuatorfrcrange="-100 100"/>
<joint name="pinky_dip" pos="0 0 0" axis="0 1 0" range="0 1.49304833" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="pinky_distal"/>
<geom class="collision" type="mesh" mesh="pinky_distal"/>
</body>
@@ -203,26 +205,26 @@
<exclude body1="pinky_middle" body2="pinky_distal"/>
</contact>
<actuator>
<position name="thumb_cmc_roll_pos" joint="thumb_cmc_roll" kp="200" kv="8" ctrlrange="0.0 1.47" forcelimited="true" forcerange="-8 8" />
<position name="thumb_cmc_yaw_pos" joint="thumb_cmc_yaw" kp="200" kv="8" ctrlrange="0.0 1.57" forcelimited="true" forcerange="-8 8" />
<position name="thumb_cmc_pitch_pos" joint="thumb_cmc_pitch" kp="200" kv="8" ctrlrange="0.0 0.84" forcelimited="true" forcerange="-8 8" />
<position name="thumb_mcp_pos" joint="thumb_mcp" kp="200" kv="8" ctrlrange="0.0 1.26" forcelimited="true" forcerange="-8 8" />
<position name="thumb_ip_pos" joint="thumb_ip" kp="200" kv="8" ctrlrange="0.0 1.29" forcelimited="true" forcerange="-8 8" />
<position name="index_mcp_roll_pos" joint="index_mcp_roll" kp="200" kv="8" ctrlrange="-0.23 0.23" forcelimited="true" forcerange="-8 8" />
<position name="index_mcp_pitch_pos" joint="index_mcp_pitch" kp="200" kv="8" ctrlrange="0.0 1.22" forcelimited="true" forcerange="-8 8" />
<position name="index_pip_pos" joint="index_pip" kp="200" kv="8" ctrlrange="0.0 1.74" forcelimited="true" forcerange="-8 8" />
<position name="index_dip_pos" joint="index_dip" kp="200" kv="8" ctrlrange="0.0 1.55" forcelimited="true" forcerange="-8 8" />
<position name="middle_mcp_roll_pos" joint="middle_mcp_roll" kp="200" kv="8" ctrlrange="-0.23 0.23" forcelimited="true" forcerange="-8 8" />
<position name="middle_mcp_pitch_pos" joint="middle_mcp_pitch" kp="200" kv="8" ctrlrange="0.0 1.22" forcelimited="true" forcerange="-8 8" />
<position name="middle_pip_pos" joint="middle_pip" kp="200" kv="8" ctrlrange="0.0 1.74" forcelimited="true" forcerange="-8 8" />
<position name="middle_dip_pos" joint="middle_dip" kp="200" kv="8" ctrlrange="0.0 1.55" forcelimited="true" forcerange="-8 8" />
<position name="ring_mcp_roll_pos" joint="ring_mcp_roll" kp="200" kv="8" ctrlrange="-0.23 0.23" forcelimited="true" forcerange="-8 8" />
<position name="ring_mcp_pitch_pos" joint="ring_mcp_pitch" kp="200" kv="8" ctrlrange="0.0 1.22" forcelimited="true" forcerange="-8 8" />
<position name="ring_pip_pos" joint="ring_pip" kp="200" kv="8" ctrlrange="0.0 1.74" forcelimited="true" forcerange="-8 8" />
<position name="ring_dip_pos" joint="ring_dip" kp="200" kv="8" ctrlrange="0.0 1.55" forcelimited="true" forcerange="-8 8" />
<position name="pinky_mcp_roll_pos" joint="pinky_mcp_roll" kp="200" kv="8" ctrlrange="-0.23 0.23" forcelimited="true" forcerange="-8 8" />
<position name="pinky_mcp_pitch_pos" joint="pinky_mcp_pitch" kp="200" kv="8" ctrlrange="0.0 1.22" forcelimited="true" forcerange="-8 8" />
<position name="pinky_pip_pos" joint="pinky_pip" kp="200" kv="8" ctrlrange="0.0 1.74" forcelimited="true" forcerange="-8 8" />
<position name="pinky_dip_pos" joint="pinky_dip" kp="200" kv="8" ctrlrange="0.0 1.55" forcelimited="true" forcerange="-8 8" />
<position name="thumb_cmc_roll_pos" joint="thumb_cmc_roll" kp="200" kv="8" ctrlrange="0.0 1.38358796" forcelimited="true" forcerange="-8 8" />
<position name="thumb_cmc_yaw_pos" joint="thumb_cmc_yaw" kp="200" kv="8" ctrlrange="0.0 1.61019336" forcelimited="true" forcerange="-8 8" />
<position name="thumb_cmc_pitch_pos" joint="thumb_cmc_pitch" kp="200" kv="8" ctrlrange="0.0 0.82302235" forcelimited="true" forcerange="-8 8" />
<position name="thumb_mcp_pos" joint="thumb_mcp" kp="200" kv="8" ctrlrange="0.0 1.20296658" forcelimited="true" forcerange="-8 8" />
<position name="thumb_ip_pos" joint="thumb_ip" kp="200" kv="8" ctrlrange="0 1.29" forcelimited="true" forcerange="-8 8" />
<position name="index_mcp_roll_pos" joint="index_mcp_roll" kp="200" kv="8" ctrlrange="-0.25155263 0.25155263" forcelimited="true" forcerange="-8 8" />
<position name="index_mcp_pitch_pos" joint="index_mcp_pitch" kp="200" kv="8" ctrlrange="0.0 1.22215668" forcelimited="true" forcerange="-8 8" />
<position name="index_pip_pos" joint="index_pip" kp="200" kv="8" ctrlrange="0.0 1.7412882" forcelimited="true" forcerange="-8 8" />
<position name="index_dip_pos" joint="index_dip" kp="200" kv="8" ctrlrange="0 1.49304833" forcelimited="true" forcerange="-8 8" />
<position name="middle_mcp_roll_pos" joint="middle_mcp_roll" kp="200" kv="8" ctrlrange="-0.25155263 0.25155263" forcelimited="true" forcerange="-8 8" />
<position name="middle_mcp_pitch_pos" joint="middle_mcp_pitch" kp="200" kv="8" ctrlrange="0.0 1.22215668" forcelimited="true" forcerange="-8 8" />
<position name="middle_pip_pos" joint="middle_pip" kp="200" kv="8" ctrlrange="0.0 1.7412882" forcelimited="true" forcerange="-8 8" />
<position name="middle_dip_pos" joint="middle_dip" kp="200" kv="8" ctrlrange="0 1.49304833" forcelimited="true" forcerange="-8 8" />
<position name="ring_mcp_roll_pos" joint="ring_mcp_roll" kp="200" kv="8" ctrlrange="-0.25155263 0.25155263" forcelimited="true" forcerange="-8 8" />
<position name="ring_mcp_pitch_pos" joint="ring_mcp_pitch" kp="200" kv="8" ctrlrange="0.0 1.22215668" forcelimited="true" forcerange="-8 8" />
<position name="ring_pip_pos" joint="ring_pip" kp="200" kv="8" ctrlrange="0.0 1.7412882" forcelimited="true" forcerange="-8 8" />
<position name="ring_dip_pos" joint="ring_dip" kp="200" kv="8" ctrlrange="0 1.49304833" forcelimited="true" forcerange="-8 8" />
<position name="pinky_mcp_roll_pos" joint="pinky_mcp_roll" kp="200" kv="8" ctrlrange="-0.25155263 0.25155263" forcelimited="true" forcerange="-8 8" />
<position name="pinky_mcp_pitch_pos" joint="pinky_mcp_pitch" kp="200" kv="8" ctrlrange="0.0 1.22215668" forcelimited="true" forcerange="-8 8" />
<position name="pinky_pip_pos" joint="pinky_pip" kp="200" kv="8" ctrlrange="0.0 1.7412882" forcelimited="true" forcerange="-8 8" />
<position name="pinky_dip_pos" joint="pinky_dip" kp="200" kv="8" ctrlrange="0 1.49304833" forcelimited="true" forcerange="-8 8" />
</actuator>
</mujoco>
@@ -1,9 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This URDF was automatically created by SolidWorks to URDF Exporter! Originally created by Stephen Brawner (brawner@gmail.com)
Commit Version: 1.6.0-4-g7f85cfe Build Version: 1.6.7995.38578
For more information, please see http://wiki.ros.org/sw_urdf_exporter -->
<robot
name="linkerhand_g20_left.urdf">
<?xml version='1.0' encoding='utf-8'?>
<!-- STRICT JSON zero calibration (no manual angle nudges).
Source: g20_left_G20_LEFT_001_calibration(4).json
schema_version=3 quality={'passed': True, 'validation_mae_rad': None, 'validation_p95_rad': None}
Method: CAD origin * local delta from table_projected_zero_rad vs fixture refs.
Runtime motion: joints[*].angle_rad 0..255 lookup (installed as g20_left_G20_LEFT_001_calibration.json).
MCP rolls: origin stays CAD; curve already zeros at command 127 (travel_midpoint only).
Passive: thumb_ip JSON is cumulative tip; URDF uses relative mimic.
Applied:
thumb_cmc_pitch measured=-0.16004248 ref=-0.3676637606472593 -> rpy=0.0 -0.8570787193527406 1.9199
thumb_cmc_roll measured=0.11705115 ref=0.0780839079142253 -> rpy=0.038967242085774706 0.0 0.0
thumb_cmc_yaw measured=0.35052345 ref=0.3573171129976329 -> rpy=0.7716384658122452 0.004737330500130627 -0.004869469585122805
thumb_mcp measured=0.17127076 ref=0.1260829540620233 -> rpy=0.0 0.045187805937976694 0.0
four MCP pitch measured=-1.52588402 nominal=-pi/2 -> rpy=0.0 0.04491230679489666 0.0
four PIP measured=-1.56052579 nominal=-pi/2 -> rpy=0.0 0.010270536794896534 0.0
-->
<robot name="linkerhand_g20_left_zero_calibrated_from_calibration4_strict_20260806">
<link
name="hand_base_link">
<inertial>
@@ -89,7 +102,7 @@
type="revolute">
<origin
xyz="0.01916 -0.01 0.0436"
rpy="0 0 0" />
rpy="0.038967242085774706 0.0 0.0" />
<parent
link="hand_base_link" />
<child
@@ -97,8 +110,8 @@
<axis
xyz="-1 0 0" />
<limit
lower="0"
upper="1.47"
lower="0.0"
upper="1.38358796"
effort="100"
velocity="1" />
</joint>
@@ -147,7 +160,7 @@
type="revolute">
<origin
xyz="0.019845 0 0"
rpy="0.77165 0 0" />
rpy="0.7716384658122452 0.004737330500130627 -0.004869469585122805" />
<parent
link="thumb_metacarpals_base1" />
<child
@@ -155,8 +168,8 @@
<axis
xyz="0 0 1" />
<limit
lower="0"
upper="1.57"
lower="0.0"
upper="1.61019336"
effort="100"
velocity="1" />
</joint>
@@ -205,7 +218,7 @@
type="revolute">
<origin
xyz="0.0088218 -0.024245 0.0005"
rpy="0 -1.0647 1.9199" />
rpy="0.0 -0.8570787193527406 1.9199" />
<parent
link="thumb_metacarpals_base2" />
<child
@@ -213,8 +226,8 @@
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="0.84"
lower="0.0"
upper="0.82302235"
effort="100"
velocity="1" />
</joint>
@@ -263,7 +276,7 @@
type="revolute">
<origin
xyz="0.0127018248047608 0 0.0627256157349894"
rpy="0 0 0" />
rpy="0.0 0.045187805937976694 0.0" />
<parent
link="thumb_metacarpals" />
<child
@@ -271,8 +284,8 @@
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.26"
lower="0.0"
upper="1.20296658"
effort="100"
velocity="1" />
</joint>
@@ -391,8 +404,8 @@
<axis
xyz="1 0 0" />
<limit
lower="-0.23"
upper="0.23"
lower="-0.25155263"
upper="0.25155263"
effort="100"
velocity="1" />
</joint>
@@ -441,7 +454,7 @@
type="revolute">
<origin
xyz="-0.0021302 0 0"
rpy="0 0 0" />
rpy="0.0 0.04491230679489666 0.0" />
<parent
link="index_metacarpals" />
<child
@@ -449,8 +462,8 @@
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.22"
lower="0.0"
upper="1.22215668"
effort="100"
velocity="1" />
</joint>
@@ -499,7 +512,7 @@
type="revolute">
<origin
xyz="-0.0041946 0 0.044819"
rpy="0 0 0" />
rpy="0.0 0.010270536794896534 0.0" />
<parent
link="index_proximal" />
<child
@@ -507,8 +520,8 @@
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.74"
lower="0.0"
upper="1.7412882"
effort="100"
velocity="1" />
</joint>
@@ -627,8 +640,8 @@
<axis
xyz="1 0 0" />
<limit
lower="-0.23"
upper="0.23"
lower="-0.25155263"
upper="0.25155263"
effort="100"
velocity="1" />
</joint>
@@ -677,7 +690,7 @@
type="revolute">
<origin
xyz="-0.0021302 0 0"
rpy="0 0 0" />
rpy="0.0 0.04491230679489666 0.0" />
<parent
link="middle_metacarpals" />
<child
@@ -685,8 +698,8 @@
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.22"
lower="0.0"
upper="1.22215668"
effort="100"
velocity="1" />
</joint>
@@ -735,7 +748,7 @@
type="revolute">
<origin
xyz="-0.00419457745591796 0 0.0448194332262389"
rpy="0 0 0" />
rpy="0.0 0.010270536794896534 0.0" />
<parent
link="middle_proximal" />
<child
@@ -743,8 +756,8 @@
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.74"
lower="0.0"
upper="1.7412882"
effort="100"
velocity="1" />
</joint>
@@ -863,8 +876,8 @@
<axis
xyz="1 0 0" />
<limit
lower="-0.23"
upper="0.23"
lower="-0.25155263"
upper="0.25155263"
effort="100"
velocity="1" />
</joint>
@@ -913,7 +926,7 @@
type="revolute">
<origin
xyz="-0.0021302 0 0"
rpy="0 0 0" />
rpy="0.0 0.04491230679489666 0.0" />
<parent
link="ring_metacarpals" />
<child
@@ -921,8 +934,8 @@
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.22"
lower="0.0"
upper="1.22215668"
effort="100"
velocity="1" />
</joint>
@@ -971,7 +984,7 @@
type="revolute">
<origin
xyz="-0.00419457745591796 0 0.0448194332262389"
rpy="0 0 0" />
rpy="0.0 0.010270536794896534 0.0" />
<parent
link="ring_proximal" />
<child
@@ -979,8 +992,8 @@
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.74"
lower="0.0"
upper="1.7412882"
effort="100"
velocity="1" />
</joint>
@@ -1099,8 +1112,8 @@
<axis
xyz="1 0 0" />
<limit
lower="-0.23"
upper="0.23"
lower="-0.25155263"
upper="0.25155263"
effort="100"
velocity="1" />
</joint>
@@ -1149,7 +1162,7 @@
type="revolute">
<origin
xyz="-0.0021302 0 0"
rpy="0 0 0" />
rpy="0.0 0.04491230679489666 0.0" />
<parent
link="pinky_metacarpals" />
<child
@@ -1157,8 +1170,8 @@
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.22"
lower="0.0"
upper="1.22215668"
effort="100"
velocity="1" />
</joint>
@@ -1207,7 +1220,7 @@
type="revolute">
<origin
xyz="-0.0041946 0 0.044819"
rpy="0 0 0" />
rpy="0.0 0.010270536794896534 0.0" />
<parent
link="pinky_proximal" />
<child
@@ -1215,8 +1228,8 @@
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.74"
lower="0.0"
upper="1.7412882"
effort="100"
velocity="1" />
</joint>
@@ -0,0 +1,688 @@
<?xml version='1.0' encoding='utf-8'?>
<robot name="linkerhand_g20_left_zero_calibrated_20260805_114742">
<!-- G20 left camera-projected zero calibrated model.
Base URDF:
linkerhand_g20_left.urdf
Calibration source:
calibration_output/G20_LEFT_001/20260805_114742/
g20_left_G20_LEFT_001_calibration.json
Source quality:
quality.passed = true
random validation disabled (validation_mae_rad/p95_rad = null)
Projection-to-hand assumptions fixed by the three-camera fixture:
front: image +x = hand_base_link +Y, image y-up = +Z
side: image +x = hand_base_link -X, image y-up = +Z
top: image +x = hand_base_link +Y, image y-up = -X
Applied zero corrections:
thumb_cmc_pitch:
measured=-0.16538706 rad, base projected reference=-0.3676637606472593 rad
origin.rpy.y: -1.0647 -> -0.862423299352741
thumb_cmc_roll:
measured=+0.12284399 rad
origin.rpy.x: 0 -> +0.0631536325822638
thumb_cmc_yaw:
measured=+0.44521882 rad while thumb_cmc_roll command=145
local zero offset=+0.0879017070023671 rad, composed as origin * Rz(offset)
origin.rpy: [0.77165,0,0] ->
[0.769716668496618,-0.0612549109009133,0.063083483004381]
thumb_mcp:
measured=+0.17285160 rad
origin.rpy.y: 0 -> +0.0467686459379767
four-finger MCP pitch (index measurement inherited by middle/ring/pinky):
measured=-1.52933771 rad, nominal side projection=-pi/2
origin.rpy.y: 0 -> +0.0414586167948965
four-finger PIP (index measurement inherited by middle/ring/pinky):
measured=-1.55151359 rad, nominal side projection=-pi/2
origin.rpy.y: 0 -> +0.0192827367948965
Four-finger MCP roll uses the calibrated command curve:
zero_command_u8=127, travel_midpoint_rad=0.24911706.
The calibrated curve already maps command 127 to URDF q=0, so its origins
remain unchanged. Passive thumb_ip and four DIP joints have no independent
zero fields and also remain unchanged.
Only the robot name and the 12 joint-origin rotations listed above differ
from the base URDF. Joint axes, translations, limits and mimic definitions
are intentionally unchanged. This is a camera-projected kinematic-zero
model; tag mounting and fixture-axis assumptions remain part of its datum.
-->
<link name="hand_base_link">
<inertial>
<origin xyz="-0.000986417037198788 0.000268049028294054 0.0773798470852122" rpy="0 0 0" />
<mass value="0.110373495507463" />
<inertia ixx="0.000264402778830212" ixy="4.59263483005334E-07" ixz="1.32798258678817E-06" iyy="0.000159680177760236" iyz="-4.78660406501498E-06" izz="0.000118075546031988" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
</collision>
</link>
<link name="thumb_metacarpals_base1">
<inertial>
<origin xyz="0.0115155126192006 0.00398068846976699 0.000272888250745694" rpy="0 0 0" />
<mass value="0.0160351802817401" />
<inertia ixx="4.043875385429E-06" ixy="1.31113365770736E-07" ixz="-7.54220071752833E-09" iyy="2.42762975209742E-06" iyz="-1.4815076502312E-07" izz="2.08582778122043E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_roll" type="revolute">
<origin xyz="0.01916 -0.01 0.0436" rpy="0.0631536325822638 0 0" />
<parent link="hand_base_link" />
<child link="thumb_metacarpals_base1" />
<axis xyz="-1 0 0" />
<limit lower="0" upper="1.4" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals_base2">
<inertial>
<origin xyz="0.00286714610220221 -0.0061572653424441 0.00472605265322379" rpy="0 0 0" />
<mass value="0.00183264554882498" />
<inertia ixx="2.10633266517815E-07" ixy="-4.9133152227129E-08" ixz="-1.35746007921721E-08" iyy="9.83742137915545E-08" iyz="4.75450167610941E-08" izz="1.80097560989649E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_yaw" type="revolute">
<origin xyz="0.019845 0 0" rpy="0.769716668496618 -0.0612549109009133 0.063083483004381" />
<parent link="thumb_metacarpals_base1" />
<child link="thumb_metacarpals_base2" />
<axis xyz="0 0 1" />
<limit lower="0" upper="1.57" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals">
<inertial>
<origin xyz="0.00670657874489922 8.60758971228653E-05 0.0331412930049483" rpy="0 0 0" />
<mass value="0.0287160359417969" />
<inertia ixx="1.13355944686632E-05" ixy="-1.18725063259997E-08" ixz="5.09011416528365E-08" iyy="1.3528651001113E-05" iyz="2.41236037965108E-08" izz="3.37809997040935E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_pitch" type="revolute">
<origin xyz="0.0088218 -0.024245 0.0005" rpy="0 -0.862423299352741 1.9199" />
<parent link="thumb_metacarpals_base2" />
<child link="thumb_metacarpals" />
<axis xyz="0 1 0" />
<limit lower="0" upper="0.84" effort="100" velocity="1" />
</joint>
<link name="thumb_proximal">
<inertial>
<origin xyz="-0.00268734410969529 0.000568244522388137 0.0118501723044777" rpy="0 0 0" />
<mass value="0.00549015507433734" />
<inertia ixx="4.78372237673962E-07" ixy="4.56375652569348E-11" ixz="5.41461160604729E-08" iyy="6.23208954846975E-07" iyz="5.1129105291147E-09" izz="2.34934736039565E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_mcp" type="revolute">
<origin xyz="0.0127018248047608 0 0.0627256157349894" rpy="0 0.0467686459379767 0" />
<parent link="thumb_metacarpals" />
<child link="thumb_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.26" effort="100" velocity="1" />
</joint>
<link name="thumb_distal">
<inertial>
<origin xyz="-0.000710021799050622 -0.00267575921172081 0.0124762660846746" rpy="0 0 0" />
<mass value="0.00382046638843239" />
<inertia ixx="2.12120816159448E-07" ixy="-2.9588372102787E-09" ixz="-7.35950751454791E-08" iyy="2.67878295781821E-07" iyz="6.04616706384983E-09" izz="1.24571609570411E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_ip" type="revolute">
<origin xyz="-0.00483652209209624 0 0.0342226492098615" rpy="0 0 0" />
<parent link="thumb_proximal" />
<child link="thumb_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.29" effort="100" velocity="1" />
<mimic joint="thumb_mcp" multiplier="1.02" offset="0" />
</joint>
<link name="index_metacarpals">
<inertial>
<origin xyz="-0.00794299653637993 -2.93673969475883E-07 0.00220106027330236" rpy="0 0 0" />
<mass value="0.000895516429792169" />
<inertia ixx="3.46279728013563E-08" ixy="-3.91962517308058E-13" ixz="5.91029152662012E-09" iyy="2.85435471088518E-08" iyz="-5.30377507775974E-13" izz="4.38102133125935E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_roll" type="revolute">
<origin xyz="0.0038 -0.03525 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="index_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="index_proximal">
<inertial>
<origin xyz="-0.00114185484694667 3.47315751996757E-05 0.0228928405204552" rpy="0 0 0" />
<mass value="0.00450921353606138" />
<inertia ixx="6.66673080974796E-07" ixy="-1.07185172336921E-11" ixz="-2.53143396223457E-08" iyy="6.74195867168963E-07" iyz="-1.59054321114837E-10" izz="1.3575909170439E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0414586167948965 0" />
<parent link="index_metacarpals" />
<child link="index_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="index_middle">
<inertial>
<origin xyz="-0.000206991936024691 2.60617060293677E-06 0.0167065328961797" rpy="0 0 0" />
<mass value="0.00237827499847615" />
<inertia ixx="2.2953872847051E-07" ixy="2.23609392315326E-12" ixz="-1.4734008024399E-08" iyy="1.76761332719485E-07" iyz="1.15917246584962E-11" izz="9.62798051827835E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
</collision>
</link>
<joint name="index_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.0192827367948965 0" />
<parent link="index_proximal" />
<child link="index_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="index_distal">
<inertial>
<origin xyz="-0.00816246517948334 2.27058913663258E-05 0.00811806505013066" rpy="0 0 0" />
<mass value="0.00334170259772608" />
<inertia ixx="1.59570744991572E-07" ixy="1.51458653099843E-10" ixz="-5.9429168549018E-08" iyy="1.78095631426193E-07" iyz="-1.24842727205631E-10" izz="1.34141383450165E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<joint name="index_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="index_middle" />
<child link="index_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="index_pip" multiplier="0.89" offset="0" />
</joint>
<link name="middle_metacarpals">
<inertial>
<origin xyz="-0.00794299618184406 -2.93006659768391E-07 0.00220106329585604" rpy="0 0 0" />
<mass value="0.000895516947435045" />
<inertia ixx="3.46279958583857E-08" ixy="-3.91785866285381E-13" ixz="5.91029262290692E-09" iyy="2.85435605322585E-08" iyz="-5.27457529869591E-13" izz="4.38102218629515E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_roll" type="revolute">
<origin xyz="0.00379999999265848 -0.0117500000029305 0.1526" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="middle_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="middle_proximal">
<inertial>
<origin xyz="-0.00114185484818909 3.47315748623067E-05 0.0228928405191574" rpy="0 0 0" />
<mass value="0.00450921353536659" />
<inertia ixx="6.6667308094129E-07" ixy="-1.07185191777588E-11" ixz="-2.53143396278864E-08" iyy="6.74195867139795E-07" iyz="-1.59054330697635E-10" izz="1.3575909169764E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0414586167948965 0" />
<parent link="middle_metacarpals" />
<child link="middle_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="middle_middle">
<inertial>
<origin xyz="-0.000206991893084203 2.60624978909441E-06 0.0167065331356697" rpy="0 0 0" />
<mass value="0.00237827502274046" />
<inertia ixx="2.29538732170979E-07" ixy="2.23722006830829E-12" ixz="-1.47340061868536E-08" iyy="1.7676133625496E-07" iyz="1.15938574397121E-11" izz="9.62798057458164E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
</collision>
</link>
<joint name="middle_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.0192827367948965 0" />
<parent link="middle_proximal" />
<child link="middle_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="middle_distal">
<inertial>
<origin xyz="-0.00816246584707385 2.27058557035753E-05 0.00811806604834034" rpy="0 0 0" />
<mass value="0.00334170288968694" />
<inertia ixx="1.59570753478215E-07" ixy="1.51460233550071E-10" ixz="-5.94291827952622E-08" iyy="1.78095663702073E-07" iyz="-1.24843061161549E-10" izz="1.34141410190336E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="middle_middle" />
<child link="middle_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="middle_pip" multiplier="0.89" offset="0" />
</joint>
<link name="ring_metacarpals">
<inertial>
<origin xyz="-0.00794299637971868 -2.94513098575422E-07 0.00220106138372037" rpy="0 0 0" />
<mass value="0.000895516612684195" />
<inertia ixx="3.46279813761428E-08" ixy="-3.92590701616938E-13" ixz="5.91029201975386E-09" iyy="2.854355219638E-08" iyz="-5.34642787239736E-13" izz="4.38102164225122E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_roll" type="revolute">
<origin xyz="0.00379999999632174 0.0117499999970695 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="ring_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="ring_proximal">
<inertial>
<origin xyz="-0.00114185484356009 3.47315809493265E-05 0.0228928405060199" rpy="0 0 0" />
<mass value="0.00450921353946778" />
<inertia ixx="6.66673081759646E-07" ixy="-1.07184694208142E-11" ixz="-2.53143399406658E-08" iyy="6.74195868060947E-07" iyz="-1.59054446512071E-10" izz="1.3575909184966E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0414586167948965 0" />
<parent link="ring_metacarpals" />
<child link="ring_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="ring_middle">
<inertial>
<origin xyz="-0.00020699321531438 2.60746916563859E-06 0.0167065326651803" rpy="0 0 0" />
<mass value="0.00237827433282015" />
<inertia ixx="2.29538714144179E-07" ixy="2.25259718797087E-12" ixz="-1.47340121880382E-08" iyy="1.76761312824203E-07" iyz="1.15994674932479E-11" izz="9.62797743780677E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
</collision>
</link>
<joint name="ring_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.0192827367948965 0" />
<parent link="ring_proximal" />
<child link="ring_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="ring_distal">
<inertial>
<origin xyz="-0.0081624655153107 2.27057324615476E-05 0.00811806561376854" rpy="0 0 0" />
<mass value="0.00334170280386792" />
<inertia ixx="1.59570757957895E-07" ixy="1.51461030930522E-10" ixz="-5.94291750853635E-08" iyy="1.78095650648522E-07" iyz="-1.2484716714453E-10" izz="1.34141394018405E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="ring_middle" />
<child link="ring_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="ring_pip" multiplier="0.89" offset="0" />
</joint>
<link name="pinky_metacarpals">
<inertial>
<origin xyz="-0.00794299617469867 -2.9300686296696E-07 0.00220106329602079" rpy="0 0 0" />
<mass value="0.000895516947513541" />
<inertia ixx="3.46279958591138E-08" ixy="-3.91785473098584E-13" ixz="5.91029262259818E-09" iyy="2.85435605328821E-08" iyz="-5.27457876763101E-13" izz="4.38102218637476E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_roll" type="revolute">
<origin xyz="0.00379999999998499 0.0352499999970695 0.1436" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="pinky_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="pinky_proximal">
<inertial>
<origin xyz="-0.00114185484350746 3.47315809787474E-05 0.0228928405060759" rpy="0 0 0" />
<mass value="0.00450921353949741" />
<inertia ixx="6.66673081761478E-07" ixy="-1.07184692555283E-11" ixz="-2.53143399404374E-08" iyy="6.74195868062215E-07" iyz="-1.59054445649156E-10" izz="1.35759091850319E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0414586167948965 0" />
<parent link="pinky_metacarpals" />
<child link="pinky_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="pinky_middle">
<inertial>
<origin xyz="-0.000206993248553502 2.60748984004522E-06 0.0167065326828462" rpy="0 0 0" />
<mass value="0.00237827431921004" />
<inertia ixx="2.29538713829186E-07" ixy="2.25292379523651E-12" ixz="-1.47340119230773E-08" iyy="1.76761312133621E-07" iyz="1.15993142153177E-11" izz="9.62797736254209E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.0192827367948965 0" />
<parent link="pinky_proximal" />
<child link="pinky_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="pinky_distal">
<inertial>
<origin xyz="-0.00816246615274486 2.27054189396725E-05 0.00811806634481432" rpy="0 0 0" />
<mass value="0.00334170283969146" />
<inertia ixx="1.59570758932551E-07" ixy="1.51458416419677E-10" ixz="-5.94291815262966E-08" iyy="1.7809566305509E-07" iyz="-1.24846982879422E-10" izz="1.34141398854287E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="pinky_middle" />
<child link="pinky_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="pinky_pip" multiplier="0.89" offset="0" />
</joint>
</robot>
@@ -0,0 +1,689 @@
<?xml version='1.0' encoding='utf-8'?>
<robot name="linkerhand_g20_left_zero_calibrated_20260805_154156">
<!-- G20 left camera-projected zero calibrated model.
Base URDF:
linkerhand_g20_left.urdf
Calibration source:
calibration_output/G20_LEFT_001/20260805_154156/
g20_left_G20_LEFT_001_calibration.json
Source quality:
quality.passed = true
random validation disabled (validation_mae_rad/p95_rad = null)
Projection-to-hand assumptions fixed by the three-camera fixture:
front: image +x = hand_base_link +Y, image y-up = +Z
side: image +x = hand_base_link -X, image y-up = +Z
top: image +x = hand_base_link +Y, image y-up = -X
Applied zero corrections:
thumb_cmc_pitch:
measured=-0.15586761 rad, base projected reference=-0.3676637606472593 rad
origin.rpy.y: -1.0647 -> -0.852903849352741
thumb_cmc_roll:
measured=+0.11385882 rad
origin.rpy.x: 0 -> +0.0541684625822638
thumb_cmc_yaw:
measured=+0.36708215 rad while thumb_cmc_roll command=145
local zero offset=+0.00976503700236708 rad, composed as origin * Rz(offset)
origin.rpy: [0.77165,0,0] ->
[0.7716261696611233,-0.006809288707327887,0.00699930654641584]
thumb_mcp:
measured=+0.17898738 rad
origin.rpy.y: 0 -> +0.0529044259379767
four-finger MCP pitch (index measurement inherited by middle/ring/pinky):
measured=-1.52488728 rad, nominal side projection=-pi/2
origin.rpy.y: 0 -> +0.0459090467948966
four-finger PIP (index measurement inherited by middle/ring/pinky):
measured=-1.56086048 rad, nominal side projection=-pi/2
origin.rpy.y: 0 -> +0.00993584679489645
Four-finger MCP roll uses the calibrated command curve:
zero_command_u8=128, travel_midpoint_rad=0.24779237.
The calibrated curve already maps command 127 to URDF q=0, so its origins
remain unchanged. Passive thumb_ip and four DIP joints have no independent
zero fields and also remain unchanged.
Only the robot name and the 12 joint-origin rotations listed above differ
from the base URDF. Joint axes, translations, limits and mimic definitions
are intentionally unchanged. This is a camera-projected kinematic-zero
model; tag mounting and fixture-axis assumptions remain part of its datum.
-->
<link name="hand_base_link">
<inertial>
<origin xyz="-0.000986417037198788 0.000268049028294054 0.0773798470852122" rpy="0 0 0" />
<mass value="0.110373495507463" />
<inertia ixx="0.000264402778830212" ixy="4.59263483005334E-07" ixz="1.32798258678817E-06" iyy="0.000159680177760236" iyz="-4.78660406501498E-06" izz="0.000118075546031988" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
</collision>
</link>
<link name="thumb_metacarpals_base1">
<inertial>
<origin xyz="0.0115155126192006 0.00398068846976699 0.000272888250745694" rpy="0 0 0" />
<mass value="0.0160351802817401" />
<inertia ixx="4.043875385429E-06" ixy="1.31113365770736E-07" ixz="-7.54220071752833E-09" iyy="2.42762975209742E-06" iyz="-1.4815076502312E-07" izz="2.08582778122043E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_roll" type="revolute">
<origin xyz="0.01916 -0.01 0.0436" rpy="0.0541684625822638 0 0" />
<parent link="hand_base_link" />
<child link="thumb_metacarpals_base1" />
<axis xyz="-1 0 0" />
<limit lower="0" upper="1.4" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals_base2">
<inertial>
<origin xyz="0.00286714610220221 -0.0061572653424441 0.00472605265322379" rpy="0 0 0" />
<mass value="0.00183264554882498" />
<inertia ixx="2.10633266517815E-07" ixy="-4.9133152227129E-08" ixz="-1.35746007921721E-08" iyy="9.83742137915545E-08" iyz="4.75450167610941E-08" izz="1.80097560989649E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_yaw" type="revolute">
<origin xyz="0.019845 0 0" rpy="0.7716261696611233 -0.006809288707327887 0.00699930654641584" />
<parent link="thumb_metacarpals_base1" />
<child link="thumb_metacarpals_base2" />
<axis xyz="0 0 1" />
<limit lower="0" upper="1.57" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals">
<inertial>
<origin xyz="0.00670657874489922 8.60758971228653E-05 0.0331412930049483" rpy="0 0 0" />
<mass value="0.0287160359417969" />
<inertia ixx="1.13355944686632E-05" ixy="-1.18725063259997E-08" ixz="5.09011416528365E-08" iyy="1.3528651001113E-05" iyz="2.41236037965108E-08" izz="3.37809997040935E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_pitch" type="revolute">
<origin xyz="0.0088218 -0.024245 0.0005" rpy="0 -0.852903849352741 1.9199" />
<parent link="thumb_metacarpals_base2" />
<child link="thumb_metacarpals" />
<axis xyz="0 1 0" />
<limit lower="0" upper="0.84" effort="100" velocity="1" />
</joint>
<link name="thumb_proximal">
<inertial>
<origin xyz="-0.00268734410969529 0.000568244522388137 0.0118501723044777" rpy="0 0 0" />
<mass value="0.00549015507433734" />
<inertia ixx="4.78372237673962E-07" ixy="4.56375652569348E-11" ixz="5.41461160604729E-08" iyy="6.23208954846975E-07" iyz="5.1129105291147E-09" izz="2.34934736039565E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_mcp" type="revolute">
<origin xyz="0.0127018248047608 0 0.0627256157349894" rpy="0 0.0529044259379767 0" />
<parent link="thumb_metacarpals" />
<child link="thumb_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.26" effort="100" velocity="1" />
</joint>
<link name="thumb_distal">
<inertial>
<origin xyz="-0.000710021799050622 -0.00267575921172081 0.0124762660846746" rpy="0 0 0" />
<mass value="0.00382046638843239" />
<inertia ixx="2.12120816159448E-07" ixy="-2.9588372102787E-09" ixz="-7.35950751454791E-08" iyy="2.67878295781821E-07" iyz="6.04616706384983E-09" izz="1.24571609570411E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_ip" type="revolute">
<origin xyz="-0.00483652209209624 0 0.0342226492098615" rpy="0 0 0" />
<parent link="thumb_proximal" />
<child link="thumb_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.29" effort="100" velocity="1" />
<mimic joint="thumb_mcp" multiplier="1.02" offset="0" />
</joint>
<link name="index_metacarpals">
<inertial>
<origin xyz="-0.00794299653637993 -2.93673969475883E-07 0.00220106027330236" rpy="0 0 0" />
<mass value="0.000895516429792169" />
<inertia ixx="3.46279728013563E-08" ixy="-3.91962517308058E-13" ixz="5.91029152662012E-09" iyy="2.85435471088518E-08" iyz="-5.30377507775974E-13" izz="4.38102133125935E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_roll" type="revolute">
<origin xyz="0.0038 -0.03525 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="index_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="index_proximal">
<inertial>
<origin xyz="-0.00114185484694667 3.47315751996757E-05 0.0228928405204552" rpy="0 0 0" />
<mass value="0.00450921353606138" />
<inertia ixx="6.66673080974796E-07" ixy="-1.07185172336921E-11" ixz="-2.53143396223457E-08" iyy="6.74195867168963E-07" iyz="-1.59054321114837E-10" izz="1.3575909170439E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0459090467948966 0" />
<parent link="index_metacarpals" />
<child link="index_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="index_middle">
<inertial>
<origin xyz="-0.000206991936024691 2.60617060293677E-06 0.0167065328961797" rpy="0 0 0" />
<mass value="0.00237827499847615" />
<inertia ixx="2.2953872847051E-07" ixy="2.23609392315326E-12" ixz="-1.4734008024399E-08" iyy="1.76761332719485E-07" iyz="1.15917246584962E-11" izz="9.62798051827835E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
</collision>
</link>
<joint name="index_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.00993584679489645 0" />
<parent link="index_proximal" />
<child link="index_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="index_distal">
<inertial>
<origin xyz="-0.00816246517948334 2.27058913663258E-05 0.00811806505013066" rpy="0 0 0" />
<mass value="0.00334170259772608" />
<inertia ixx="1.59570744991572E-07" ixy="1.51458653099843E-10" ixz="-5.9429168549018E-08" iyy="1.78095631426193E-07" iyz="-1.24842727205631E-10" izz="1.34141383450165E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<joint name="index_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="index_middle" />
<child link="index_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="index_pip" multiplier="0.89" offset="0" />
</joint>
<link name="middle_metacarpals">
<inertial>
<origin xyz="-0.00794299618184406 -2.93006659768391E-07 0.00220106329585604" rpy="0 0 0" />
<mass value="0.000895516947435045" />
<inertia ixx="3.46279958583857E-08" ixy="-3.91785866285381E-13" ixz="5.91029262290692E-09" iyy="2.85435605322585E-08" iyz="-5.27457529869591E-13" izz="4.38102218629515E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_roll" type="revolute">
<origin xyz="0.00379999999265848 -0.0117500000029305 0.1526" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="middle_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="middle_proximal">
<inertial>
<origin xyz="-0.00114185484818909 3.47315748623067E-05 0.0228928405191574" rpy="0 0 0" />
<mass value="0.00450921353536659" />
<inertia ixx="6.6667308094129E-07" ixy="-1.07185191777588E-11" ixz="-2.53143396278864E-08" iyy="6.74195867139795E-07" iyz="-1.59054330697635E-10" izz="1.3575909169764E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0459090467948966 0" />
<parent link="middle_metacarpals" />
<child link="middle_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="middle_middle">
<inertial>
<origin xyz="-0.000206991893084203 2.60624978909441E-06 0.0167065331356697" rpy="0 0 0" />
<mass value="0.00237827502274046" />
<inertia ixx="2.29538732170979E-07" ixy="2.23722006830829E-12" ixz="-1.47340061868536E-08" iyy="1.7676133625496E-07" iyz="1.15938574397121E-11" izz="9.62798057458164E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
</collision>
</link>
<joint name="middle_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.00993584679489645 0" />
<parent link="middle_proximal" />
<child link="middle_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="middle_distal">
<inertial>
<origin xyz="-0.00816246584707385 2.27058557035753E-05 0.00811806604834034" rpy="0 0 0" />
<mass value="0.00334170288968694" />
<inertia ixx="1.59570753478215E-07" ixy="1.51460233550071E-10" ixz="-5.94291827952622E-08" iyy="1.78095663702073E-07" iyz="-1.24843061161549E-10" izz="1.34141410190336E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="middle_middle" />
<child link="middle_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="middle_pip" multiplier="0.89" offset="0" />
</joint>
<link name="ring_metacarpals">
<inertial>
<origin xyz="-0.00794299637971868 -2.94513098575422E-07 0.00220106138372037" rpy="0 0 0" />
<mass value="0.000895516612684195" />
<inertia ixx="3.46279813761428E-08" ixy="-3.92590701616938E-13" ixz="5.91029201975386E-09" iyy="2.854355219638E-08" iyz="-5.34642787239736E-13" izz="4.38102164225122E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_roll" type="revolute">
<origin xyz="0.00379999999632174 0.0117499999970695 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="ring_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="ring_proximal">
<inertial>
<origin xyz="-0.00114185484356009 3.47315809493265E-05 0.0228928405060199" rpy="0 0 0" />
<mass value="0.00450921353946778" />
<inertia ixx="6.66673081759646E-07" ixy="-1.07184694208142E-11" ixz="-2.53143399406658E-08" iyy="6.74195868060947E-07" iyz="-1.59054446512071E-10" izz="1.3575909184966E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0459090467948966 0" />
<parent link="ring_metacarpals" />
<child link="ring_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="ring_middle">
<inertial>
<origin xyz="-0.00020699321531438 2.60746916563859E-06 0.0167065326651803" rpy="0 0 0" />
<mass value="0.00237827433282015" />
<inertia ixx="2.29538714144179E-07" ixy="2.25259718797087E-12" ixz="-1.47340121880382E-08" iyy="1.76761312824203E-07" iyz="1.15994674932479E-11" izz="9.62797743780677E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
</collision>
</link>
<joint name="ring_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.00993584679489645 0" />
<parent link="ring_proximal" />
<child link="ring_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="ring_distal">
<inertial>
<origin xyz="-0.0081624655153107 2.27057324615476E-05 0.00811806561376854" rpy="0 0 0" />
<mass value="0.00334170280386792" />
<inertia ixx="1.59570757957895E-07" ixy="1.51461030930522E-10" ixz="-5.94291750853635E-08" iyy="1.78095650648522E-07" iyz="-1.2484716714453E-10" izz="1.34141394018405E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="ring_middle" />
<child link="ring_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="ring_pip" multiplier="0.89" offset="0" />
</joint>
<link name="pinky_metacarpals">
<inertial>
<origin xyz="-0.00794299617469867 -2.9300686296696E-07 0.00220106329602079" rpy="0 0 0" />
<mass value="0.000895516947513541" />
<inertia ixx="3.46279958591138E-08" ixy="-3.91785473098584E-13" ixz="5.91029262259818E-09" iyy="2.85435605328821E-08" iyz="-5.27457876763101E-13" izz="4.38102218637476E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_roll" type="revolute">
<origin xyz="0.00379999999998499 0.0352499999970695 0.1436" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="pinky_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="pinky_proximal">
<inertial>
<origin xyz="-0.00114185484350746 3.47315809787474E-05 0.0228928405060759" rpy="0 0 0" />
<mass value="0.00450921353949741" />
<inertia ixx="6.66673081761478E-07" ixy="-1.07184692555283E-11" ixz="-2.53143399404374E-08" iyy="6.74195868062215E-07" iyz="-1.59054445649156E-10" izz="1.35759091850319E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0459090467948966 0" />
<parent link="pinky_metacarpals" />
<child link="pinky_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="pinky_middle">
<inertial>
<origin xyz="-0.000206993248553502 2.60748984004522E-06 0.0167065326828462" rpy="0 0 0" />
<mass value="0.00237827431921004" />
<inertia ixx="2.29538713829186E-07" ixy="2.25292379523651E-12" ixz="-1.47340119230773E-08" iyy="1.76761312133621E-07" iyz="1.15993142153177E-11" izz="9.62797736254209E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.00993584679489645 0" />
<parent link="pinky_proximal" />
<child link="pinky_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="pinky_distal">
<inertial>
<origin xyz="-0.00816246615274486 2.27054189396725E-05 0.00811806634481432" rpy="0 0 0" />
<mass value="0.00334170283969146" />
<inertia ixx="1.59570758932551E-07" ixy="1.51458416419677E-10" ixz="-5.94291815262966E-08" iyy="1.7809566305509E-07" iyz="-1.24846982879422E-10" izz="1.34141398854287E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="pinky_middle" />
<child link="pinky_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="pinky_pip" multiplier="0.89" offset="0" />
</joint>
</robot>
@@ -0,0 +1,688 @@
<?xml version='1.0' encoding='utf-8'?>
<robot name="linkerhand_g20_left_zero_calibrated_20260805_165056">
<!-- G20 left camera-projected zero calibrated model.
Base URDF:
linkerhand_g20_left.urdf
Calibration source:
calibration_output/G20_LEFT_001/20260805_165056/
g20_left_G20_LEFT_001_calibration.json
Source quality:
quality.passed = true
random validation disabled (validation_mae_rad/p95_rad = null)
Projection-to-hand assumptions fixed by the three-camera fixture:
front: image +x = hand_base_link +Y, image y-up = +Z
side: image +x = hand_base_link -X, image y-up = +Z
top: image +x = hand_base_link +Y, image y-up = -X
Applied zero corrections:
thumb_cmc_pitch:
measured=-0.16004248 rad, base projected reference=-0.3676637606472593 rad
origin.rpy.y: -1.0647 -> -0.8570787193527407
thumb_cmc_roll:
measured=+0.11705115 rad, base projected reference=+0.0780839079142253 rad
origin.rpy.x: 0 -> +0.0389672420857747
thumb_cmc_yaw:
measured=+0.35052345 rad while thumb_cmc_roll command=145
local zero offset=-0.006793662997632899 rad, composed as origin * Rz(offset)
origin.rpy: [0.77165,0,0] ->
[0.771638465812245,0.00473733050013081,-0.004869469585122743]
thumb_mcp:
measured=+0.17127076 rad
origin.rpy.y: 0 -> +0.045187805937976694
four-finger MCP pitch (index measurement inherited by middle/ring/pinky):
measured=-1.52588402 rad, nominal side projection=-pi/2
origin.rpy.y: 0 -> +0.04491230679489666
four-finger PIP (index measurement inherited by middle/ring/pinky):
measured=-1.56052579 rad, nominal side projection=-pi/2
origin.rpy.y: 0 -> +0.010270536794896534
Four-finger MCP roll uses the calibrated command curve:
zero_command_u8=127, travel_midpoint_rad=0.25155263.
The calibrated curve already maps command 127 to URDF q=0, so its origins
remain unchanged. Passive thumb_ip and four DIP joints have no independent
zero fields and also remain unchanged.
Only the robot name and the 12 joint-origin rotations listed above differ
from the base URDF. Joint axes, translations, limits and mimic definitions
are intentionally unchanged. This is a camera-projected kinematic-zero
model; tag mounting and fixture-axis assumptions remain part of its datum.
-->
<link name="hand_base_link">
<inertial>
<origin xyz="-0.000986417037198788 0.000268049028294054 0.0773798470852122" rpy="0 0 0" />
<mass value="0.110373495507463" />
<inertia ixx="0.000264402778830212" ixy="4.59263483005334E-07" ixz="1.32798258678817E-06" iyy="0.000159680177760236" iyz="-4.78660406501498E-06" izz="0.000118075546031988" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
</collision>
</link>
<link name="thumb_metacarpals_base1">
<inertial>
<origin xyz="0.0115155126192006 0.00398068846976699 0.000272888250745694" rpy="0 0 0" />
<mass value="0.0160351802817401" />
<inertia ixx="4.043875385429E-06" ixy="1.31113365770736E-07" ixz="-7.54220071752833E-09" iyy="2.42762975209742E-06" iyz="-1.4815076502312E-07" izz="2.08582778122043E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_roll" type="revolute">
<origin xyz="0.01916 -0.01 0.0436" rpy="0.0389672420857747 0 0" />
<parent link="hand_base_link" />
<child link="thumb_metacarpals_base1" />
<axis xyz="-1 0 0" />
<limit lower="0" upper="1.4" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals_base2">
<inertial>
<origin xyz="0.00286714610220221 -0.0061572653424441 0.00472605265322379" rpy="0 0 0" />
<mass value="0.00183264554882498" />
<inertia ixx="2.10633266517815E-07" ixy="-4.9133152227129E-08" ixz="-1.35746007921721E-08" iyy="9.83742137915545E-08" iyz="4.75450167610941E-08" izz="1.80097560989649E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_yaw" type="revolute">
<origin xyz="0.019845 0 0" rpy="0.771638465812245 0.00473733050013081 -0.004869469585122743" />
<parent link="thumb_metacarpals_base1" />
<child link="thumb_metacarpals_base2" />
<axis xyz="0 0 1" />
<limit lower="0" upper="1.57" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals">
<inertial>
<origin xyz="0.00670657874489922 8.60758971228653E-05 0.0331412930049483" rpy="0 0 0" />
<mass value="0.0287160359417969" />
<inertia ixx="1.13355944686632E-05" ixy="-1.18725063259997E-08" ixz="5.09011416528365E-08" iyy="1.3528651001113E-05" iyz="2.41236037965108E-08" izz="3.37809997040935E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_pitch" type="revolute">
<origin xyz="0.0088218 -0.024245 0.0005" rpy="0 -0.8570787193527407 1.9199" />
<parent link="thumb_metacarpals_base2" />
<child link="thumb_metacarpals" />
<axis xyz="0 1 0" />
<limit lower="0" upper="0.84" effort="100" velocity="1" />
</joint>
<link name="thumb_proximal">
<inertial>
<origin xyz="-0.00268734410969529 0.000568244522388137 0.0118501723044777" rpy="0 0 0" />
<mass value="0.00549015507433734" />
<inertia ixx="4.78372237673962E-07" ixy="4.56375652569348E-11" ixz="5.41461160604729E-08" iyy="6.23208954846975E-07" iyz="5.1129105291147E-09" izz="2.34934736039565E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_mcp" type="revolute">
<origin xyz="0.0127018248047608 0 0.0627256157349894" rpy="0 0.045187805937976694 0" />
<parent link="thumb_metacarpals" />
<child link="thumb_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.26" effort="100" velocity="1" />
</joint>
<link name="thumb_distal">
<inertial>
<origin xyz="-0.000710021799050622 -0.00267575921172081 0.0124762660846746" rpy="0 0 0" />
<mass value="0.00382046638843239" />
<inertia ixx="2.12120816159448E-07" ixy="-2.9588372102787E-09" ixz="-7.35950751454791E-08" iyy="2.67878295781821E-07" iyz="6.04616706384983E-09" izz="1.24571609570411E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_ip" type="revolute">
<origin xyz="-0.00483652209209624 0 0.0342226492098615" rpy="0 0 0" />
<parent link="thumb_proximal" />
<child link="thumb_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.29" effort="100" velocity="1" />
<mimic joint="thumb_mcp" multiplier="1.02" offset="0" />
</joint>
<link name="index_metacarpals">
<inertial>
<origin xyz="-0.00794299653637993 -2.93673969475883E-07 0.00220106027330236" rpy="0 0 0" />
<mass value="0.000895516429792169" />
<inertia ixx="3.46279728013563E-08" ixy="-3.91962517308058E-13" ixz="5.91029152662012E-09" iyy="2.85435471088518E-08" iyz="-5.30377507775974E-13" izz="4.38102133125935E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_roll" type="revolute">
<origin xyz="0.0038 -0.03525 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="index_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="index_proximal">
<inertial>
<origin xyz="-0.00114185484694667 3.47315751996757E-05 0.0228928405204552" rpy="0 0 0" />
<mass value="0.00450921353606138" />
<inertia ixx="6.66673080974796E-07" ixy="-1.07185172336921E-11" ixz="-2.53143396223457E-08" iyy="6.74195867168963E-07" iyz="-1.59054321114837E-10" izz="1.3575909170439E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="index_metacarpals" />
<child link="index_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="index_middle">
<inertial>
<origin xyz="-0.000206991936024691 2.60617060293677E-06 0.0167065328961797" rpy="0 0 0" />
<mass value="0.00237827499847615" />
<inertia ixx="2.2953872847051E-07" ixy="2.23609392315326E-12" ixz="-1.4734008024399E-08" iyy="1.76761332719485E-07" iyz="1.15917246584962E-11" izz="9.62798051827835E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
</collision>
</link>
<joint name="index_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.010270536794896534 0" />
<parent link="index_proximal" />
<child link="index_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="index_distal">
<inertial>
<origin xyz="-0.00816246517948334 2.27058913663258E-05 0.00811806505013066" rpy="0 0 0" />
<mass value="0.00334170259772608" />
<inertia ixx="1.59570744991572E-07" ixy="1.51458653099843E-10" ixz="-5.9429168549018E-08" iyy="1.78095631426193E-07" iyz="-1.24842727205631E-10" izz="1.34141383450165E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<joint name="index_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="index_middle" />
<child link="index_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="index_pip" multiplier="0.89" offset="0" />
</joint>
<link name="middle_metacarpals">
<inertial>
<origin xyz="-0.00794299618184406 -2.93006659768391E-07 0.00220106329585604" rpy="0 0 0" />
<mass value="0.000895516947435045" />
<inertia ixx="3.46279958583857E-08" ixy="-3.91785866285381E-13" ixz="5.91029262290692E-09" iyy="2.85435605322585E-08" iyz="-5.27457529869591E-13" izz="4.38102218629515E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_roll" type="revolute">
<origin xyz="0.00379999999265848 -0.0117500000029305 0.1526" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="middle_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="middle_proximal">
<inertial>
<origin xyz="-0.00114185484818909 3.47315748623067E-05 0.0228928405191574" rpy="0 0 0" />
<mass value="0.00450921353536659" />
<inertia ixx="6.6667308094129E-07" ixy="-1.07185191777588E-11" ixz="-2.53143396278864E-08" iyy="6.74195867139795E-07" iyz="-1.59054330697635E-10" izz="1.3575909169764E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="middle_metacarpals" />
<child link="middle_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="middle_middle">
<inertial>
<origin xyz="-0.000206991893084203 2.60624978909441E-06 0.0167065331356697" rpy="0 0 0" />
<mass value="0.00237827502274046" />
<inertia ixx="2.29538732170979E-07" ixy="2.23722006830829E-12" ixz="-1.47340061868536E-08" iyy="1.7676133625496E-07" iyz="1.15938574397121E-11" izz="9.62798057458164E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
</collision>
</link>
<joint name="middle_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.010270536794896534 0" />
<parent link="middle_proximal" />
<child link="middle_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="middle_distal">
<inertial>
<origin xyz="-0.00816246584707385 2.27058557035753E-05 0.00811806604834034" rpy="0 0 0" />
<mass value="0.00334170288968694" />
<inertia ixx="1.59570753478215E-07" ixy="1.51460233550071E-10" ixz="-5.94291827952622E-08" iyy="1.78095663702073E-07" iyz="-1.24843061161549E-10" izz="1.34141410190336E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="middle_middle" />
<child link="middle_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="middle_pip" multiplier="0.89" offset="0" />
</joint>
<link name="ring_metacarpals">
<inertial>
<origin xyz="-0.00794299637971868 -2.94513098575422E-07 0.00220106138372037" rpy="0 0 0" />
<mass value="0.000895516612684195" />
<inertia ixx="3.46279813761428E-08" ixy="-3.92590701616938E-13" ixz="5.91029201975386E-09" iyy="2.854355219638E-08" iyz="-5.34642787239736E-13" izz="4.38102164225122E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_roll" type="revolute">
<origin xyz="0.00379999999632174 0.0117499999970695 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="ring_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="ring_proximal">
<inertial>
<origin xyz="-0.00114185484356009 3.47315809493265E-05 0.0228928405060199" rpy="0 0 0" />
<mass value="0.00450921353946778" />
<inertia ixx="6.66673081759646E-07" ixy="-1.07184694208142E-11" ixz="-2.53143399406658E-08" iyy="6.74195868060947E-07" iyz="-1.59054446512071E-10" izz="1.3575909184966E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="ring_metacarpals" />
<child link="ring_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="ring_middle">
<inertial>
<origin xyz="-0.00020699321531438 2.60746916563859E-06 0.0167065326651803" rpy="0 0 0" />
<mass value="0.00237827433282015" />
<inertia ixx="2.29538714144179E-07" ixy="2.25259718797087E-12" ixz="-1.47340121880382E-08" iyy="1.76761312824203E-07" iyz="1.15994674932479E-11" izz="9.62797743780677E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
</collision>
</link>
<joint name="ring_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.010270536794896534 0" />
<parent link="ring_proximal" />
<child link="ring_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="ring_distal">
<inertial>
<origin xyz="-0.0081624655153107 2.27057324615476E-05 0.00811806561376854" rpy="0 0 0" />
<mass value="0.00334170280386792" />
<inertia ixx="1.59570757957895E-07" ixy="1.51461030930522E-10" ixz="-5.94291750853635E-08" iyy="1.78095650648522E-07" iyz="-1.2484716714453E-10" izz="1.34141394018405E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="ring_middle" />
<child link="ring_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="ring_pip" multiplier="0.89" offset="0" />
</joint>
<link name="pinky_metacarpals">
<inertial>
<origin xyz="-0.00794299617469867 -2.9300686296696E-07 0.00220106329602079" rpy="0 0 0" />
<mass value="0.000895516947513541" />
<inertia ixx="3.46279958591138E-08" ixy="-3.91785473098584E-13" ixz="5.91029262259818E-09" iyy="2.85435605328821E-08" iyz="-5.27457876763101E-13" izz="4.38102218637476E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_roll" type="revolute">
<origin xyz="0.00379999999998499 0.0352499999970695 0.1436" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="pinky_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="pinky_proximal">
<inertial>
<origin xyz="-0.00114185484350746 3.47315809787474E-05 0.0228928405060759" rpy="0 0 0" />
<mass value="0.00450921353949741" />
<inertia ixx="6.66673081761478E-07" ixy="-1.07184692555283E-11" ixz="-2.53143399404374E-08" iyy="6.74195868062215E-07" iyz="-1.59054445649156E-10" izz="1.35759091850319E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="pinky_metacarpals" />
<child link="pinky_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="pinky_middle">
<inertial>
<origin xyz="-0.000206993248553502 2.60748984004522E-06 0.0167065326828462" rpy="0 0 0" />
<mass value="0.00237827431921004" />
<inertia ixx="2.29538713829186E-07" ixy="2.25292379523651E-12" ixz="-1.47340119230773E-08" iyy="1.76761312133621E-07" iyz="1.15993142153177E-11" izz="9.62797736254209E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.010270536794896534 0" />
<parent link="pinky_proximal" />
<child link="pinky_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="pinky_distal">
<inertial>
<origin xyz="-0.00816246615274486 2.27054189396725E-05 0.00811806634481432" rpy="0 0 0" />
<mass value="0.00334170283969146" />
<inertia ixx="1.59570758932551E-07" ixy="1.51458416419677E-10" ixz="-5.94291815262966E-08" iyy="1.7809566305509E-07" iyz="-1.24846982879422E-10" izz="1.34141398854287E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="pinky_middle" />
<child link="pinky_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="pinky_pip" multiplier="0.89" offset="0" />
</joint>
</robot>
@@ -0,0 +1,706 @@
<?xml version='1.0' encoding='utf-8'?>
<robot name="linkerhand_g20_left_zero_calibrated_20260805_165056">
<!-- G20 left camera-projected zero calibrated model.
Base URDF:
linkerhand_g20_left.urdf
Calibration source:
calibration_output/G20_LEFT_001/20260805_165056/
g20_left_G20_LEFT_001_calibration.json
Source quality:
quality.passed = true
random validation disabled (validation_mae_rad/p95_rad = null)
Final zero-datum audit:
- Camera-projected measurements are not blindly treated as mechanical
joint offsets. Each projected zero is compared with its fixed base-model
datum before it is composed into the joint origin.
- thumb_cmc_roll uses the documented base projection 0.0780839079142253
rad and its command-255 pose has been checked against the real hand.
- thumb_cmc_yaw retains the command-255 local hand-zero that was checked
against the real hand with calibration 20260805_114742. The later top
camera projection moved by -0.09469537 rad and is not allowed to move
this mechanical datum.
- The other front/side projected zeros vary by at most 0.55 deg over the
four completed calibrations 114742/154156/161324/165056.
- The four MCP-roll origins stay unchanged: their calibrated curves put
command 127 at q=+0.002241 deg, effectively zero.
Projection-to-hand assumptions fixed by the three-camera fixture:
front: image +x = hand_base_link +Y, image y-up = +Z
side: image +x = hand_base_link -X, image y-up = +Z
top: image +x = hand_base_link +Y, image y-up = -X
Applied zero corrections:
thumb_cmc_pitch:
measured=-0.16004248 rad, base projected reference=-0.3676637606472593 rad
origin.rpy.y: -1.0647 -> -0.8570787193527407
thumb_cmc_roll:
measured=+0.11705115 rad, base projected reference=+0.0780839079142253 rad
origin.rpy.x: 0 -> +0.0389672420857747
thumb_cmc_yaw:
measured=+0.35052345 rad while thumb_cmc_roll command=145
the top-camera projected datum moved by -0.09469537 rad relative to the
previously hand-validated 20260805_114742 zero, although the mechanical
hand zero did not change. Retain that validated local hand-zero offset:
local zero offset=+0.0879017070023671 rad, composed as origin * Rz(offset)
origin.rpy: [0.77165,0,0] ->
[0.769716668496618,-0.0612549109009133,0.063083483004381]
thumb_mcp:
measured=+0.17127076 rad, fixed projected reference=+0.1260829540620233 rad
origin.rpy.y: 0 -> +0.045187805937976694
four-finger MCP pitch (index measurement inherited by middle/ring/pinky):
measured=-1.52588402 rad, nominal side projection=-pi/2
origin.rpy.y: 0 -> +0.04491230679489666
four-finger PIP (index measurement inherited by middle/ring/pinky):
measured=-1.56052579 rad, nominal side projection=-pi/2
origin.rpy.y: 0 -> +0.010270536794896534
Four-finger MCP roll uses the calibrated command curve:
zero_command_u8=127, travel_midpoint_rad=0.25155263.
The calibrated curve already maps command 127 to URDF q=0, so its origins
remain unchanged. Passive thumb_ip and four DIP joints have no independent
zero fields and also remain unchanged.
Only the robot name and the 12 joint-origin rotations listed above differ
from the base URDF. Joint axes, translations, limits and mimic definitions
are intentionally unchanged. This is a camera-projected kinematic-zero
model; tag mounting and fixture-axis assumptions remain part of its datum.
-->
<link name="hand_base_link">
<inertial>
<origin xyz="-0.000986417037198788 0.000268049028294054 0.0773798470852122" rpy="0 0 0" />
<mass value="0.110373495507463" />
<inertia ixx="0.000264402778830212" ixy="4.59263483005334E-07" ixz="1.32798258678817E-06" iyy="0.000159680177760236" iyz="-4.78660406501498E-06" izz="0.000118075546031988" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
</collision>
</link>
<link name="thumb_metacarpals_base1">
<inertial>
<origin xyz="0.0115155126192006 0.00398068846976699 0.000272888250745694" rpy="0 0 0" />
<mass value="0.0160351802817401" />
<inertia ixx="4.043875385429E-06" ixy="1.31113365770736E-07" ixz="-7.54220071752833E-09" iyy="2.42762975209742E-06" iyz="-1.4815076502312E-07" izz="2.08582778122043E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_roll" type="revolute">
<origin xyz="0.01916 -0.01 0.0436" rpy="0.0389672420857747 0 0" />
<parent link="hand_base_link" />
<child link="thumb_metacarpals_base1" />
<axis xyz="-1 0 0" />
<limit lower="0" upper="1.4" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals_base2">
<inertial>
<origin xyz="0.00286714610220221 -0.0061572653424441 0.00472605265322379" rpy="0 0 0" />
<mass value="0.00183264554882498" />
<inertia ixx="2.10633266517815E-07" ixy="-4.9133152227129E-08" ixz="-1.35746007921721E-08" iyy="9.83742137915545E-08" iyz="4.75450167610941E-08" izz="1.80097560989649E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_yaw" type="revolute">
<origin xyz="0.019845 0 0" rpy="0.769716668496618 -0.0612549109009133 0.063083483004381" />
<parent link="thumb_metacarpals_base1" />
<child link="thumb_metacarpals_base2" />
<axis xyz="0 0 1" />
<limit lower="0" upper="1.57" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals">
<inertial>
<origin xyz="0.00670657874489922 8.60758971228653E-05 0.0331412930049483" rpy="0 0 0" />
<mass value="0.0287160359417969" />
<inertia ixx="1.13355944686632E-05" ixy="-1.18725063259997E-08" ixz="5.09011416528365E-08" iyy="1.3528651001113E-05" iyz="2.41236037965108E-08" izz="3.37809997040935E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_pitch" type="revolute">
<origin xyz="0.0088218 -0.024245 0.0005" rpy="0 -0.8570787193527407 1.9199" />
<parent link="thumb_metacarpals_base2" />
<child link="thumb_metacarpals" />
<axis xyz="0 1 0" />
<limit lower="0" upper="0.84" effort="100" velocity="1" />
</joint>
<link name="thumb_proximal">
<inertial>
<origin xyz="-0.00268734410969529 0.000568244522388137 0.0118501723044777" rpy="0 0 0" />
<mass value="0.00549015507433734" />
<inertia ixx="4.78372237673962E-07" ixy="4.56375652569348E-11" ixz="5.41461160604729E-08" iyy="6.23208954846975E-07" iyz="5.1129105291147E-09" izz="2.34934736039565E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_mcp" type="revolute">
<origin xyz="0.0127018248047608 0 0.0627256157349894" rpy="0 0.045187805937976694 0" />
<parent link="thumb_metacarpals" />
<child link="thumb_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.26" effort="100" velocity="1" />
</joint>
<link name="thumb_distal">
<inertial>
<origin xyz="-0.000710021799050622 -0.00267575921172081 0.0124762660846746" rpy="0 0 0" />
<mass value="0.00382046638843239" />
<inertia ixx="2.12120816159448E-07" ixy="-2.9588372102787E-09" ixz="-7.35950751454791E-08" iyy="2.67878295781821E-07" iyz="6.04616706384983E-09" izz="1.24571609570411E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_ip" type="revolute">
<origin xyz="-0.00483652209209624 0 0.0342226492098615" rpy="0 0 0" />
<parent link="thumb_proximal" />
<child link="thumb_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.29" effort="100" velocity="1" />
<mimic joint="thumb_mcp" multiplier="1.02" offset="0" />
</joint>
<link name="index_metacarpals">
<inertial>
<origin xyz="-0.00794299653637993 -2.93673969475883E-07 0.00220106027330236" rpy="0 0 0" />
<mass value="0.000895516429792169" />
<inertia ixx="3.46279728013563E-08" ixy="-3.91962517308058E-13" ixz="5.91029152662012E-09" iyy="2.85435471088518E-08" iyz="-5.30377507775974E-13" izz="4.38102133125935E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_roll" type="revolute">
<origin xyz="0.0038 -0.03525 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="index_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="index_proximal">
<inertial>
<origin xyz="-0.00114185484694667 3.47315751996757E-05 0.0228928405204552" rpy="0 0 0" />
<mass value="0.00450921353606138" />
<inertia ixx="6.66673080974796E-07" ixy="-1.07185172336921E-11" ixz="-2.53143396223457E-08" iyy="6.74195867168963E-07" iyz="-1.59054321114837E-10" izz="1.3575909170439E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="index_metacarpals" />
<child link="index_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="index_middle">
<inertial>
<origin xyz="-0.000206991936024691 2.60617060293677E-06 0.0167065328961797" rpy="0 0 0" />
<mass value="0.00237827499847615" />
<inertia ixx="2.2953872847051E-07" ixy="2.23609392315326E-12" ixz="-1.4734008024399E-08" iyy="1.76761332719485E-07" iyz="1.15917246584962E-11" izz="9.62798051827835E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
</collision>
</link>
<joint name="index_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.010270536794896534 0" />
<parent link="index_proximal" />
<child link="index_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="index_distal">
<inertial>
<origin xyz="-0.00816246517948334 2.27058913663258E-05 0.00811806505013066" rpy="0 0 0" />
<mass value="0.00334170259772608" />
<inertia ixx="1.59570744991572E-07" ixy="1.51458653099843E-10" ixz="-5.9429168549018E-08" iyy="1.78095631426193E-07" iyz="-1.24842727205631E-10" izz="1.34141383450165E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<joint name="index_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="index_middle" />
<child link="index_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="index_pip" multiplier="0.89" offset="0" />
</joint>
<link name="middle_metacarpals">
<inertial>
<origin xyz="-0.00794299618184406 -2.93006659768391E-07 0.00220106329585604" rpy="0 0 0" />
<mass value="0.000895516947435045" />
<inertia ixx="3.46279958583857E-08" ixy="-3.91785866285381E-13" ixz="5.91029262290692E-09" iyy="2.85435605322585E-08" iyz="-5.27457529869591E-13" izz="4.38102218629515E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_roll" type="revolute">
<origin xyz="0.00379999999265848 -0.0117500000029305 0.1526" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="middle_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="middle_proximal">
<inertial>
<origin xyz="-0.00114185484818909 3.47315748623067E-05 0.0228928405191574" rpy="0 0 0" />
<mass value="0.00450921353536659" />
<inertia ixx="6.6667308094129E-07" ixy="-1.07185191777588E-11" ixz="-2.53143396278864E-08" iyy="6.74195867139795E-07" iyz="-1.59054330697635E-10" izz="1.3575909169764E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="middle_metacarpals" />
<child link="middle_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="middle_middle">
<inertial>
<origin xyz="-0.000206991893084203 2.60624978909441E-06 0.0167065331356697" rpy="0 0 0" />
<mass value="0.00237827502274046" />
<inertia ixx="2.29538732170979E-07" ixy="2.23722006830829E-12" ixz="-1.47340061868536E-08" iyy="1.7676133625496E-07" iyz="1.15938574397121E-11" izz="9.62798057458164E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
</collision>
</link>
<joint name="middle_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.010270536794896534 0" />
<parent link="middle_proximal" />
<child link="middle_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="middle_distal">
<inertial>
<origin xyz="-0.00816246584707385 2.27058557035753E-05 0.00811806604834034" rpy="0 0 0" />
<mass value="0.00334170288968694" />
<inertia ixx="1.59570753478215E-07" ixy="1.51460233550071E-10" ixz="-5.94291827952622E-08" iyy="1.78095663702073E-07" iyz="-1.24843061161549E-10" izz="1.34141410190336E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="middle_middle" />
<child link="middle_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="middle_pip" multiplier="0.89" offset="0" />
</joint>
<link name="ring_metacarpals">
<inertial>
<origin xyz="-0.00794299637971868 -2.94513098575422E-07 0.00220106138372037" rpy="0 0 0" />
<mass value="0.000895516612684195" />
<inertia ixx="3.46279813761428E-08" ixy="-3.92590701616938E-13" ixz="5.91029201975386E-09" iyy="2.854355219638E-08" iyz="-5.34642787239736E-13" izz="4.38102164225122E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_roll" type="revolute">
<origin xyz="0.00379999999632174 0.0117499999970695 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="ring_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="ring_proximal">
<inertial>
<origin xyz="-0.00114185484356009 3.47315809493265E-05 0.0228928405060199" rpy="0 0 0" />
<mass value="0.00450921353946778" />
<inertia ixx="6.66673081759646E-07" ixy="-1.07184694208142E-11" ixz="-2.53143399406658E-08" iyy="6.74195868060947E-07" iyz="-1.59054446512071E-10" izz="1.3575909184966E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="ring_metacarpals" />
<child link="ring_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="ring_middle">
<inertial>
<origin xyz="-0.00020699321531438 2.60746916563859E-06 0.0167065326651803" rpy="0 0 0" />
<mass value="0.00237827433282015" />
<inertia ixx="2.29538714144179E-07" ixy="2.25259718797087E-12" ixz="-1.47340121880382E-08" iyy="1.76761312824203E-07" iyz="1.15994674932479E-11" izz="9.62797743780677E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
</collision>
</link>
<joint name="ring_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.010270536794896534 0" />
<parent link="ring_proximal" />
<child link="ring_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="ring_distal">
<inertial>
<origin xyz="-0.0081624655153107 2.27057324615476E-05 0.00811806561376854" rpy="0 0 0" />
<mass value="0.00334170280386792" />
<inertia ixx="1.59570757957895E-07" ixy="1.51461030930522E-10" ixz="-5.94291750853635E-08" iyy="1.78095650648522E-07" iyz="-1.2484716714453E-10" izz="1.34141394018405E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="ring_middle" />
<child link="ring_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="ring_pip" multiplier="0.89" offset="0" />
</joint>
<link name="pinky_metacarpals">
<inertial>
<origin xyz="-0.00794299617469867 -2.9300686296696E-07 0.00220106329602079" rpy="0 0 0" />
<mass value="0.000895516947513541" />
<inertia ixx="3.46279958591138E-08" ixy="-3.91785473098584E-13" ixz="5.91029262259818E-09" iyy="2.85435605328821E-08" iyz="-5.27457876763101E-13" izz="4.38102218637476E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_roll" type="revolute">
<origin xyz="0.00379999999998499 0.0352499999970695 0.1436" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="pinky_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="pinky_proximal">
<inertial>
<origin xyz="-0.00114185484350746 3.47315809787474E-05 0.0228928405060759" rpy="0 0 0" />
<mass value="0.00450921353949741" />
<inertia ixx="6.66673081761478E-07" ixy="-1.07184692555283E-11" ixz="-2.53143399404374E-08" iyy="6.74195868062215E-07" iyz="-1.59054445649156E-10" izz="1.35759091850319E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="pinky_metacarpals" />
<child link="pinky_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="pinky_middle">
<inertial>
<origin xyz="-0.000206993248553502 2.60748984004522E-06 0.0167065326828462" rpy="0 0 0" />
<mass value="0.00237827431921004" />
<inertia ixx="2.29538713829186E-07" ixy="2.25292379523651E-12" ixz="-1.47340119230773E-08" iyy="1.76761312133621E-07" iyz="1.15993142153177E-11" izz="9.62797736254209E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.010270536794896534 0" />
<parent link="pinky_proximal" />
<child link="pinky_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="pinky_distal">
<inertial>
<origin xyz="-0.00816246615274486 2.27054189396725E-05 0.00811806634481432" rpy="0 0 0" />
<mass value="0.00334170283969146" />
<inertia ixx="1.59570758932551E-07" ixy="1.51458416419677E-10" ixz="-5.94291815262966E-08" iyy="1.7809566305509E-07" iyz="-1.24846982879422E-10" izz="1.34141398854287E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="pinky_middle" />
<child link="pinky_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="pinky_pip" multiplier="0.89" offset="0" />
</joint>
</robot>
@@ -0,0 +1,712 @@
<?xml version='1.0' encoding='utf-8'?>
<robot name="linkerhand_g20_left_zero_calibrated_20260805_165056">
<!-- G20 left camera-projected zero calibrated model.
Base URDF:
linkerhand_g20_left.urdf
Calibration source:
calibration_output/G20_LEFT_001/20260805_165056/
g20_left_G20_LEFT_001_calibration.json
Source quality:
quality.passed = true
random validation disabled (validation_mae_rad/p95_rad = null)
Final zero-datum audit:
- Camera-projected measurements are not blindly treated as mechanical
joint offsets. Each projected zero is compared with its fixed base-model
datum before it is composed into the joint origin.
- thumb_cmc_roll uses the documented base projection 0.0780839079142253
rad and its command-255 pose has been checked against the real hand.
- thumb_cmc_yaw retains the command-255 local hand-zero that was checked
against the real hand with calibration 20260805_114742. The later top
camera projection moved by -0.09469537 rad and is not allowed to move
this mechanical datum.
- The other front/side projected zeros vary by at most 0.55 deg over the
four completed calibrations 114742/154156/161324/165056.
- The four MCP-roll origins stay unchanged: their calibrated curves put
command 127 at q=+0.002241 deg, effectively zero.
- Position limits are expanded only where an active calibrated curve would
otherwise be clipped: thumb CMC yaw, four MCP pitch/roll and four PIP.
Passive thumb_ip is not expanded because its stored 2-D observation is a
cumulative link projection, while the URDF coordinate remains the
relative mimic joint thumb_mcp*1.02.
Projection-to-hand assumptions fixed by the three-camera fixture:
front: image +x = hand_base_link +Y, image y-up = +Z
side: image +x = hand_base_link -X, image y-up = +Z
top: image +x = hand_base_link +Y, image y-up = -X
Applied zero corrections:
thumb_cmc_pitch:
measured=-0.16004248 rad, base projected reference=-0.3676637606472593 rad
origin.rpy.y: -1.0647 -> -0.8570787193527407
thumb_cmc_roll:
measured=+0.11705115 rad, base projected reference=+0.0780839079142253 rad
origin.rpy.x: 0 -> +0.0389672420857747
thumb_cmc_yaw:
measured=+0.35052345 rad while thumb_cmc_roll command=145
the top-camera projected datum moved by -0.09469537 rad relative to the
previously hand-validated 20260805_114742 zero, although the mechanical
hand zero did not change. Retain that validated local hand-zero offset:
local zero offset=+0.0879017070023671 rad, composed as origin * Rz(offset)
origin.rpy: [0.77165,0,0] ->
[0.769716668496618,-0.0612549109009133,0.063083483004381]
thumb_mcp:
measured=+0.17127076 rad, fixed projected reference=+0.1260829540620233 rad
origin.rpy.y: 0 -> +0.045187805937976694
four-finger MCP pitch (index measurement inherited by middle/ring/pinky):
measured=-1.52588402 rad, nominal side projection=-pi/2
origin.rpy.y: 0 -> +0.04491230679489666
four-finger PIP (index measurement inherited by middle/ring/pinky):
measured=-1.56052579 rad, nominal side projection=-pi/2
origin.rpy.y: 0 -> +0.010270536794896534
Four-finger MCP roll uses the calibrated command curve:
zero_command_u8=127, travel_midpoint_rad=0.25155263.
The calibrated curve already maps command 127 to URDF q=0, so its origins
remain unchanged. Passive thumb_ip and four DIP joints have no independent
zero fields and also remain unchanged.
Only the robot name, the 12 joint-origin rotations listed above and 13
active-joint position limits differ from the base URDF. Joint axes,
translations, effort/velocity limits and mimic definitions are unchanged.
This is a camera-projected kinematic-zero model; tag mounting and fixture-axis
assumptions remain part of its datum.
-->
<link name="hand_base_link">
<inertial>
<origin xyz="-0.000986417037198788 0.000268049028294054 0.0773798470852122" rpy="0 0 0" />
<mass value="0.110373495507463" />
<inertia ixx="0.000264402778830212" ixy="4.59263483005334E-07" ixz="1.32798258678817E-06" iyy="0.000159680177760236" iyz="-4.78660406501498E-06" izz="0.000118075546031988" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
</collision>
</link>
<link name="thumb_metacarpals_base1">
<inertial>
<origin xyz="0.0115155126192006 0.00398068846976699 0.000272888250745694" rpy="0 0 0" />
<mass value="0.0160351802817401" />
<inertia ixx="4.043875385429E-06" ixy="1.31113365770736E-07" ixz="-7.54220071752833E-09" iyy="2.42762975209742E-06" iyz="-1.4815076502312E-07" izz="2.08582778122043E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_roll" type="revolute">
<origin xyz="0.01916 -0.01 0.0436" rpy="0.0389672420857747 0 0" />
<parent link="hand_base_link" />
<child link="thumb_metacarpals_base1" />
<axis xyz="-1 0 0" />
<limit lower="0" upper="1.4" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals_base2">
<inertial>
<origin xyz="0.00286714610220221 -0.0061572653424441 0.00472605265322379" rpy="0 0 0" />
<mass value="0.00183264554882498" />
<inertia ixx="2.10633266517815E-07" ixy="-4.9133152227129E-08" ixz="-1.35746007921721E-08" iyy="9.83742137915545E-08" iyz="4.75450167610941E-08" izz="1.80097560989649E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_yaw" type="revolute">
<origin xyz="0.019845 0 0" rpy="0.769716668496618 -0.0612549109009133 0.063083483004381" />
<parent link="thumb_metacarpals_base1" />
<child link="thumb_metacarpals_base2" />
<axis xyz="0 0 1" />
<limit lower="0" upper="1.61019336" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals">
<inertial>
<origin xyz="0.00670657874489922 8.60758971228653E-05 0.0331412930049483" rpy="0 0 0" />
<mass value="0.0287160359417969" />
<inertia ixx="1.13355944686632E-05" ixy="-1.18725063259997E-08" ixz="5.09011416528365E-08" iyy="1.3528651001113E-05" iyz="2.41236037965108E-08" izz="3.37809997040935E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_pitch" type="revolute">
<origin xyz="0.0088218 -0.024245 0.0005" rpy="0 -0.8570787193527407 1.9199" />
<parent link="thumb_metacarpals_base2" />
<child link="thumb_metacarpals" />
<axis xyz="0 1 0" />
<limit lower="0" upper="0.84" effort="100" velocity="1" />
</joint>
<link name="thumb_proximal">
<inertial>
<origin xyz="-0.00268734410969529 0.000568244522388137 0.0118501723044777" rpy="0 0 0" />
<mass value="0.00549015507433734" />
<inertia ixx="4.78372237673962E-07" ixy="4.56375652569348E-11" ixz="5.41461160604729E-08" iyy="6.23208954846975E-07" iyz="5.1129105291147E-09" izz="2.34934736039565E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_mcp" type="revolute">
<origin xyz="0.0127018248047608 0 0.0627256157349894" rpy="0 0.045187805937976694 0" />
<parent link="thumb_metacarpals" />
<child link="thumb_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.26" effort="100" velocity="1" />
</joint>
<link name="thumb_distal">
<inertial>
<origin xyz="-0.000710021799050622 -0.00267575921172081 0.0124762660846746" rpy="0 0 0" />
<mass value="0.00382046638843239" />
<inertia ixx="2.12120816159448E-07" ixy="-2.9588372102787E-09" ixz="-7.35950751454791E-08" iyy="2.67878295781821E-07" iyz="6.04616706384983E-09" izz="1.24571609570411E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_ip" type="revolute">
<origin xyz="-0.00483652209209624 0 0.0342226492098615" rpy="0 0 0" />
<parent link="thumb_proximal" />
<child link="thumb_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.29" effort="100" velocity="1" />
<mimic joint="thumb_mcp" multiplier="1.02" offset="0" />
</joint>
<link name="index_metacarpals">
<inertial>
<origin xyz="-0.00794299653637993 -2.93673969475883E-07 0.00220106027330236" rpy="0 0 0" />
<mass value="0.000895516429792169" />
<inertia ixx="3.46279728013563E-08" ixy="-3.91962517308058E-13" ixz="5.91029152662012E-09" iyy="2.85435471088518E-08" iyz="-5.30377507775974E-13" izz="4.38102133125935E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_roll" type="revolute">
<origin xyz="0.0038 -0.03525 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="index_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="index_proximal">
<inertial>
<origin xyz="-0.00114185484694667 3.47315751996757E-05 0.0228928405204552" rpy="0 0 0" />
<mass value="0.00450921353606138" />
<inertia ixx="6.66673080974796E-07" ixy="-1.07185172336921E-11" ixz="-2.53143396223457E-08" iyy="6.74195867168963E-07" iyz="-1.59054321114837E-10" izz="1.3575909170439E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="index_metacarpals" />
<child link="index_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="index_middle">
<inertial>
<origin xyz="-0.000206991936024691 2.60617060293677E-06 0.0167065328961797" rpy="0 0 0" />
<mass value="0.00237827499847615" />
<inertia ixx="2.2953872847051E-07" ixy="2.23609392315326E-12" ixz="-1.4734008024399E-08" iyy="1.76761332719485E-07" iyz="1.15917246584962E-11" izz="9.62798051827835E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
</collision>
</link>
<joint name="index_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.010270536794896534 0" />
<parent link="index_proximal" />
<child link="index_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="index_distal">
<inertial>
<origin xyz="-0.00816246517948334 2.27058913663258E-05 0.00811806505013066" rpy="0 0 0" />
<mass value="0.00334170259772608" />
<inertia ixx="1.59570744991572E-07" ixy="1.51458653099843E-10" ixz="-5.9429168549018E-08" iyy="1.78095631426193E-07" iyz="-1.24842727205631E-10" izz="1.34141383450165E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<joint name="index_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="index_middle" />
<child link="index_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="index_pip" multiplier="0.89" offset="0" />
</joint>
<link name="middle_metacarpals">
<inertial>
<origin xyz="-0.00794299618184406 -2.93006659768391E-07 0.00220106329585604" rpy="0 0 0" />
<mass value="0.000895516947435045" />
<inertia ixx="3.46279958583857E-08" ixy="-3.91785866285381E-13" ixz="5.91029262290692E-09" iyy="2.85435605322585E-08" iyz="-5.27457529869591E-13" izz="4.38102218629515E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_roll" type="revolute">
<origin xyz="0.00379999999265848 -0.0117500000029305 0.1526" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="middle_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="middle_proximal">
<inertial>
<origin xyz="-0.00114185484818909 3.47315748623067E-05 0.0228928405191574" rpy="0 0 0" />
<mass value="0.00450921353536659" />
<inertia ixx="6.6667308094129E-07" ixy="-1.07185191777588E-11" ixz="-2.53143396278864E-08" iyy="6.74195867139795E-07" iyz="-1.59054330697635E-10" izz="1.3575909169764E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="middle_metacarpals" />
<child link="middle_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="middle_middle">
<inertial>
<origin xyz="-0.000206991893084203 2.60624978909441E-06 0.0167065331356697" rpy="0 0 0" />
<mass value="0.00237827502274046" />
<inertia ixx="2.29538732170979E-07" ixy="2.23722006830829E-12" ixz="-1.47340061868536E-08" iyy="1.7676133625496E-07" iyz="1.15938574397121E-11" izz="9.62798057458164E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
</collision>
</link>
<joint name="middle_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.010270536794896534 0" />
<parent link="middle_proximal" />
<child link="middle_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="middle_distal">
<inertial>
<origin xyz="-0.00816246584707385 2.27058557035753E-05 0.00811806604834034" rpy="0 0 0" />
<mass value="0.00334170288968694" />
<inertia ixx="1.59570753478215E-07" ixy="1.51460233550071E-10" ixz="-5.94291827952622E-08" iyy="1.78095663702073E-07" iyz="-1.24843061161549E-10" izz="1.34141410190336E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="middle_middle" />
<child link="middle_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="middle_pip" multiplier="0.89" offset="0" />
</joint>
<link name="ring_metacarpals">
<inertial>
<origin xyz="-0.00794299637971868 -2.94513098575422E-07 0.00220106138372037" rpy="0 0 0" />
<mass value="0.000895516612684195" />
<inertia ixx="3.46279813761428E-08" ixy="-3.92590701616938E-13" ixz="5.91029201975386E-09" iyy="2.854355219638E-08" iyz="-5.34642787239736E-13" izz="4.38102164225122E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_roll" type="revolute">
<origin xyz="0.00379999999632174 0.0117499999970695 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="ring_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="ring_proximal">
<inertial>
<origin xyz="-0.00114185484356009 3.47315809493265E-05 0.0228928405060199" rpy="0 0 0" />
<mass value="0.00450921353946778" />
<inertia ixx="6.66673081759646E-07" ixy="-1.07184694208142E-11" ixz="-2.53143399406658E-08" iyy="6.74195868060947E-07" iyz="-1.59054446512071E-10" izz="1.3575909184966E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="ring_metacarpals" />
<child link="ring_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="ring_middle">
<inertial>
<origin xyz="-0.00020699321531438 2.60746916563859E-06 0.0167065326651803" rpy="0 0 0" />
<mass value="0.00237827433282015" />
<inertia ixx="2.29538714144179E-07" ixy="2.25259718797087E-12" ixz="-1.47340121880382E-08" iyy="1.76761312824203E-07" iyz="1.15994674932479E-11" izz="9.62797743780677E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
</collision>
</link>
<joint name="ring_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.010270536794896534 0" />
<parent link="ring_proximal" />
<child link="ring_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="ring_distal">
<inertial>
<origin xyz="-0.0081624655153107 2.27057324615476E-05 0.00811806561376854" rpy="0 0 0" />
<mass value="0.00334170280386792" />
<inertia ixx="1.59570757957895E-07" ixy="1.51461030930522E-10" ixz="-5.94291750853635E-08" iyy="1.78095650648522E-07" iyz="-1.2484716714453E-10" izz="1.34141394018405E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="ring_middle" />
<child link="ring_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="ring_pip" multiplier="0.89" offset="0" />
</joint>
<link name="pinky_metacarpals">
<inertial>
<origin xyz="-0.00794299617469867 -2.9300686296696E-07 0.00220106329602079" rpy="0 0 0" />
<mass value="0.000895516947513541" />
<inertia ixx="3.46279958591138E-08" ixy="-3.91785473098584E-13" ixz="5.91029262259818E-09" iyy="2.85435605328821E-08" iyz="-5.27457876763101E-13" izz="4.38102218637476E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_roll" type="revolute">
<origin xyz="0.00379999999998499 0.0352499999970695 0.1436" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="pinky_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="pinky_proximal">
<inertial>
<origin xyz="-0.00114185484350746 3.47315809787474E-05 0.0228928405060759" rpy="0 0 0" />
<mass value="0.00450921353949741" />
<inertia ixx="6.66673081761478E-07" ixy="-1.07184692555283E-11" ixz="-2.53143399404374E-08" iyy="6.74195868062215E-07" iyz="-1.59054445649156E-10" izz="1.35759091850319E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="pinky_metacarpals" />
<child link="pinky_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="pinky_middle">
<inertial>
<origin xyz="-0.000206993248553502 2.60748984004522E-06 0.0167065326828462" rpy="0 0 0" />
<mass value="0.00237827431921004" />
<inertia ixx="2.29538713829186E-07" ixy="2.25292379523651E-12" ixz="-1.47340119230773E-08" iyy="1.76761312133621E-07" iyz="1.15993142153177E-11" izz="9.62797736254209E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.010270536794896534 0" />
<parent link="pinky_proximal" />
<child link="pinky_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="pinky_distal">
<inertial>
<origin xyz="-0.00816246615274486 2.27054189396725E-05 0.00811806634481432" rpy="0 0 0" />
<mass value="0.00334170283969146" />
<inertia ixx="1.59570758932551E-07" ixy="1.51458416419677E-10" ixz="-5.94291815262966E-08" iyy="1.7809566305509E-07" iyz="-1.24846982879422E-10" izz="1.34141398854287E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="pinky_middle" />
<child link="pinky_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="pinky_pip" multiplier="0.89" offset="0" />
</joint>
</robot>
@@ -0,0 +1,688 @@
<?xml version='1.0' encoding='utf-8'?>
<robot name="linkerhand_g20_left_zero_calibrated_20260805_165056">
<!-- G20 left camera-projected zero calibrated model.
Base URDF:
linkerhand_g20_left.urdf
Calibration source:
calibration_output/G20_LEFT_001/20260805_165056/
g20_left_G20_LEFT_001_calibration.json
Source quality:
quality.passed = true
random validation disabled (validation_mae_rad/p95_rad = null)
Projection-to-hand assumptions fixed by the three-camera fixture:
front: image +x = hand_base_link +Y, image y-up = +Z
side: image +x = hand_base_link -X, image y-up = +Z
top: image +x = hand_base_link +Y, image y-up = -X
Applied zero corrections:
thumb_cmc_pitch:
measured=-0.16004248 rad, base projected reference=-0.3676637606472593 rad
origin.rpy.y: -1.0647 -> -0.8570787193527407
thumb_cmc_roll:
measured=+0.11705115 rad
origin.rpy.x: 0 -> +0.05736079258226381
thumb_cmc_yaw:
measured=+0.35052345 rad while thumb_cmc_roll command=145
local zero offset=-0.006793662997632899 rad, composed as origin * Rz(offset)
origin.rpy: [0.77165,0,0] ->
[0.771638465812245,0.00473733050013081,-0.004869469585122743]
thumb_mcp:
measured=+0.17127076 rad
origin.rpy.y: 0 -> +0.045187805937976694
four-finger MCP pitch (index measurement inherited by middle/ring/pinky):
measured=-1.52588402 rad, nominal side projection=-pi/2
origin.rpy.y: 0 -> +0.04491230679489666
four-finger PIP (index measurement inherited by middle/ring/pinky):
measured=-1.56052579 rad, nominal side projection=-pi/2
origin.rpy.y: 0 -> +0.010270536794896534
Four-finger MCP roll uses the calibrated command curve:
zero_command_u8=127, travel_midpoint_rad=0.25155263.
The calibrated curve already maps command 127 to URDF q=0, so its origins
remain unchanged. Passive thumb_ip and four DIP joints have no independent
zero fields and also remain unchanged.
Only the robot name and the 12 joint-origin rotations listed above differ
from the base URDF. Joint axes, translations, limits and mimic definitions
are intentionally unchanged. This is a camera-projected kinematic-zero
model; tag mounting and fixture-axis assumptions remain part of its datum.
-->
<link name="hand_base_link">
<inertial>
<origin xyz="-0.000986417037198788 0.000268049028294054 0.0773798470852122" rpy="0 0 0" />
<mass value="0.110373495507463" />
<inertia ixx="0.000264402778830212" ixy="4.59263483005334E-07" ixz="1.32798258678817E-06" iyy="0.000159680177760236" iyz="-4.78660406501498E-06" izz="0.000118075546031988" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
</collision>
</link>
<link name="thumb_metacarpals_base1">
<inertial>
<origin xyz="0.0115155126192006 0.00398068846976699 0.000272888250745694" rpy="0 0 0" />
<mass value="0.0160351802817401" />
<inertia ixx="4.043875385429E-06" ixy="1.31113365770736E-07" ixz="-7.54220071752833E-09" iyy="2.42762975209742E-06" iyz="-1.4815076502312E-07" izz="2.08582778122043E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_roll" type="revolute">
<origin xyz="0.01916 -0.01 0.0436" rpy="0.05736079258226381 0 0" />
<parent link="hand_base_link" />
<child link="thumb_metacarpals_base1" />
<axis xyz="-1 0 0" />
<limit lower="0" upper="1.4" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals_base2">
<inertial>
<origin xyz="0.00286714610220221 -0.0061572653424441 0.00472605265322379" rpy="0 0 0" />
<mass value="0.00183264554882498" />
<inertia ixx="2.10633266517815E-07" ixy="-4.9133152227129E-08" ixz="-1.35746007921721E-08" iyy="9.83742137915545E-08" iyz="4.75450167610941E-08" izz="1.80097560989649E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_yaw" type="revolute">
<origin xyz="0.019845 0 0" rpy="0.771638465812245 0.00473733050013081 -0.004869469585122743" />
<parent link="thumb_metacarpals_base1" />
<child link="thumb_metacarpals_base2" />
<axis xyz="0 0 1" />
<limit lower="0" upper="1.57" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals">
<inertial>
<origin xyz="0.00670657874489922 8.60758971228653E-05 0.0331412930049483" rpy="0 0 0" />
<mass value="0.0287160359417969" />
<inertia ixx="1.13355944686632E-05" ixy="-1.18725063259997E-08" ixz="5.09011416528365E-08" iyy="1.3528651001113E-05" iyz="2.41236037965108E-08" izz="3.37809997040935E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_pitch" type="revolute">
<origin xyz="0.0088218 -0.024245 0.0005" rpy="0 -0.8570787193527407 1.9199" />
<parent link="thumb_metacarpals_base2" />
<child link="thumb_metacarpals" />
<axis xyz="0 1 0" />
<limit lower="0" upper="0.84" effort="100" velocity="1" />
</joint>
<link name="thumb_proximal">
<inertial>
<origin xyz="-0.00268734410969529 0.000568244522388137 0.0118501723044777" rpy="0 0 0" />
<mass value="0.00549015507433734" />
<inertia ixx="4.78372237673962E-07" ixy="4.56375652569348E-11" ixz="5.41461160604729E-08" iyy="6.23208954846975E-07" iyz="5.1129105291147E-09" izz="2.34934736039565E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_mcp" type="revolute">
<origin xyz="0.0127018248047608 0 0.0627256157349894" rpy="0 0.045187805937976694 0" />
<parent link="thumb_metacarpals" />
<child link="thumb_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.26" effort="100" velocity="1" />
</joint>
<link name="thumb_distal">
<inertial>
<origin xyz="-0.000710021799050622 -0.00267575921172081 0.0124762660846746" rpy="0 0 0" />
<mass value="0.00382046638843239" />
<inertia ixx="2.12120816159448E-07" ixy="-2.9588372102787E-09" ixz="-7.35950751454791E-08" iyy="2.67878295781821E-07" iyz="6.04616706384983E-09" izz="1.24571609570411E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_ip" type="revolute">
<origin xyz="-0.00483652209209624 0 0.0342226492098615" rpy="0 0 0" />
<parent link="thumb_proximal" />
<child link="thumb_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.29" effort="100" velocity="1" />
<mimic joint="thumb_mcp" multiplier="1.02" offset="0" />
</joint>
<link name="index_metacarpals">
<inertial>
<origin xyz="-0.00794299653637993 -2.93673969475883E-07 0.00220106027330236" rpy="0 0 0" />
<mass value="0.000895516429792169" />
<inertia ixx="3.46279728013563E-08" ixy="-3.91962517308058E-13" ixz="5.91029152662012E-09" iyy="2.85435471088518E-08" iyz="-5.30377507775974E-13" izz="4.38102133125935E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_roll" type="revolute">
<origin xyz="0.0038 -0.03525 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="index_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="index_proximal">
<inertial>
<origin xyz="-0.00114185484694667 3.47315751996757E-05 0.0228928405204552" rpy="0 0 0" />
<mass value="0.00450921353606138" />
<inertia ixx="6.66673080974796E-07" ixy="-1.07185172336921E-11" ixz="-2.53143396223457E-08" iyy="6.74195867168963E-07" iyz="-1.59054321114837E-10" izz="1.3575909170439E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="index_metacarpals" />
<child link="index_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="index_middle">
<inertial>
<origin xyz="-0.000206991936024691 2.60617060293677E-06 0.0167065328961797" rpy="0 0 0" />
<mass value="0.00237827499847615" />
<inertia ixx="2.2953872847051E-07" ixy="2.23609392315326E-12" ixz="-1.4734008024399E-08" iyy="1.76761332719485E-07" iyz="1.15917246584962E-11" izz="9.62798051827835E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
</collision>
</link>
<joint name="index_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.010270536794896534 0" />
<parent link="index_proximal" />
<child link="index_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="index_distal">
<inertial>
<origin xyz="-0.00816246517948334 2.27058913663258E-05 0.00811806505013066" rpy="0 0 0" />
<mass value="0.00334170259772608" />
<inertia ixx="1.59570744991572E-07" ixy="1.51458653099843E-10" ixz="-5.9429168549018E-08" iyy="1.78095631426193E-07" iyz="-1.24842727205631E-10" izz="1.34141383450165E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<joint name="index_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="index_middle" />
<child link="index_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="index_pip" multiplier="0.89" offset="0" />
</joint>
<link name="middle_metacarpals">
<inertial>
<origin xyz="-0.00794299618184406 -2.93006659768391E-07 0.00220106329585604" rpy="0 0 0" />
<mass value="0.000895516947435045" />
<inertia ixx="3.46279958583857E-08" ixy="-3.91785866285381E-13" ixz="5.91029262290692E-09" iyy="2.85435605322585E-08" iyz="-5.27457529869591E-13" izz="4.38102218629515E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_roll" type="revolute">
<origin xyz="0.00379999999265848 -0.0117500000029305 0.1526" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="middle_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="middle_proximal">
<inertial>
<origin xyz="-0.00114185484818909 3.47315748623067E-05 0.0228928405191574" rpy="0 0 0" />
<mass value="0.00450921353536659" />
<inertia ixx="6.6667308094129E-07" ixy="-1.07185191777588E-11" ixz="-2.53143396278864E-08" iyy="6.74195867139795E-07" iyz="-1.59054330697635E-10" izz="1.3575909169764E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="middle_metacarpals" />
<child link="middle_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="middle_middle">
<inertial>
<origin xyz="-0.000206991893084203 2.60624978909441E-06 0.0167065331356697" rpy="0 0 0" />
<mass value="0.00237827502274046" />
<inertia ixx="2.29538732170979E-07" ixy="2.23722006830829E-12" ixz="-1.47340061868536E-08" iyy="1.7676133625496E-07" iyz="1.15938574397121E-11" izz="9.62798057458164E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
</collision>
</link>
<joint name="middle_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.010270536794896534 0" />
<parent link="middle_proximal" />
<child link="middle_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="middle_distal">
<inertial>
<origin xyz="-0.00816246584707385 2.27058557035753E-05 0.00811806604834034" rpy="0 0 0" />
<mass value="0.00334170288968694" />
<inertia ixx="1.59570753478215E-07" ixy="1.51460233550071E-10" ixz="-5.94291827952622E-08" iyy="1.78095663702073E-07" iyz="-1.24843061161549E-10" izz="1.34141410190336E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="middle_middle" />
<child link="middle_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="middle_pip" multiplier="0.89" offset="0" />
</joint>
<link name="ring_metacarpals">
<inertial>
<origin xyz="-0.00794299637971868 -2.94513098575422E-07 0.00220106138372037" rpy="0 0 0" />
<mass value="0.000895516612684195" />
<inertia ixx="3.46279813761428E-08" ixy="-3.92590701616938E-13" ixz="5.91029201975386E-09" iyy="2.854355219638E-08" iyz="-5.34642787239736E-13" izz="4.38102164225122E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_roll" type="revolute">
<origin xyz="0.00379999999632174 0.0117499999970695 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="ring_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="ring_proximal">
<inertial>
<origin xyz="-0.00114185484356009 3.47315809493265E-05 0.0228928405060199" rpy="0 0 0" />
<mass value="0.00450921353946778" />
<inertia ixx="6.66673081759646E-07" ixy="-1.07184694208142E-11" ixz="-2.53143399406658E-08" iyy="6.74195868060947E-07" iyz="-1.59054446512071E-10" izz="1.3575909184966E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="ring_metacarpals" />
<child link="ring_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="ring_middle">
<inertial>
<origin xyz="-0.00020699321531438 2.60746916563859E-06 0.0167065326651803" rpy="0 0 0" />
<mass value="0.00237827433282015" />
<inertia ixx="2.29538714144179E-07" ixy="2.25259718797087E-12" ixz="-1.47340121880382E-08" iyy="1.76761312824203E-07" iyz="1.15994674932479E-11" izz="9.62797743780677E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
</collision>
</link>
<joint name="ring_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.010270536794896534 0" />
<parent link="ring_proximal" />
<child link="ring_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="ring_distal">
<inertial>
<origin xyz="-0.0081624655153107 2.27057324615476E-05 0.00811806561376854" rpy="0 0 0" />
<mass value="0.00334170280386792" />
<inertia ixx="1.59570757957895E-07" ixy="1.51461030930522E-10" ixz="-5.94291750853635E-08" iyy="1.78095650648522E-07" iyz="-1.2484716714453E-10" izz="1.34141394018405E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="ring_middle" />
<child link="ring_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="ring_pip" multiplier="0.89" offset="0" />
</joint>
<link name="pinky_metacarpals">
<inertial>
<origin xyz="-0.00794299617469867 -2.9300686296696E-07 0.00220106329602079" rpy="0 0 0" />
<mass value="0.000895516947513541" />
<inertia ixx="3.46279958591138E-08" ixy="-3.91785473098584E-13" ixz="5.91029262259818E-09" iyy="2.85435605328821E-08" iyz="-5.27457876763101E-13" izz="4.38102218637476E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_roll" type="revolute">
<origin xyz="0.00379999999998499 0.0352499999970695 0.1436" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="pinky_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.23" upper="0.23" effort="100" velocity="1" />
</joint>
<link name="pinky_proximal">
<inertial>
<origin xyz="-0.00114185484350746 3.47315809787474E-05 0.0228928405060759" rpy="0 0 0" />
<mass value="0.00450921353949741" />
<inertia ixx="6.66673081761478E-07" ixy="-1.07184692555283E-11" ixz="-2.53143399404374E-08" iyy="6.74195868062215E-07" iyz="-1.59054445649156E-10" izz="1.35759091850319E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.04491230679489666 0" />
<parent link="pinky_metacarpals" />
<child link="pinky_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22" effort="100" velocity="1" />
</joint>
<link name="pinky_middle">
<inertial>
<origin xyz="-0.000206993248553502 2.60748984004522E-06 0.0167065326828462" rpy="0 0 0" />
<mass value="0.00237827431921004" />
<inertia ixx="2.29538713829186E-07" ixy="2.25292379523651E-12" ixz="-1.47340119230773E-08" iyy="1.76761312133621E-07" iyz="1.15993142153177E-11" izz="9.62797736254209E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.010270536794896534 0" />
<parent link="pinky_proximal" />
<child link="pinky_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.74" effort="100" velocity="1" />
</joint>
<link name="pinky_distal">
<inertial>
<origin xyz="-0.00816246615274486 2.27054189396725E-05 0.00811806634481432" rpy="0 0 0" />
<mass value="0.00334170283969146" />
<inertia ixx="1.59570758932551E-07" ixy="1.51458416419677E-10" ixz="-5.94291815262966E-08" iyy="1.7809566305509E-07" iyz="-1.24846982879422E-10" izz="1.34141398854287E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="pinky_middle" />
<child link="pinky_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.55" effort="100" velocity="1" />
<mimic joint="pinky_pip" multiplier="0.89" offset="0" />
</joint>
</robot>
@@ -0,0 +1,658 @@
<?xml version='1.0' encoding='utf-8'?>
<!-- G20 left camera-projected zero calibrated from TAG trajectories.
Source: g20_left_G20_LEFT_001_calibration(4).json
schema_version=3 quality.passed=True
Tag layout (from user photos):
palm/base tag; thumb base/mid/tip tags; index tip (+ side chain tags);
middle finger proximal/middle/distal tags used for four-finger inheritance.
Applied origin corrections (CAD * local delta):
thumb_cmc_pitch measured=-0.16004248 CAD_ref=-0.3676637606472593 -> rpy=-1.11022302462516e-16 -0.857078719352741 1.9199
thumb_cmc_roll measured=0.11705115 CAD_ref=0.0780839079142253 -> rpy=0.0389672420857747 0 0
thumb_cmc_yaw measured=0.35052345 ref=0.3573171129976329 -> rpy=0.771638465812245 0.00473733050013081 -0.00486946958512274
thumb_mcp measured=0.17127076 ref=0.1260829540620233 -> rpy=0 0.0451878059379767 0
four-finger MCP pitch measured=-1.52588402 nominal=-pi/2 -> rpy.y=0.044912306794896883
four-finger PIP measured=-1.56052579 nominal=-pi/2 -> rpy.y=0.010270536794896534
Runtime: active joints use calibration JSON lookup; thumb_ip uses relative mimic
(JSON thumb_ip is cumulative tip projection, not relative URDF q).
-->
<robot name="linkerhand_g20_left_zero_calibrated_from_calibration4_20260806">
<link name="hand_base_link">
<inertial>
<origin xyz="-0.000986417037198788 0.000268049028294054 0.0773798470852122" rpy="0 0 0" />
<mass value="0.110373495507463" />
<inertia ixx="0.000264402778830212" ixy="4.59263483005334E-07" ixz="1.32798258678817E-06" iyy="0.000159680177760236" iyz="-4.78660406501498E-06" izz="0.000118075546031988" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
</collision>
</link>
<link name="thumb_metacarpals_base1">
<inertial>
<origin xyz="0.0115155126192006 0.00398068846976699 0.000272888250745694" rpy="0 0 0" />
<mass value="0.0160351802817401" />
<inertia ixx="4.043875385429E-06" ixy="1.31113365770736E-07" ixz="-7.54220071752833E-09" iyy="2.42762975209742E-06" iyz="-1.4815076502312E-07" izz="2.08582778122043E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_roll" type="revolute">
<origin xyz="0.01916 -0.01 0.0436" rpy="0.0389672420857747 0 0" />
<parent link="hand_base_link" />
<child link="thumb_metacarpals_base1" />
<axis xyz="-1 0 0" />
<limit lower="0" upper="1.38358796" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals_base2">
<inertial>
<origin xyz="0.00286714610220221 -0.0061572653424441 0.00472605265322379" rpy="0 0 0" />
<mass value="0.00183264554882498" />
<inertia ixx="2.10633266517815E-07" ixy="-4.9133152227129E-08" ixz="-1.35746007921721E-08" iyy="9.83742137915545E-08" iyz="4.75450167610941E-08" izz="1.80097560989649E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_yaw" type="revolute">
<origin xyz="0.019845 0 0" rpy="0.771638465812245 0.00473733050013081 -0.00486946958512274" />
<parent link="thumb_metacarpals_base1" />
<child link="thumb_metacarpals_base2" />
<axis xyz="0 0 1" />
<limit lower="0" upper="1.61019336" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals">
<inertial>
<origin xyz="0.00670657874489922 8.60758971228653E-05 0.0331412930049483" rpy="0 0 0" />
<mass value="0.0287160359417969" />
<inertia ixx="1.13355944686632E-05" ixy="-1.18725063259997E-08" ixz="5.09011416528365E-08" iyy="1.3528651001113E-05" iyz="2.41236037965108E-08" izz="3.37809997040935E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_pitch" type="revolute">
<origin xyz="0.0088218 -0.024245 0.0005" rpy="0 -0.857078719352741 1.9199" />
<parent link="thumb_metacarpals_base2" />
<child link="thumb_metacarpals" />
<axis xyz="0 1 0" />
<limit lower="0" upper="0.82302235" effort="100" velocity="1" />
</joint>
<link name="thumb_proximal">
<inertial>
<origin xyz="-0.00268734410969529 0.000568244522388137 0.0118501723044777" rpy="0 0 0" />
<mass value="0.00549015507433734" />
<inertia ixx="4.78372237673962E-07" ixy="4.56375652569348E-11" ixz="5.41461160604729E-08" iyy="6.23208954846975E-07" iyz="5.1129105291147E-09" izz="2.34934736039565E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_mcp" type="revolute">
<origin xyz="0.0127018248047608 0 0.0627256157349894" rpy="0 0.0451878059379767 0" />
<parent link="thumb_metacarpals" />
<child link="thumb_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.20296658" effort="100" velocity="1" />
</joint>
<link name="thumb_distal">
<inertial>
<origin xyz="-0.000710021799050622 -0.00267575921172081 0.0124762660846746" rpy="0 0 0" />
<mass value="0.00382046638843239" />
<inertia ixx="2.12120816159448E-07" ixy="-2.9588372102787E-09" ixz="-7.35950751454791E-08" iyy="2.67878295781821E-07" iyz="6.04616706384983E-09" izz="1.24571609570411E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_ip" type="revolute">
<origin xyz="-0.00483652209209624 0 0.0342226492098615" rpy="0 0 0" />
<parent link="thumb_proximal" />
<child link="thumb_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.29" effort="100" velocity="1" />
<mimic joint="thumb_mcp" multiplier="1.02" offset="0" />
</joint>
<link name="index_metacarpals">
<inertial>
<origin xyz="-0.00794299653637993 -2.93673969475883E-07 0.00220106027330236" rpy="0 0 0" />
<mass value="0.000895516429792169" />
<inertia ixx="3.46279728013563E-08" ixy="-3.91962517308058E-13" ixz="5.91029152662012E-09" iyy="2.85435471088518E-08" iyz="-5.30377507775974E-13" izz="4.38102133125935E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_roll" type="revolute">
<origin xyz="0.0038 -0.03525 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="index_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="index_proximal">
<inertial>
<origin xyz="-0.00114185484694667 3.47315751996757E-05 0.0228928405204552" rpy="0 0 0" />
<mass value="0.00450921353606138" />
<inertia ixx="6.66673080974796E-07" ixy="-1.07185172336921E-11" ixz="-2.53143396223457E-08" iyy="6.74195867168963E-07" iyz="-1.59054321114837E-10" izz="1.3575909170439E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0449123067948969 0" />
<parent link="index_metacarpals" />
<child link="index_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="index_middle">
<inertial>
<origin xyz="-0.000206991936024691 2.60617060293677E-06 0.0167065328961797" rpy="0 0 0" />
<mass value="0.00237827499847615" />
<inertia ixx="2.2953872847051E-07" ixy="2.23609392315326E-12" ixz="-1.4734008024399E-08" iyy="1.76761332719485E-07" iyz="1.15917246584962E-11" izz="9.62798051827835E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
</collision>
</link>
<joint name="index_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.0102705367948965 0" />
<parent link="index_proximal" />
<child link="index_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="index_distal">
<inertial>
<origin xyz="-0.00816246517948334 2.27058913663258E-05 0.00811806505013066" rpy="0 0 0" />
<mass value="0.00334170259772608" />
<inertia ixx="1.59570744991572E-07" ixy="1.51458653099843E-10" ixz="-5.9429168549018E-08" iyy="1.78095631426193E-07" iyz="-1.24842727205631E-10" izz="1.34141383450165E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<joint name="index_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="index_middle" />
<child link="index_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.49304833" effort="100" velocity="1" />
<mimic joint="index_pip" multiplier="0.89" offset="0" />
</joint>
<link name="middle_metacarpals">
<inertial>
<origin xyz="-0.00794299618184406 -2.93006659768391E-07 0.00220106329585604" rpy="0 0 0" />
<mass value="0.000895516947435045" />
<inertia ixx="3.46279958583857E-08" ixy="-3.91785866285381E-13" ixz="5.91029262290692E-09" iyy="2.85435605322585E-08" iyz="-5.27457529869591E-13" izz="4.38102218629515E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_roll" type="revolute">
<origin xyz="0.00379999999265848 -0.0117500000029305 0.1526" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="middle_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="middle_proximal">
<inertial>
<origin xyz="-0.00114185484818909 3.47315748623067E-05 0.0228928405191574" rpy="0 0 0" />
<mass value="0.00450921353536659" />
<inertia ixx="6.6667308094129E-07" ixy="-1.07185191777588E-11" ixz="-2.53143396278864E-08" iyy="6.74195867139795E-07" iyz="-1.59054330697635E-10" izz="1.3575909169764E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0449123067948969 0" />
<parent link="middle_metacarpals" />
<child link="middle_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="middle_middle">
<inertial>
<origin xyz="-0.000206991893084203 2.60624978909441E-06 0.0167065331356697" rpy="0 0 0" />
<mass value="0.00237827502274046" />
<inertia ixx="2.29538732170979E-07" ixy="2.23722006830829E-12" ixz="-1.47340061868536E-08" iyy="1.7676133625496E-07" iyz="1.15938574397121E-11" izz="9.62798057458164E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
</collision>
</link>
<joint name="middle_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.0102705367948965 0" />
<parent link="middle_proximal" />
<child link="middle_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="middle_distal">
<inertial>
<origin xyz="-0.00816246584707385 2.27058557035753E-05 0.00811806604834034" rpy="0 0 0" />
<mass value="0.00334170288968694" />
<inertia ixx="1.59570753478215E-07" ixy="1.51460233550071E-10" ixz="-5.94291827952622E-08" iyy="1.78095663702073E-07" iyz="-1.24843061161549E-10" izz="1.34141410190336E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="middle_middle" />
<child link="middle_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.49304833" effort="100" velocity="1" />
<mimic joint="middle_pip" multiplier="0.89" offset="0" />
</joint>
<link name="ring_metacarpals">
<inertial>
<origin xyz="-0.00794299637971868 -2.94513098575422E-07 0.00220106138372037" rpy="0 0 0" />
<mass value="0.000895516612684195" />
<inertia ixx="3.46279813761428E-08" ixy="-3.92590701616938E-13" ixz="5.91029201975386E-09" iyy="2.854355219638E-08" iyz="-5.34642787239736E-13" izz="4.38102164225122E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_roll" type="revolute">
<origin xyz="0.00379999999632174 0.0117499999970695 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="ring_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="ring_proximal">
<inertial>
<origin xyz="-0.00114185484356009 3.47315809493265E-05 0.0228928405060199" rpy="0 0 0" />
<mass value="0.00450921353946778" />
<inertia ixx="6.66673081759646E-07" ixy="-1.07184694208142E-11" ixz="-2.53143399406658E-08" iyy="6.74195868060947E-07" iyz="-1.59054446512071E-10" izz="1.3575909184966E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0449123067948969 0" />
<parent link="ring_metacarpals" />
<child link="ring_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="ring_middle">
<inertial>
<origin xyz="-0.00020699321531438 2.60746916563859E-06 0.0167065326651803" rpy="0 0 0" />
<mass value="0.00237827433282015" />
<inertia ixx="2.29538714144179E-07" ixy="2.25259718797087E-12" ixz="-1.47340121880382E-08" iyy="1.76761312824203E-07" iyz="1.15994674932479E-11" izz="9.62797743780677E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
</collision>
</link>
<joint name="ring_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.0102705367948965 0" />
<parent link="ring_proximal" />
<child link="ring_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="ring_distal">
<inertial>
<origin xyz="-0.0081624655153107 2.27057324615476E-05 0.00811806561376854" rpy="0 0 0" />
<mass value="0.00334170280386792" />
<inertia ixx="1.59570757957895E-07" ixy="1.51461030930522E-10" ixz="-5.94291750853635E-08" iyy="1.78095650648522E-07" iyz="-1.2484716714453E-10" izz="1.34141394018405E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="ring_middle" />
<child link="ring_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.49304833" effort="100" velocity="1" />
<mimic joint="ring_pip" multiplier="0.89" offset="0" />
</joint>
<link name="pinky_metacarpals">
<inertial>
<origin xyz="-0.00794299617469867 -2.9300686296696E-07 0.00220106329602079" rpy="0 0 0" />
<mass value="0.000895516947513541" />
<inertia ixx="3.46279958591138E-08" ixy="-3.91785473098584E-13" ixz="5.91029262259818E-09" iyy="2.85435605328821E-08" iyz="-5.27457876763101E-13" izz="4.38102218637476E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_roll" type="revolute">
<origin xyz="0.00379999999998499 0.0352499999970695 0.1436" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="pinky_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="pinky_proximal">
<inertial>
<origin xyz="-0.00114185484350746 3.47315809787474E-05 0.0228928405060759" rpy="0 0 0" />
<mass value="0.00450921353949741" />
<inertia ixx="6.66673081761478E-07" ixy="-1.07184692555283E-11" ixz="-2.53143399404374E-08" iyy="6.74195868062215E-07" iyz="-1.59054445649156E-10" izz="1.35759091850319E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0449123067948969 0" />
<parent link="pinky_metacarpals" />
<child link="pinky_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="pinky_middle">
<inertial>
<origin xyz="-0.000206993248553502 2.60748984004522E-06 0.0167065326828462" rpy="0 0 0" />
<mass value="0.00237827431921004" />
<inertia ixx="2.29538713829186E-07" ixy="2.25292379523651E-12" ixz="-1.47340119230773E-08" iyy="1.76761312133621E-07" iyz="1.15993142153177E-11" izz="9.62797736254209E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.0102705367948965 0" />
<parent link="pinky_proximal" />
<child link="pinky_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="pinky_distal">
<inertial>
<origin xyz="-0.00816246615274486 2.27054189396725E-05 0.00811806634481432" rpy="0 0 0" />
<mass value="0.00334170283969146" />
<inertia ixx="1.59570758932551E-07" ixy="1.51458416419677E-10" ixz="-5.94291815262966E-08" iyy="1.7809566305509E-07" iyz="-1.24846982879422E-10" izz="1.34141398854287E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="pinky_middle" />
<child link="pinky_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.49304833" effort="100" velocity="1" />
<mimic joint="pinky_pip" multiplier="0.89" offset="0" />
</joint>
</robot>
@@ -0,0 +1,662 @@
<?xml version='1.0' encoding='utf-8'?>
<!-- G20 left camera-projected zero calibrated from TAG trajectories.
Source: g20_left_G20_LEFT_001_calibration(4).json
schema_version=3 quality.passed=True
Tag layout (from user photos):
palm/base tag; thumb base/mid/tip tags; index tip (+ side chain tags);
middle finger proximal/middle/distal tags used for four-finger inheritance.
Applied origin corrections (CAD * local delta):
thumb_cmc_pitch measured=-0.16004248 CAD_ref=-0.3676637606472593 +extra_flex=0.04
-> rpy=0.0 -0.8170787193527406 1.9199
thumb_cmc_roll measured=0.11705115 CAD_ref=0.0780839079142253 -> rpy=0.0389672420857747 0 0
thumb_cmc_yaw half of pose-confirmed offset (photos: full restore overshot)
offset=0.04395085350118355 (full confirmed=0.0879017070023671)
-> rpy=0.7711671141556778 -0.0306426875620437 0.03151212571987987
thumb_mcp measured=0.17127076 ref=0.1260829540620233 +extra_flex=0.03
-> rpy=0.0 0.07518780593797669 0.0
four-finger MCP pitch measured=-1.52588402 nominal=-pi/2 -> rpy.y=0.044912306794896883
four-finger PIP measured=-1.56052579 nominal=-pi/2 -> rpy.y=0.010270536794896534
Runtime: active joints use calibration JSON lookup; thumb_ip uses relative mimic
(JSON thumb_ip is cumulative tip projection, not relative URDF q).
-->
<robot name="linkerhand_g20_left_zero_calibrated_from_calibration4_yaw_pitch_tune_20260806">
<link name="hand_base_link">
<inertial>
<origin xyz="-0.000986417037198788 0.000268049028294054 0.0773798470852122" rpy="0 0 0" />
<mass value="0.110373495507463" />
<inertia ixx="0.000264402778830212" ixy="4.59263483005334E-07" ixz="1.32798258678817E-06" iyy="0.000159680177760236" iyz="-4.78660406501498E-06" izz="0.000118075546031988" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
</collision>
</link>
<link name="thumb_metacarpals_base1">
<inertial>
<origin xyz="0.0115155126192006 0.00398068846976699 0.000272888250745694" rpy="0 0 0" />
<mass value="0.0160351802817401" />
<inertia ixx="4.043875385429E-06" ixy="1.31113365770736E-07" ixz="-7.54220071752833E-09" iyy="2.42762975209742E-06" iyz="-1.4815076502312E-07" izz="2.08582778122043E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_roll" type="revolute">
<origin xyz="0.01916 -0.01 0.0436" rpy="0.0389672420857747 0 0" />
<parent link="hand_base_link" />
<child link="thumb_metacarpals_base1" />
<axis xyz="-1 0 0" />
<limit lower="0" upper="1.38358796" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals_base2">
<inertial>
<origin xyz="0.00286714610220221 -0.0061572653424441 0.00472605265322379" rpy="0 0 0" />
<mass value="0.00183264554882498" />
<inertia ixx="2.10633266517815E-07" ixy="-4.9133152227129E-08" ixz="-1.35746007921721E-08" iyy="9.83742137915545E-08" iyz="4.75450167610941E-08" izz="1.80097560989649E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_yaw" type="revolute">
<origin xyz="0.019845 0 0" rpy="0.7711671141556778 -0.0306426875620437 0.03151212571987987" />
<parent link="thumb_metacarpals_base1" />
<child link="thumb_metacarpals_base2" />
<axis xyz="0 0 1" />
<limit lower="0" upper="1.61019336" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals">
<inertial>
<origin xyz="0.00670657874489922 8.60758971228653E-05 0.0331412930049483" rpy="0 0 0" />
<mass value="0.0287160359417969" />
<inertia ixx="1.13355944686632E-05" ixy="-1.18725063259997E-08" ixz="5.09011416528365E-08" iyy="1.3528651001113E-05" iyz="2.41236037965108E-08" izz="3.37809997040935E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_pitch" type="revolute">
<origin xyz="0.0088218 -0.024245 0.0005" rpy="0.0 -0.8170787193527406 1.9199" />
<parent link="thumb_metacarpals_base2" />
<child link="thumb_metacarpals" />
<axis xyz="0 1 0" />
<limit lower="0" upper="0.82302235" effort="100" velocity="1" />
</joint>
<link name="thumb_proximal">
<inertial>
<origin xyz="-0.00268734410969529 0.000568244522388137 0.0118501723044777" rpy="0 0 0" />
<mass value="0.00549015507433734" />
<inertia ixx="4.78372237673962E-07" ixy="4.56375652569348E-11" ixz="5.41461160604729E-08" iyy="6.23208954846975E-07" iyz="5.1129105291147E-09" izz="2.34934736039565E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_mcp" type="revolute">
<origin xyz="0.0127018248047608 0 0.0627256157349894" rpy="0.0 0.07518780593797669 0.0" />
<parent link="thumb_metacarpals" />
<child link="thumb_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.20296658" effort="100" velocity="1" />
</joint>
<link name="thumb_distal">
<inertial>
<origin xyz="-0.000710021799050622 -0.00267575921172081 0.0124762660846746" rpy="0 0 0" />
<mass value="0.00382046638843239" />
<inertia ixx="2.12120816159448E-07" ixy="-2.9588372102787E-09" ixz="-7.35950751454791E-08" iyy="2.67878295781821E-07" iyz="6.04616706384983E-09" izz="1.24571609570411E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_ip" type="revolute">
<origin xyz="-0.00483652209209624 0 0.0342226492098615" rpy="0 0 0" />
<parent link="thumb_proximal" />
<child link="thumb_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.29" effort="100" velocity="1" />
<mimic joint="thumb_mcp" multiplier="1.02" offset="0" />
</joint>
<link name="index_metacarpals">
<inertial>
<origin xyz="-0.00794299653637993 -2.93673969475883E-07 0.00220106027330236" rpy="0 0 0" />
<mass value="0.000895516429792169" />
<inertia ixx="3.46279728013563E-08" ixy="-3.91962517308058E-13" ixz="5.91029152662012E-09" iyy="2.85435471088518E-08" iyz="-5.30377507775974E-13" izz="4.38102133125935E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_roll" type="revolute">
<origin xyz="0.0038 -0.03525 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="index_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="index_proximal">
<inertial>
<origin xyz="-0.00114185484694667 3.47315751996757E-05 0.0228928405204552" rpy="0 0 0" />
<mass value="0.00450921353606138" />
<inertia ixx="6.66673080974796E-07" ixy="-1.07185172336921E-11" ixz="-2.53143396223457E-08" iyy="6.74195867168963E-07" iyz="-1.59054321114837E-10" izz="1.3575909170439E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0449123067948969 0" />
<parent link="index_metacarpals" />
<child link="index_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="index_middle">
<inertial>
<origin xyz="-0.000206991936024691 2.60617060293677E-06 0.0167065328961797" rpy="0 0 0" />
<mass value="0.00237827499847615" />
<inertia ixx="2.2953872847051E-07" ixy="2.23609392315326E-12" ixz="-1.4734008024399E-08" iyy="1.76761332719485E-07" iyz="1.15917246584962E-11" izz="9.62798051827835E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
</collision>
</link>
<joint name="index_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.0102705367948965 0" />
<parent link="index_proximal" />
<child link="index_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="index_distal">
<inertial>
<origin xyz="-0.00816246517948334 2.27058913663258E-05 0.00811806505013066" rpy="0 0 0" />
<mass value="0.00334170259772608" />
<inertia ixx="1.59570744991572E-07" ixy="1.51458653099843E-10" ixz="-5.9429168549018E-08" iyy="1.78095631426193E-07" iyz="-1.24842727205631E-10" izz="1.34141383450165E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<joint name="index_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="index_middle" />
<child link="index_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.49304833" effort="100" velocity="1" />
<mimic joint="index_pip" multiplier="0.89" offset="0" />
</joint>
<link name="middle_metacarpals">
<inertial>
<origin xyz="-0.00794299618184406 -2.93006659768391E-07 0.00220106329585604" rpy="0 0 0" />
<mass value="0.000895516947435045" />
<inertia ixx="3.46279958583857E-08" ixy="-3.91785866285381E-13" ixz="5.91029262290692E-09" iyy="2.85435605322585E-08" iyz="-5.27457529869591E-13" izz="4.38102218629515E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_roll" type="revolute">
<origin xyz="0.00379999999265848 -0.0117500000029305 0.1526" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="middle_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="middle_proximal">
<inertial>
<origin xyz="-0.00114185484818909 3.47315748623067E-05 0.0228928405191574" rpy="0 0 0" />
<mass value="0.00450921353536659" />
<inertia ixx="6.6667308094129E-07" ixy="-1.07185191777588E-11" ixz="-2.53143396278864E-08" iyy="6.74195867139795E-07" iyz="-1.59054330697635E-10" izz="1.3575909169764E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0449123067948969 0" />
<parent link="middle_metacarpals" />
<child link="middle_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="middle_middle">
<inertial>
<origin xyz="-0.000206991893084203 2.60624978909441E-06 0.0167065331356697" rpy="0 0 0" />
<mass value="0.00237827502274046" />
<inertia ixx="2.29538732170979E-07" ixy="2.23722006830829E-12" ixz="-1.47340061868536E-08" iyy="1.7676133625496E-07" iyz="1.15938574397121E-11" izz="9.62798057458164E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
</collision>
</link>
<joint name="middle_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.0102705367948965 0" />
<parent link="middle_proximal" />
<child link="middle_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="middle_distal">
<inertial>
<origin xyz="-0.00816246584707385 2.27058557035753E-05 0.00811806604834034" rpy="0 0 0" />
<mass value="0.00334170288968694" />
<inertia ixx="1.59570753478215E-07" ixy="1.51460233550071E-10" ixz="-5.94291827952622E-08" iyy="1.78095663702073E-07" iyz="-1.24843061161549E-10" izz="1.34141410190336E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="middle_middle" />
<child link="middle_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.49304833" effort="100" velocity="1" />
<mimic joint="middle_pip" multiplier="0.89" offset="0" />
</joint>
<link name="ring_metacarpals">
<inertial>
<origin xyz="-0.00794299637971868 -2.94513098575422E-07 0.00220106138372037" rpy="0 0 0" />
<mass value="0.000895516612684195" />
<inertia ixx="3.46279813761428E-08" ixy="-3.92590701616938E-13" ixz="5.91029201975386E-09" iyy="2.854355219638E-08" iyz="-5.34642787239736E-13" izz="4.38102164225122E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_roll" type="revolute">
<origin xyz="0.00379999999632174 0.0117499999970695 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="ring_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="ring_proximal">
<inertial>
<origin xyz="-0.00114185484356009 3.47315809493265E-05 0.0228928405060199" rpy="0 0 0" />
<mass value="0.00450921353946778" />
<inertia ixx="6.66673081759646E-07" ixy="-1.07184694208142E-11" ixz="-2.53143399406658E-08" iyy="6.74195868060947E-07" iyz="-1.59054446512071E-10" izz="1.3575909184966E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0449123067948969 0" />
<parent link="ring_metacarpals" />
<child link="ring_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="ring_middle">
<inertial>
<origin xyz="-0.00020699321531438 2.60746916563859E-06 0.0167065326651803" rpy="0 0 0" />
<mass value="0.00237827433282015" />
<inertia ixx="2.29538714144179E-07" ixy="2.25259718797087E-12" ixz="-1.47340121880382E-08" iyy="1.76761312824203E-07" iyz="1.15994674932479E-11" izz="9.62797743780677E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
</collision>
</link>
<joint name="ring_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.0102705367948965 0" />
<parent link="ring_proximal" />
<child link="ring_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="ring_distal">
<inertial>
<origin xyz="-0.0081624655153107 2.27057324615476E-05 0.00811806561376854" rpy="0 0 0" />
<mass value="0.00334170280386792" />
<inertia ixx="1.59570757957895E-07" ixy="1.51461030930522E-10" ixz="-5.94291750853635E-08" iyy="1.78095650648522E-07" iyz="-1.2484716714453E-10" izz="1.34141394018405E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="ring_middle" />
<child link="ring_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.49304833" effort="100" velocity="1" />
<mimic joint="ring_pip" multiplier="0.89" offset="0" />
</joint>
<link name="pinky_metacarpals">
<inertial>
<origin xyz="-0.00794299617469867 -2.9300686296696E-07 0.00220106329602079" rpy="0 0 0" />
<mass value="0.000895516947513541" />
<inertia ixx="3.46279958591138E-08" ixy="-3.91785473098584E-13" ixz="5.91029262259818E-09" iyy="2.85435605328821E-08" iyz="-5.27457876763101E-13" izz="4.38102218637476E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_roll" type="revolute">
<origin xyz="0.00379999999998499 0.0352499999970695 0.1436" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="pinky_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="pinky_proximal">
<inertial>
<origin xyz="-0.00114185484350746 3.47315809787474E-05 0.0228928405060759" rpy="0 0 0" />
<mass value="0.00450921353949741" />
<inertia ixx="6.66673081761478E-07" ixy="-1.07184692555283E-11" ixz="-2.53143399404374E-08" iyy="6.74195868062215E-07" iyz="-1.59054445649156E-10" izz="1.35759091850319E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0449123067948969 0" />
<parent link="pinky_metacarpals" />
<child link="pinky_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="pinky_middle">
<inertial>
<origin xyz="-0.000206993248553502 2.60748984004522E-06 0.0167065326828462" rpy="0 0 0" />
<mass value="0.00237827431921004" />
<inertia ixx="2.29538713829186E-07" ixy="2.25292379523651E-12" ixz="-1.47340119230773E-08" iyy="1.76761312133621E-07" iyz="1.15993142153177E-11" izz="9.62797736254209E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.0102705367948965 0" />
<parent link="pinky_proximal" />
<child link="pinky_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="pinky_distal">
<inertial>
<origin xyz="-0.00816246615274486 2.27054189396725E-05 0.00811806634481432" rpy="0 0 0" />
<mass value="0.00334170283969146" />
<inertia ixx="1.59570758932551E-07" ixy="1.51458416419677E-10" ixz="-5.94291815262966E-08" iyy="1.7809566305509E-07" iyz="-1.24846982879422E-10" izz="1.34141398854287E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="pinky_middle" />
<child link="pinky_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.49304833" effort="100" velocity="1" />
<mimic joint="pinky_pip" multiplier="0.89" offset="0" />
</joint>
</robot>
@@ -0,0 +1,660 @@
<?xml version='1.0' encoding='utf-8'?>
<!-- G20 left camera-projected zero calibrated from TAG trajectories.
Source: g20_left_G20_LEFT_001_calibration(4).json
schema_version=3 quality.passed=True
Tag layout (from user photos):
palm/base tag; thumb base/mid/tip tags; index tip (+ side chain tags);
middle finger proximal/middle/distal tags used for four-finger inheritance.
Applied origin corrections (CAD * local delta):
thumb_cmc_pitch measured=-0.16004248 CAD_ref=-0.3676637606472593 -> rpy=-1.11022302462516e-16 -0.857078719352741 1.9199
thumb_cmc_roll measured=0.11705115 CAD_ref=0.0780839079142253 -> rpy=0.0389672420857747 0 0
thumb_cmc_yaw RESTORED pose-confirmed frame from 20260805_114742/v3
(calib4 table_projected_zero=0.35052345 nearly cancelled CAD; unreliable alone).
measured=+0.44521882 @ roll_cmd=145, local_offset=+0.087901707 -> rpy=0.769716668496618 -0.0612549109009133 0.063083483004381
thumb_mcp measured=0.17127076 ref=0.1260829540620233 -> rpy=0 0.0451878059379767 0
four-finger MCP pitch measured=-1.52588402 nominal=-pi/2 -> rpy.y=0.044912306794896883
four-finger PIP measured=-1.56052579 nominal=-pi/2 -> rpy.y=0.010270536794896534
Runtime: active joints use calibration JSON lookup; thumb_ip uses relative mimic
(JSON thumb_ip is cumulative tip projection, not relative URDF q).
-->
<robot name="linkerhand_g20_left_zero_calibrated_from_calibration4_yawfix_20260806">
<link name="hand_base_link">
<inertial>
<origin xyz="-0.000986417037198788 0.000268049028294054 0.0773798470852122" rpy="0 0 0" />
<mass value="0.110373495507463" />
<inertia ixx="0.000264402778830212" ixy="4.59263483005334E-07" ixz="1.32798258678817E-06" iyy="0.000159680177760236" iyz="-4.78660406501498E-06" izz="0.000118075546031988" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/hand_base_link.STL" />
</geometry>
</collision>
</link>
<link name="thumb_metacarpals_base1">
<inertial>
<origin xyz="0.0115155126192006 0.00398068846976699 0.000272888250745694" rpy="0 0 0" />
<mass value="0.0160351802817401" />
<inertia ixx="4.043875385429E-06" ixy="1.31113365770736E-07" ixz="-7.54220071752833E-09" iyy="2.42762975209742E-06" iyz="-1.4815076502312E-07" izz="2.08582778122043E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_roll" type="revolute">
<origin xyz="0.01916 -0.01 0.0436" rpy="0.0389672420857747 0 0" />
<parent link="hand_base_link" />
<child link="thumb_metacarpals_base1" />
<axis xyz="-1 0 0" />
<limit lower="0" upper="1.38358796" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals_base2">
<inertial>
<origin xyz="0.00286714610220221 -0.0061572653424441 0.00472605265322379" rpy="0 0 0" />
<mass value="0.00183264554882498" />
<inertia ixx="2.10633266517815E-07" ixy="-4.9133152227129E-08" ixz="-1.35746007921721E-08" iyy="9.83742137915545E-08" iyz="4.75450167610941E-08" izz="1.80097560989649E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_yaw" type="revolute">
<origin xyz="0.019845 0 0" rpy="0.769716668496618 -0.0612549109009133 0.063083483004381" />
<parent link="thumb_metacarpals_base1" />
<child link="thumb_metacarpals_base2" />
<axis xyz="0 0 1" />
<limit lower="0" upper="1.61019336" effort="100" velocity="1" />
</joint>
<link name="thumb_metacarpals">
<inertial>
<origin xyz="0.00670657874489922 8.60758971228653E-05 0.0331412930049483" rpy="0 0 0" />
<mass value="0.0287160359417969" />
<inertia ixx="1.13355944686632E-05" ixy="-1.18725063259997E-08" ixz="5.09011416528365E-08" iyy="1.3528651001113E-05" iyz="2.41236037965108E-08" izz="3.37809997040935E-06" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_cmc_pitch" type="revolute">
<origin xyz="0.0088218 -0.024245 0.0005" rpy="0 -0.857078719352741 1.9199" />
<parent link="thumb_metacarpals_base2" />
<child link="thumb_metacarpals" />
<axis xyz="0 1 0" />
<limit lower="0" upper="0.82302235" effort="100" velocity="1" />
</joint>
<link name="thumb_proximal">
<inertial>
<origin xyz="-0.00268734410969529 0.000568244522388137 0.0118501723044777" rpy="0 0 0" />
<mass value="0.00549015507433734" />
<inertia ixx="4.78372237673962E-07" ixy="4.56375652569348E-11" ixz="5.41461160604729E-08" iyy="6.23208954846975E-07" iyz="5.1129105291147E-09" izz="2.34934736039565E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_mcp" type="revolute">
<origin xyz="0.0127018248047608 0 0.0627256157349894" rpy="0 0.0451878059379767 0" />
<parent link="thumb_metacarpals" />
<child link="thumb_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.20296658" effort="100" velocity="1" />
</joint>
<link name="thumb_distal">
<inertial>
<origin xyz="-0.000710021799050622 -0.00267575921172081 0.0124762660846746" rpy="0 0 0" />
<mass value="0.00382046638843239" />
<inertia ixx="2.12120816159448E-07" ixy="-2.9588372102787E-09" ixz="-7.35950751454791E-08" iyy="2.67878295781821E-07" iyz="6.04616706384983E-09" izz="1.24571609570411E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<joint name="thumb_ip" type="revolute">
<origin xyz="-0.00483652209209624 0 0.0342226492098615" rpy="0 0 0" />
<parent link="thumb_proximal" />
<child link="thumb_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.29" effort="100" velocity="1" />
<mimic joint="thumb_mcp" multiplier="1.02" offset="0" />
</joint>
<link name="index_metacarpals">
<inertial>
<origin xyz="-0.00794299653637993 -2.93673969475883E-07 0.00220106027330236" rpy="0 0 0" />
<mass value="0.000895516429792169" />
<inertia ixx="3.46279728013563E-08" ixy="-3.91962517308058E-13" ixz="5.91029152662012E-09" iyy="2.85435471088518E-08" iyz="-5.30377507775974E-13" izz="4.38102133125935E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_roll" type="revolute">
<origin xyz="0.0038 -0.03525 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="index_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="index_proximal">
<inertial>
<origin xyz="-0.00114185484694667 3.47315751996757E-05 0.0228928405204552" rpy="0 0 0" />
<mass value="0.00450921353606138" />
<inertia ixx="6.66673080974796E-07" ixy="-1.07185172336921E-11" ixz="-2.53143396223457E-08" iyy="6.74195867168963E-07" iyz="-1.59054321114837E-10" izz="1.3575909170439E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="index_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0449123067948969 0" />
<parent link="index_metacarpals" />
<child link="index_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="index_middle">
<inertial>
<origin xyz="-0.000206991936024691 2.60617060293677E-06 0.0167065328961797" rpy="0 0 0" />
<mass value="0.00237827499847615" />
<inertia ixx="2.2953872847051E-07" ixy="2.23609392315326E-12" ixz="-1.4734008024399E-08" iyy="1.76761332719485E-07" iyz="1.15917246584962E-11" izz="9.62798051827835E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_middle.STL" />
</geometry>
</collision>
</link>
<joint name="index_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.0102705367948965 0" />
<parent link="index_proximal" />
<child link="index_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="index_distal">
<inertial>
<origin xyz="-0.00816246517948334 2.27058913663258E-05 0.00811806505013066" rpy="0 0 0" />
<mass value="0.00334170259772608" />
<inertia ixx="1.59570744991572E-07" ixy="1.51458653099843E-10" ixz="-5.9429168549018E-08" iyy="1.78095631426193E-07" iyz="-1.24842727205631E-10" izz="1.34141383450165E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<joint name="index_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="index_middle" />
<child link="index_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.49304833" effort="100" velocity="1" />
<mimic joint="index_pip" multiplier="0.89" offset="0" />
</joint>
<link name="middle_metacarpals">
<inertial>
<origin xyz="-0.00794299618184406 -2.93006659768391E-07 0.00220106329585604" rpy="0 0 0" />
<mass value="0.000895516947435045" />
<inertia ixx="3.46279958583857E-08" ixy="-3.91785866285381E-13" ixz="5.91029262290692E-09" iyy="2.85435605322585E-08" iyz="-5.27457529869591E-13" izz="4.38102218629515E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_roll" type="revolute">
<origin xyz="0.00379999999265848 -0.0117500000029305 0.1526" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="middle_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="middle_proximal">
<inertial>
<origin xyz="-0.00114185484818909 3.47315748623067E-05 0.0228928405191574" rpy="0 0 0" />
<mass value="0.00450921353536659" />
<inertia ixx="6.6667308094129E-07" ixy="-1.07185191777588E-11" ixz="-2.53143396278864E-08" iyy="6.74195867139795E-07" iyz="-1.59054330697635E-10" izz="1.3575909169764E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0449123067948969 0" />
<parent link="middle_metacarpals" />
<child link="middle_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="middle_middle">
<inertial>
<origin xyz="-0.000206991893084203 2.60624978909441E-06 0.0167065331356697" rpy="0 0 0" />
<mass value="0.00237827502274046" />
<inertia ixx="2.29538732170979E-07" ixy="2.23722006830829E-12" ixz="-1.47340061868536E-08" iyy="1.7676133625496E-07" iyz="1.15938574397121E-11" izz="9.62798057458164E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_middle.STL" />
</geometry>
</collision>
</link>
<joint name="middle_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.0102705367948965 0" />
<parent link="middle_proximal" />
<child link="middle_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="middle_distal">
<inertial>
<origin xyz="-0.00816246584707385 2.27058557035753E-05 0.00811806604834034" rpy="0 0 0" />
<mass value="0.00334170288968694" />
<inertia ixx="1.59570753478215E-07" ixy="1.51460233550071E-10" ixz="-5.94291827952622E-08" iyy="1.78095663702073E-07" iyz="-1.24843061161549E-10" izz="1.34141410190336E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<joint name="middle_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="middle_middle" />
<child link="middle_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.49304833" effort="100" velocity="1" />
<mimic joint="middle_pip" multiplier="0.89" offset="0" />
</joint>
<link name="ring_metacarpals">
<inertial>
<origin xyz="-0.00794299637971868 -2.94513098575422E-07 0.00220106138372037" rpy="0 0 0" />
<mass value="0.000895516612684195" />
<inertia ixx="3.46279813761428E-08" ixy="-3.92590701616938E-13" ixz="5.91029201975386E-09" iyy="2.854355219638E-08" iyz="-5.34642787239736E-13" izz="4.38102164225122E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_roll" type="revolute">
<origin xyz="0.00379999999632174 0.0117499999970695 0.1481" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="ring_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="ring_proximal">
<inertial>
<origin xyz="-0.00114185484356009 3.47315809493265E-05 0.0228928405060199" rpy="0 0 0" />
<mass value="0.00450921353946778" />
<inertia ixx="6.66673081759646E-07" ixy="-1.07184694208142E-11" ixz="-2.53143399406658E-08" iyy="6.74195868060947E-07" iyz="-1.59054446512071E-10" izz="1.3575909184966E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0449123067948969 0" />
<parent link="ring_metacarpals" />
<child link="ring_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="ring_middle">
<inertial>
<origin xyz="-0.00020699321531438 2.60746916563859E-06 0.0167065326651803" rpy="0 0 0" />
<mass value="0.00237827433282015" />
<inertia ixx="2.29538714144179E-07" ixy="2.25259718797087E-12" ixz="-1.47340121880382E-08" iyy="1.76761312824203E-07" iyz="1.15994674932479E-11" izz="9.62797743780677E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_middle.STL" />
</geometry>
</collision>
</link>
<joint name="ring_pip" type="revolute">
<origin xyz="-0.00419457745591796 0 0.0448194332262389" rpy="0 0.0102705367948965 0" />
<parent link="ring_proximal" />
<child link="ring_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="ring_distal">
<inertial>
<origin xyz="-0.0081624655153107 2.27057324615476E-05 0.00811806561376854" rpy="0 0 0" />
<mass value="0.00334170280386792" />
<inertia ixx="1.59570757957895E-07" ixy="1.51461030930522E-10" ixz="-5.94291750853635E-08" iyy="1.78095650648522E-07" iyz="-1.2484716714453E-10" izz="1.34141394018405E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<joint name="ring_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="ring_middle" />
<child link="ring_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.49304833" effort="100" velocity="1" />
<mimic joint="ring_pip" multiplier="0.89" offset="0" />
</joint>
<link name="pinky_metacarpals">
<inertial>
<origin xyz="-0.00794299617469867 -2.9300686296696E-07 0.00220106329602079" rpy="0 0 0" />
<mass value="0.000895516947513541" />
<inertia ixx="3.46279958591138E-08" ixy="-3.91785473098584E-13" ixz="5.91029262259818E-09" iyy="2.85435605328821E-08" iyz="-5.27457876763101E-13" izz="4.38102218637476E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_roll" type="revolute">
<origin xyz="0.00379999999998499 0.0352499999970695 0.1436" rpy="0 0 0" />
<parent link="hand_base_link" />
<child link="pinky_metacarpals" />
<axis xyz="1 0 0" />
<limit lower="-0.25155263" upper="0.25155263" effort="100" velocity="1" />
</joint>
<link name="pinky_proximal">
<inertial>
<origin xyz="-0.00114185484350746 3.47315809787474E-05 0.0228928405060759" rpy="0 0 0" />
<mass value="0.00450921353949741" />
<inertia ixx="6.66673081761478E-07" ixy="-1.07184692555283E-11" ixz="-2.53143399404374E-08" iyy="6.74195868062215E-07" iyz="-1.59054445649156E-10" izz="1.35759091850319E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_proximal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_mcp_pitch" type="revolute">
<origin xyz="-0.0021302 0 0" rpy="0 0.0449123067948969 0" />
<parent link="pinky_metacarpals" />
<child link="pinky_proximal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.22215668" effort="100" velocity="1" />
</joint>
<link name="pinky_middle">
<inertial>
<origin xyz="-0.000206993248553502 2.60748984004522E-06 0.0167065326828462" rpy="0 0 0" />
<mass value="0.00237827431921004" />
<inertia ixx="2.29538713829186E-07" ixy="2.25292379523651E-12" ixz="-1.47340119230773E-08" iyy="1.76761312133621E-07" iyz="1.15993142153177E-11" izz="9.62797736254209E-08" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_middle.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_pip" type="revolute">
<origin xyz="-0.0041946 0 0.044819" rpy="0 0.0102705367948965 0" />
<parent link="pinky_proximal" />
<child link="pinky_middle" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.7412882" effort="100" velocity="1" />
</joint>
<link name="pinky_distal">
<inertial>
<origin xyz="-0.00816246615274486 2.27054189396725E-05 0.00811806634481432" rpy="0 0 0" />
<mass value="0.00334170283969146" />
<inertia ixx="1.59570758932551E-07" ixy="1.51458416419677E-10" ixz="-5.94291815262966E-08" iyy="1.7809566305509E-07" iyz="-1.24846982879422E-10" izz="1.34141398854287E-07" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
<material name="">
<color rgba="1 1 1 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<joint name="pinky_dip" type="revolute">
<origin xyz="0.0028245 0 0.031696" rpy="0 0 0" />
<parent link="pinky_middle" />
<child link="pinky_distal" />
<axis xyz="0 1 0" />
<limit lower="0" upper="1.49304833" effort="100" velocity="1" />
<mimic joint="pinky_pip" multiplier="0.89" offset="0" />
</joint>
</robot>
@@ -0,0 +1,228 @@
<mujoco model="linker_hand_l20v101_left">
<compiler angle="radian" meshdir="meshes" balanceinertia="true"/>
<option timestep="0.002" gravity="0 0 -9.81" integrator="implicitfast" />
<default>
<joint damping="0.15" armature="0.01" />
<position kp="200" kv="8" forcelimited="true" forcerange="-8 8" />
<!-- visual: 仅显示;collision: 同 mesh 的凸包(MuJoCo 默认),比 AABB 更贴视觉外形 -->
<default class="visual">
<geom type="mesh" contype="0" conaffinity="0" group="2" rgba="1 1 1 1"/>
</default>
<default class="collision">
<geom contype="1" conaffinity="1" condim="3" group="3" rgba="0 1 0 0"
friction="1 0.5 0.01" solref="0.004 1" solimp="0.9 0.95 0.001" margin="0.0005"/>
</default>
</default>
<asset>
<mesh name="hand_base_link" file="hand_base_link.STL"/>
<mesh name="thumb_metacarpals_base1" file="thumb_metacarpals_base1.STL"/>
<mesh name="thumb_metacarpals_base2" file="thumb_metacarpals_base2.STL"/>
<mesh name="thumb_metacarpals" file="thumb_metacarpals.STL"/>
<mesh name="thumb_proximal" file="thumb_proximal.STL"/>
<mesh name="thumb_distal" file="thumb_distal.STL"/>
<mesh name="index_metacarpals" file="index_metacarpals.STL"/>
<mesh name="index_proximal" file="index_proximal.STL"/>
<mesh name="index_middle" file="index_middle.STL"/>
<mesh name="index_distal" file="index_distal.STL"/>
<mesh name="middle_metacarpals" file="middle_metacarpals.STL"/>
<mesh name="middle_proximal" file="middle_proximal.STL"/>
<mesh name="middle_middle" file="middle_middle.STL"/>
<mesh name="middle_distal" file="middle_distal.STL"/>
<mesh name="ring_metacarpals" file="ring_metacarpals.STL"/>
<mesh name="ring_proximal" file="ring_proximal.STL"/>
<mesh name="ring_middle" file="ring_middle.STL"/>
<mesh name="ring_distal" file="ring_distal.STL"/>
<mesh name="pinky_metacarpals" file="pinky_metacarpals.STL"/>
<mesh name="pinky_proximal" file="pinky_proximal.STL"/>
<mesh name="pinky_middle" file="pinky_middle.STL"/>
<mesh name="pinky_distal" file="pinky_distal.STL"/>
</asset>
<worldbody>
<body name="hand_base" pos="0 0 0">
<geom class="visual" mesh="hand_base_link"/>
<geom class="collision" type="mesh" mesh="hand_base_link"/>
</body>
<body name="thumb_metacarpals_base1" pos="0.01916 -0.01 0.0436">
<inertial pos="0.0115155 0.00398069 0.000272888" quat="0.982721 -0.180506 -0.00245546 0.0408729" mass="0.0160352" diaginertia="4.05464e-06 2.4729e-06 2.02979e-06"/>
<joint name="thumb_cmc_roll" pos="0 0 0" axis="-1 0 0" range="0 1.309" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="thumb_metacarpals_base1"/>
<body name="thumb_metacarpals_base2" pos="0.019845 0 0" quat="0.926488 0.376324 0 0">
<inertial pos="0.00286715 -0.00615727 0.00472605" quat="0.751727 0.570385 0.0649353 -0.32458" mass="0.00183265" diaginertia="1.63035e-07 1.63035e-07 1.63035e-07"/>
<joint name="thumb_cmc_yaw" pos="0 0 0" axis="0 0 1" range="0 1.5882" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="thumb_metacarpals_base2"/>
<body name="thumb_metacarpals" pos="0.0088218 -0.024245 0.0005" quat="0.515207 0.359991 -0.252058 0.735822">
<inertial pos="0.00670658 8.60759e-05 0.0331413" quat="0.705208 -0.00142811 -0.0031017 0.708992" mass="0.028716" diaginertia="1.35288e-05 1.13359e-05 3.37772e-06"/>
<joint name="thumb_cmc_pitch" pos="0 0 0" axis="0 1 0" range="0 0.7854" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="thumb_metacarpals"/>
<geom class="collision" type="mesh" mesh="thumb_metacarpals"/>
<body name="thumb_proximal" pos="0.0127018 0 0.0627256">
<inertial pos="-0.00268734 0.000568245 0.0118502" quat="0.705679 -0.0691221 -0.0785141 0.700767" mass="0.00549016" diaginertia="6.2328e-07 4.89863e-07 2.23372e-07"/>
<joint name="thumb_mcp" pos="0 0 0" axis="0 1 0" range="0 1.2217" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="thumb_proximal"/>
<geom class="collision" type="mesh" mesh="thumb_proximal"/>
<body name="thumb_distal" pos="-0.00483652 0 0.0342226">
<inertial pos="-0.000710022 -0.00267576 0.0124763" quat="0.561846 0.213938 0.139751 0.786785" mass="0.00382047" diaginertia="2.69897e-07 2.52035e-07 8.26387e-08"/>
<joint name="thumb_ip" pos="0 0 0" axis="0 1 0" range="0 1.2392" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="thumb_distal"/>
<geom class="collision" type="mesh" mesh="thumb_distal"/>
</body>
</body>
</body>
</body>
</body>
<body name="index_metacarpals" pos="0.0038 -0.03525 0.1481">
<inertial pos="-0.007943 -2.93674e-07 0.00220106" quat="0.599922 0.599922 -0.374291 0.374291" mass="0.000895516" diaginertia="4.67031e-08 3.17351e-08 2.85435e-08"/>
<joint name="index_mcp_roll" pos="0 0 0" axis="-1 0 0" range="-0.2269 0.2269" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="index_metacarpals"/>
<body name="index_proximal" pos="-0.0021302 0 0">
<inertial pos="-0.00114185 3.47316e-05 0.0228928" quat="0.706728 0.0167051 0.0169055 0.707086" mass="0.00450921" diaginertia="6.74196e-07 6.67877e-07 1.34555e-07"/>
<joint name="index_mcp_pitch" pos="0 0 0" axis="0 1 0" range="0 1.2217" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="index_proximal"/>
<geom class="collision" type="mesh" mesh="index_proximal"/>
<body name="index_middle" pos="-0.0041946 0 0.044819">
<inertial pos="-0.000206992 2.60617e-06 0.0167065" quat="0.99852 7.15582e-05 0.054381 -3.89717e-06" mass="0.00237827" diaginertia="2.31148e-07 1.76761e-07 9.46702e-08"/>
<joint name="index_pip" pos="0 0 0" axis="0 1 0" range="0 1.7279" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="index_middle"/>
<geom class="collision" type="mesh" mesh="index_middle"/>
<body name="index_distal" pos="0.0028245 0 0.031696">
<inertial pos="-0.00816247 2.27059e-05 0.00811807" quat="0.942747 0.00109864 0.333492 0.00313565" mass="0.0033417" diaginertia="2.07631e-07 1.78094e-07 8.6082e-08"/>
<joint name="index_dip" pos="0 0 0" axis="0 1 0" range="0 1.3614" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="index_distal"/>
<geom class="collision" type="mesh" mesh="index_distal"/>
</body>
</body>
</body>
</body>
<body name="middle_metacarpals" pos="0.0038 -0.01175 0.1526">
<inertial pos="-0.007943 -2.93007e-07 0.00220106" quat="0.599922 0.599922 -0.374291 0.374291" mass="0.000895517" diaginertia="4.67031e-08 3.17351e-08 2.85436e-08"/>
<joint name="middle_mcp_roll" pos="0 0 0" axis="-1 0 0" range="-0.23 0.23" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="middle_metacarpals"/>
<body name="middle_proximal" pos="-0.0021302 0 0">
<inertial pos="-0.00114185 3.47316e-05 0.0228928" quat="0.706728 0.0167051 0.0169055 0.707086" mass="0.00450921" diaginertia="6.74196e-07 6.67877e-07 1.34555e-07"/>
<joint name="middle_mcp_pitch" pos="0 0 0" axis="0 1 0" range="0 1.2217" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="middle_proximal"/>
<geom class="collision" type="mesh" mesh="middle_proximal"/>
<body name="middle_middle" pos="-0.00419458 0 0.0448194">
<inertial pos="-0.000206992 2.60625e-06 0.0167065" quat="0.99852 7.15718e-05 0.054381 -3.89791e-06" mass="0.00237828" diaginertia="2.31148e-07 1.76761e-07 9.46702e-08"/>
<joint name="middle_pip" pos="0 0 0" axis="0 1 0" range="0 1.7279" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="middle_middle"/>
<geom class="collision" type="mesh" mesh="middle_middle"/>
<body name="middle_distal" pos="0.0028245 0 0.031696">
<inertial pos="-0.00816247 2.27059e-05 0.00811807" quat="0.942747 0.00109865 0.333492 0.00313567" mass="0.0033417" diaginertia="2.07631e-07 1.78094e-07 8.6082e-08"/>
<joint name="middle_dip" pos="0 0 0" axis="0 1 0" range="0 1.3614" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="middle_distal"/>
<geom class="collision" type="mesh" mesh="middle_distal"/>
</body>
</body>
</body>
</body>
<body name="ring_metacarpals" pos="0.0038 0.01175 0.1481">
<inertial pos="-0.007943 -2.94513e-07 0.00220106" quat="0.599922 0.599922 -0.374291 0.374291" mass="0.000895517" diaginertia="4.67031e-08 3.17351e-08 2.85436e-08"/>
<joint name="ring_mcp_roll" pos="0 0 0" axis="-1 0 0" range="-0.23 0.23" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="ring_metacarpals"/>
<body name="ring_proximal" pos="-0.0021302 0 0">
<inertial pos="-0.00114185 3.47316e-05 0.0228928" quat="0.706728 0.0167051 0.0169055 0.707086" mass="0.00450921" diaginertia="6.74196e-07 6.67877e-07 1.34555e-07"/>
<joint name="ring_mcp_pitch" pos="0 0 0" axis="0 1 0" range="0 1.2217" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="ring_proximal"/>
<geom class="collision" type="mesh" mesh="ring_proximal"/>
<body name="ring_middle" pos="-0.00419458 0 0.0448194">
<inertial pos="-0.000206993 2.60747e-06 0.0167065" quat="0.99852 7.16159e-05 0.054381 -3.90032e-06" mass="0.00237827" diaginertia="2.31148e-07 1.76761e-07 9.46701e-08"/>
<joint name="ring_pip" pos="0 0 0" axis="0 1 0" range="0 1.7279" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="ring_middle"/>
<geom class="collision" type="mesh" mesh="ring_middle"/>
<body name="ring_distal" pos="0.0028245 0 0.031696">
<inertial pos="-0.00816247 2.27057e-05 0.00811807" quat="0.942747 0.00109865 0.333492 0.00313572" mass="0.0033417" diaginertia="2.07631e-07 1.78094e-07 8.6082e-08"/>
<joint name="ring_dip" pos="0 0 0" axis="0 1 0" range="0 1.3614" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="ring_distal"/>
<geom class="collision" type="mesh" mesh="ring_distal"/>
</body>
</body>
</body>
</body>
<body name="pinky_metacarpals" pos="0.0038 0.03525 0.1436">
<inertial pos="-0.007943 -2.93007e-07 0.00220106" quat="0.599922 0.599922 -0.374291 0.374291" mass="0.000895517" diaginertia="4.67031e-08 3.17351e-08 2.85436e-08"/>
<joint name="pinky_mcp_roll" pos="0 0 0" axis="-1 0 0" range="-0.23 0.23" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="pinky_metacarpals"/>
<body name="pinky_proximal" pos="-0.0021302 0 0">
<inertial pos="-0.00114185 3.47316e-05 0.0228928" quat="0.706728 0.0167051 0.0169055 0.707086" mass="0.00450921" diaginertia="6.74196e-07 6.67877e-07 1.34555e-07"/>
<joint name="pinky_mcp_pitch" pos="0 0 0" axis="0 1 0" range="0 1.2217" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="pinky_proximal"/>
<geom class="collision" type="mesh" mesh="pinky_proximal"/>
<body name="pinky_middle" pos="-0.0041946 0 0.044819">
<inertial pos="-0.000206993 2.60749e-06 0.0167065" quat="0.99852 7.16152e-05 0.054381 -3.90028e-06" mass="0.00237827" diaginertia="2.31148e-07 1.76761e-07 9.46701e-08"/>
<joint name="pinky_pip" pos="0 0 0" axis="0 1 0" range="0 1.7279" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="pinky_middle"/>
<geom class="collision" type="mesh" mesh="pinky_middle"/>
<body name="pinky_distal" pos="0.0028245 0 0.031696">
<inertial pos="-0.00816247 2.27054e-05 0.00811807" quat="0.942747 0.00109863 0.333492 0.00313569" mass="0.0033417" diaginertia="2.07631e-07 1.78094e-07 8.6082e-08"/>
<joint name="pinky_dip" pos="0 0 0" axis="0 1 0" range="0 1.3614" actuatorfrcrange="-100 100"/>
<geom class="visual" mesh="pinky_distal"/>
<geom class="collision" type="mesh" mesh="pinky_distal"/>
</body>
</body>
</body>
</body>
</worldbody>
<contact>
<!-- 掌心与各指根/近端:mesh 本就嵌套,排除假碰撞 -->
<exclude body1="hand_base" body2="thumb_metacarpals_base1"/>
<exclude body1="hand_base" body2="thumb_metacarpals_base2"/>
<exclude body1="hand_base" body2="thumb_metacarpals"/>
<exclude body1="hand_base" body2="index_metacarpals"/>
<exclude body1="hand_base" body2="index_proximal"/>
<exclude body1="hand_base" body2="middle_metacarpals"/>
<exclude body1="hand_base" body2="middle_proximal"/>
<exclude body1="hand_base" body2="ring_metacarpals"/>
<exclude body1="hand_base" body2="ring_proximal"/>
<exclude body1="hand_base" body2="pinky_metacarpals"/>
<exclude body1="hand_base" body2="pinky_proximal"/>
<!-- 拇指链相邻 / 隔代假重叠 -->
<exclude body1="thumb_metacarpals_base1" body2="thumb_metacarpals_base2"/>
<exclude body1="thumb_metacarpals_base1" body2="thumb_metacarpals"/>
<exclude body1="thumb_metacarpals_base2" body2="thumb_metacarpals"/>
<exclude body1="thumb_metacarpals" body2="thumb_proximal"/>
<exclude body1="thumb_proximal" body2="thumb_distal"/>
<!-- 食指链 -->
<exclude body1="index_metacarpals" body2="index_proximal"/>
<exclude body1="index_proximal" body2="index_middle"/>
<exclude body1="index_middle" body2="index_distal"/>
<!-- 中指链 -->
<exclude body1="middle_metacarpals" body2="middle_proximal"/>
<exclude body1="middle_proximal" body2="middle_middle"/>
<exclude body1="middle_middle" body2="middle_distal"/>
<!-- 无名指链 -->
<exclude body1="ring_metacarpals" body2="ring_proximal"/>
<exclude body1="ring_proximal" body2="ring_middle"/>
<exclude body1="ring_middle" body2="ring_distal"/>
<!-- 小指链 -->
<exclude body1="pinky_metacarpals" body2="pinky_proximal"/>
<exclude body1="pinky_proximal" body2="pinky_middle"/>
<exclude body1="pinky_middle" body2="pinky_distal"/>
</contact>
<actuator>
<position name="thumb_cmc_roll_pos" joint="thumb_cmc_roll" kp="200" kv="8" ctrlrange="0 1.309" forcelimited="true" forcerange="-8 8" />
<position name="thumb_cmc_yaw_pos" joint="thumb_cmc_yaw" kp="200" kv="8" ctrlrange="0 1.5882" forcelimited="true" forcerange="-8 8" />
<position name="thumb_cmc_pitch_pos" joint="thumb_cmc_pitch" kp="200" kv="8" ctrlrange="0 0.7854" forcelimited="true" forcerange="-8 8" />
<position name="thumb_mcp_pos" joint="thumb_mcp" kp="200" kv="8" ctrlrange="0 1.2217" forcelimited="true" forcerange="-8 8" />
<position name="thumb_ip_pos" joint="thumb_ip" kp="200" kv="8" ctrlrange="0 1.2392" forcelimited="true" forcerange="-8 8" />
<position name="index_mcp_roll_pos" joint="index_mcp_roll" kp="200" kv="8" ctrlrange="-0.2269 0.2269" forcelimited="true" forcerange="-8 8" />
<position name="index_mcp_pitch_pos" joint="index_mcp_pitch" kp="200" kv="8" ctrlrange="0 1.2217" forcelimited="true" forcerange="-8 8" />
<position name="index_pip_pos" joint="index_pip" kp="200" kv="8" ctrlrange="0 1.7279" forcelimited="true" forcerange="-8 8" />
<position name="index_dip_pos" joint="index_dip" kp="200" kv="8" ctrlrange="0 1.3614" forcelimited="true" forcerange="-8 8" />
<position name="middle_mcp_roll_pos" joint="middle_mcp_roll" kp="200" kv="8" ctrlrange="-0.23 0.23" forcelimited="true" forcerange="-8 8" />
<position name="middle_mcp_pitch_pos" joint="middle_mcp_pitch" kp="200" kv="8" ctrlrange="0 1.2217" forcelimited="true" forcerange="-8 8" />
<position name="middle_pip_pos" joint="middle_pip" kp="200" kv="8" ctrlrange="0 1.7279" forcelimited="true" forcerange="-8 8" />
<position name="middle_dip_pos" joint="middle_dip" kp="200" kv="8" ctrlrange="0 1.3614" forcelimited="true" forcerange="-8 8" />
<position name="ring_mcp_roll_pos" joint="ring_mcp_roll" kp="200" kv="8" ctrlrange="-0.23 0.23" forcelimited="true" forcerange="-8 8" />
<position name="ring_mcp_pitch_pos" joint="ring_mcp_pitch" kp="200" kv="8" ctrlrange="0 1.2217" forcelimited="true" forcerange="-8 8" />
<position name="ring_pip_pos" joint="ring_pip" kp="200" kv="8" ctrlrange="0 1.7279" forcelimited="true" forcerange="-8 8" />
<position name="ring_dip_pos" joint="ring_dip" kp="200" kv="8" ctrlrange="0 1.3614" forcelimited="true" forcerange="-8 8" />
<position name="pinky_mcp_roll_pos" joint="pinky_mcp_roll" kp="200" kv="8" ctrlrange="-0.23 0.23" forcelimited="true" forcerange="-8 8" />
<position name="pinky_mcp_pitch_pos" joint="pinky_mcp_pitch" kp="200" kv="8" ctrlrange="0 1.2217" forcelimited="true" forcerange="-8 8" />
<position name="pinky_pip_pos" joint="pinky_pip" kp="200" kv="8" ctrlrange="0 1.7279" forcelimited="true" forcerange="-8 8" />
<position name="pinky_dip_pos" joint="pinky_dip" kp="200" kv="8" ctrlrange="0 1.3614" forcelimited="true" forcerange="-8 8" />
</actuator>
</mujoco>
@@ -3,6 +3,10 @@
from __future__ import annotations
import json
from pathlib import Path
from typing import NamedTuple
# SDK 索引 → URDF 关节名(不含前缀)
L20_SDK_TO_URDF: dict[int, str] = {
0: "thumb_cmc_pitch",
@@ -24,8 +28,78 @@ L20_SDK_TO_URDF: dict[int, str] = {
}
DIP_MIMIC_MULTIPLIER = 0.89
# URDF 默认线性比;实际被动跟随以标定 JSON 分段为准
THUMB_IP_MIMIC_MULTIPLIER = 1.02
_G20_CALIB_JSON = (
Path(__file__).resolve().parent.parent
/ "urdf/G20/linker_hand_g20_left/g20_left_G20_LEFT_001_calibration.json"
)
class PiecewiseMap(NamedTuple):
"""单调分段线性映射:master_pts[i] <-> mimic_pts[i]。"""
master: str
master_pts: tuple[float, ...]
mimic_pts: tuple[float, ...]
def _load_g20_calibration(path: Path = _G20_CALIB_JSON) -> dict:
with path.open("r", encoding="utf-8") as f:
data = json.load(f)
if int(data.get("schema_version", 0)) < 3:
raise ValueError(f"unsupported calibration schema in {path}")
joints = data.get("joints") or {}
tables: dict[str, tuple[float, ...]] = {}
for name, spec in joints.items():
arr = spec.get("angle_rad")
if not isinstance(arr, list) or len(arr) != 256:
raise ValueError(f"{name}: angle_rad must be length 256")
tables[name] = tuple(float(x) for x in arr)
baseline = [float(x) for x in data.get("baseline_command_u8", [255] * 20)]
while len(baseline) < 20:
baseline.append(255.0)
return {"tables": tables, "baseline": baseline[:20], "path": str(path)}
_G20_CALIB = _load_g20_calibration()
G20_CMD_ANGLE_TABLES: dict[str, tuple[float, ...]] = _G20_CALIB["tables"]
G20_OPEN_CMD: list[float] = list(_G20_CALIB["baseline"])
def _downsample_passive_map(
master_name: str, mimic_name: str, n_pts: int = 33
) -> PiecewiseMap:
"""同一命令下的 master/mimic 曲线 → 升序分段表。
thumb_ip JSON 是累计指尖投影角需减 thumb_mcp 得到相对 URDF 关节角
"""
master = G20_CMD_ANGLE_TABLES[master_name]
mimic = G20_CMD_ANGLE_TABLES[mimic_name]
pairs: list[tuple[float, float]] = []
last_m: float | None = None
for cmd in range(255, -1, -1):
m = float(master[cmd])
y = float(mimic[cmd])
if mimic_name == "thumb_ip":
y = max(0.0, y - m)
if last_m is None or m > last_m + 1e-9:
pairs.append((m, y))
last_m = m
if len(pairs) < 2:
raise ValueError(f"cannot build passive map {master_name}->{mimic_name}")
if n_pts < 2:
n_pts = 2
idxs = sorted({round(i * (len(pairs) - 1) / (n_pts - 1)) for i in range(n_pts)})
sel = [pairs[i] for i in idxs]
return PiecewiseMap(
master=master_name,
master_pts=tuple(p[0] for p in sel),
mimic_pts=tuple(p[1] for p in sel),
)
G20_ACTUATED_JOINT_NAMES: tuple[str, ...] = tuple(
sorted({name for name in L20_SDK_TO_URDF.values()})
)
@@ -48,16 +122,38 @@ G20_MIMIC_OF: dict[str, tuple[str, float]] = {
"pinky_dip": ("pinky_pip", DIP_MIMIC_MULTIPLIER),
}
# 左手 G20 限位(与 Isaac / URDF 一致);SDK 索引语义同 L20
G20_L_MIN = [0, 0, 0, 0, 0, 0.0, -0.23, -0.23, -0.23, -0.23, 0.0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# 通道5 thumb_cmc_roll:试调 max=1.47(原 URDF/Isaac 为 1.4
G20_L_MAX = [0.84, 1.22, 1.22, 1.22, 1.22, 1.47, 0.23, 0.23, 0.23, 0.23, 1.57, 0, 0, 0, 0, 1.29, 1.55, 1.55, 1.55, 1.55]
# 四指侧摆 (6..9)SDK0→下限、255→上限
G20_L_DIR = [-1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1]
# 被动关节:标定 JSON 中同命令下的 master/mimic 配对曲线
G20_PASSIVE_MAPS: dict[str, PiecewiseMap] = {
"thumb_ip": _downsample_passive_map("thumb_mcp", "thumb_ip"),
"index_dip": _downsample_passive_map("index_pip", "index_dip"),
"middle_dip": _downsample_passive_map("middle_pip", "middle_dip"),
"ring_dip": _downsample_passive_map("ring_pip", "ring_dip"),
"pinky_dip": _downsample_passive_map("pinky_pip", "pinky_dip"),
}
G20_OPEN_CMD = [
255, 255, 255, 255, 255, 255, 193, 148, 105, 42, 245, 255, 255, 255, 255, 255, 255, 255, 255, 255
]
# 四指侧摆相对标定表视觉反向时取负;拇指 cmc_roll 不翻
G20_FLIP_JOINT_SIGN: frozenset[str] = frozenset(
{
"index_mcp_roll",
"middle_mcp_roll",
"ring_mcp_roll",
"pinky_mcp_roll",
}
)
# 由标定表极值导出(供日志/兼容);实际 0~255 走查表
G20_L_MIN = [0.0] * 20
G20_L_MAX = [0.0] * 20
for _sdk_idx, _jname in L20_SDK_TO_URDF.items():
_arr = G20_CMD_ANGLE_TABLES[_jname]
_lo = float(min(_arr))
_hi = float(max(_arr))
if _jname in G20_FLIP_JOINT_SIGN:
_lo, _hi = -_hi, -_lo
G20_L_MIN[_sdk_idx] = _lo
G20_L_MAX[_sdk_idx] = _hi
# 侧摆方向提示:标定表 cmd0=+max、cmd255=-max(不再用线性 DIR
G20_L_DIR = [-1, -1, -1, -1, -1, -1, 1, 1, 1, 1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1]
def g20_required_joint_names() -> frozenset[str]:
@@ -68,14 +164,45 @@ def g20_actuated_joint_names() -> frozenset[str]:
return frozenset(G20_ACTUATED_JOINT_NAMES)
def _interp_piecewise(x: float, xs: tuple[float, ...], ys: tuple[float, ...]) -> float:
if len(xs) != len(ys):
raise ValueError(f"piecewise size mismatch: len(xs)={len(xs)} len(ys)={len(ys)}")
if len(xs) < 2:
raise ValueError("piecewise map must contain at least 2 points")
for i in range(1, len(xs)):
if xs[i] <= xs[i - 1]:
raise ValueError(f"xs must be strictly increasing: {xs}")
x = float(x)
if x <= xs[0]:
return float(ys[0])
if x >= xs[-1]:
return float(ys[-1])
for i in range(1, len(xs)):
if x <= xs[i]:
return scale_value(x, xs[i - 1], xs[i], ys[i - 1], ys[i])
return float(ys[-1])
def passive_master_to_mimic(mimic_name: str, master_value: float) -> float:
spec = G20_PASSIVE_MAPS[mimic_name]
return _interp_piecewise(float(master_value), spec.master_pts, spec.mimic_pts)
def passive_mimic_to_master(mimic_name: str, mimic_value: float) -> float:
spec = G20_PASSIVE_MAPS[mimic_name]
return _interp_piecewise(float(mimic_value), spec.mimic_pts, spec.master_pts)
def complete_g20_mimic_positions(positions: dict[str, float]) -> dict[str, float]:
"""补全缺失的 mimic 关节;已给出的 mimic 值优先保留。"""
out = dict(positions)
for mimic, (master, ratio) in G20_MIMIC_OF.items():
for mimic, spec in G20_PASSIVE_MAPS.items():
if mimic in out:
continue
master = spec.master
if master in out:
out[mimic] = float(out[master]) * float(ratio)
out[mimic] = passive_master_to_mimic(mimic, float(out[master]))
return out
@@ -89,42 +216,54 @@ def clamp(value: float, low: float, high: float) -> float:
return max(low, min(high, value))
def g20_cmd_to_joint_angle(joint_name: str, command: float) -> float:
"""查标定表:命令 0~255 → 关节弧度(支持小数命令线性插值)。"""
table = G20_CMD_ANGLE_TABLES[joint_name]
cmd = clamp(float(command), 0.0, 255.0)
lo = int(cmd)
if lo >= 255:
angle = float(table[255])
else:
hi = lo + 1
frac = cmd - lo
angle = float(table[lo]) * (1.0 - frac) + float(table[hi]) * frac
if joint_name in G20_FLIP_JOINT_SIGN:
angle = -angle
return angle
def range_to_arc_g20_left(values: list[float]) -> list[float]:
"""SDK 0~255 → 20 维弧度(预留通道 11~14 保持 0)。"""
"""SDK 0~255 → 20 维弧度(预留通道 11~14 保持 0);按标定表查"""
arcs = [0.0] * 20
for i in range(20):
if 11 <= i <= 14:
continue
val = clamp(values[i] if i < len(values) else 0.0, 0.0, 255.0)
if G20_L_DIR[i] == -1:
arcs[i] = scale_value(val, 0.0, 255.0, G20_L_MAX[i], G20_L_MIN[i])
else:
arcs[i] = scale_value(val, 0.0, 255.0, G20_L_MIN[i], G20_L_MAX[i])
for sdk_idx, joint_name in L20_SDK_TO_URDF.items():
cmd = values[sdk_idx] if sdk_idx < len(values) else G20_OPEN_CMD[sdk_idx]
arcs[sdk_idx] = g20_cmd_to_joint_angle(joint_name, cmd)
return arcs
def sdk_range_to_urdf_positions(values: list[float]) -> dict[str, float]:
"""20 维 SDK → URDF 主动关节弧度(与 Isaac joint_mapping 一致)。
SDK 15 控制指尖弧度空间写入 thumb_mcp = arc/1.02
SDK 16..19 控制 DIP 电机弧度写入 *_pip = arc/0.89
"""
arcs = range_to_arc_g20_left(values)
"""20 维 SDK → URDF 主动关节弧度(标定表直接对应主动关节,不做 tip/DIP 反算)。"""
out: dict[str, float] = {}
for sdk_idx, short_name in L20_SDK_TO_URDF.items():
arc = float(arcs[sdk_idx])
if sdk_idx == 15:
out["thumb_mcp"] = arc / THUMB_IP_MIMIC_MULTIPLIER
elif sdk_idx in (16, 17, 18, 19):
out[short_name] = arc / DIP_MIMIC_MULTIPLIER
else:
out[short_name] = arc
cmd = values[sdk_idx] if sdk_idx < len(values) else G20_OPEN_CMD[sdk_idx]
out[short_name] = g20_cmd_to_joint_angle(short_name, cmd)
return out
def sdk_range_to_full_urdf_positions(values: list[float]) -> dict[str, float]:
"""主动 + mimic,供运动学写入(对齐 Isaac sdk_range_to_full_urdf_positions"""
return complete_g20_mimic_positions(sdk_range_to_urdf_positions(values))
"""主动查标定表;被动:thumb_ip 用相对分段,*_dip 用同命令相对表"""
out = sdk_range_to_urdf_positions(values)
# thumb_ip JSON 是累计 tip,勿直接写入;用 master→相对 mimic
out["thumb_ip"] = passive_master_to_mimic("thumb_ip", float(out["thumb_mcp"]))
for sdk_idx, dip_name in (
(16, "index_dip"),
(17, "middle_dip"),
(18, "ring_dip"),
(19, "pinky_dip"),
):
cmd = values[sdk_idx] if len(values) > sdk_idx else G20_OPEN_CMD[sdk_idx]
out[dip_name] = g20_cmd_to_joint_angle(dip_name, cmd)
return out
def open_hand_positions() -> dict[str, float]:
@@ -132,8 +271,116 @@ def open_hand_positions() -> dict[str, float]:
def half_fist_positions() -> dict[str, float]:
"""半握:主动弯曲通道约中间值,侧摆保持张开"""
"""半握:主动弯曲通道约中间值,侧摆保持 baseline"""
cmd = list(G20_OPEN_CMD)
for i in (0, 1, 2, 3, 4, 15, 16, 17, 18, 19):
cmd[i] = 80.0
return sdk_range_to_full_urdf_positions(cmd)
# ---------------------------------------------------------------------------
# L20 V10.1:严格按 URDF 限位 + 线性 mimic,不做 G20 标定/分段/指尖反算修正
# ---------------------------------------------------------------------------
L20_URDF_THUMB_IP_MIMIC = 1.0142 # URDF: thumb_dip <- thumb_mcp
L20_URDF_DIP_MIMIC = 0.7879 # URDF: *_dip <- *_pip
L20_URDF_MIMIC_OF: dict[str, tuple[str, float]] = {
"thumb_ip": ("thumb_mcp", L20_URDF_THUMB_IP_MIMIC),
"index_dip": ("index_pip", L20_URDF_DIP_MIMIC),
"middle_dip": ("middle_pip", L20_URDF_DIP_MIMIC),
"ring_dip": ("ring_pip", L20_URDF_DIP_MIMIC),
"pinky_dip": ("pinky_pip", L20_URDF_DIP_MIMIC),
}
# SDK 通道限位直接取自 L20 URDF 主动关节(15 直接对应 thumb_mcp,不是 tip 反算)
L20_URDF_L_MIN = [
0.0, 0.0, 0.0, 0.0, 0.0,
0.0, -0.2269, -0.23, -0.23, -0.23,
0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0,
]
L20_URDF_L_MAX = [
0.7854, 1.2217, 1.2217, 1.2217, 1.2217,
1.309, 0.2269, 0.23, 0.23, 0.23,
1.5882, 0.0, 0.0, 0.0, 0.0,
1.2217, 1.7279, 1.7279, 1.7279, 1.7279,
]
# 弯曲:0→MAX、255→MIN。四指侧摆 6..9L20 URDF 轴为 -XG20 为 +X),
# 故与 G20 相反,取 DIR=-1,避免相对真机/G20 视觉反向。
L20_URDF_L_DIR = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1]
def complete_l20_urdf_mimic_positions(positions: dict[str, float]) -> dict[str, float]:
"""按 L20 URDF 线性 mimic 补全;已给出的 mimic 值优先保留。"""
out = dict(positions)
for mimic, (master, ratio) in L20_URDF_MIMIC_OF.items():
if mimic in out:
continue
if master in out:
out[mimic] = float(out[master]) * float(ratio)
return out
def range_to_arc_l20_urdf(values: list[float]) -> list[float]:
arcs = [0.0] * 20
for i in range(20):
if 11 <= i <= 14:
continue
val = clamp(values[i] if i < len(values) else 0.0, 0.0, 255.0)
if L20_URDF_L_DIR[i] == -1:
arcs[i] = scale_value(val, 0.0, 255.0, L20_URDF_L_MAX[i], L20_URDF_L_MIN[i])
else:
arcs[i] = scale_value(val, 0.0, 255.0, L20_URDF_L_MIN[i], L20_URDF_L_MAX[i])
return arcs
def sdk_range_to_l20_urdf_positions(values: list[float]) -> dict[str, float]:
"""0~255 → L20 主动关节:直接按 URDF 限位插值,不做 tip/DIP 反算修正。"""
arcs = range_to_arc_l20_urdf(values)
out: dict[str, float] = {}
for sdk_idx, short_name in L20_SDK_TO_URDF.items():
out[short_name] = float(arcs[sdk_idx])
return out
def sdk_range_to_full_l20_urdf_positions(values: list[float]) -> dict[str, float]:
return complete_l20_urdf_mimic_positions(sdk_range_to_l20_urdf_positions(values))
def open_hand_positions_l20_urdf() -> dict[str, float]:
# 不用 G20_OPEN_CMD:其侧摆偏置在 L20 mesh 上会无名指/小指自碰,
# 把 collision_project 卡死导致整手不动。
# 弯曲全开=255;侧摆 6..9 取中值→约 0 rad。
cmd = [255.0] * 20
for i in (6, 7, 8, 9):
cmd[i] = 127.5
return sdk_range_to_full_l20_urdf_positions(cmd)
def half_fist_positions_l20_urdf() -> dict[str, float]:
cmd = [255.0] * 20
for i in (6, 7, 8, 9):
cmd[i] = 127.5
for i in (0, 1, 2, 3, 4, 15, 16, 17, 18, 19):
cmd[i] = 80.0
return sdk_range_to_full_l20_urdf_positions(cmd)
def get_mapping_fns(profile: str):
"""返回 (sdk_to_full, complete_mimic, open_hand, half_fist)。"""
p = str(profile).strip().lower()
if p in ("l20_urdf", "l20", "urdf"):
return (
sdk_range_to_full_l20_urdf_positions,
complete_l20_urdf_mimic_positions,
open_hand_positions_l20_urdf,
half_fist_positions_l20_urdf,
)
if p in ("g20", "g20_calibrated", "calibrated"):
return (
sdk_range_to_full_urdf_positions,
complete_g20_mimic_positions,
open_hand_positions,
half_fist_positions,
)
raise ValueError(f"unknown mapping profile: {profile}")
@@ -35,6 +35,7 @@ setup(
'linker_hand_mujoco_ros2_node=linker_hand_mujoco_ros2.linker_hand_mujoco_ros2:main',
'hand_curve_recorder=linker_hand_mujoco_ros2.hand_curve_recorder:main',
'g20_plan_bridge=linker_hand_mujoco_ros2.g20_plan_bridge:main',
'g20_joint_monitor_gui=linker_hand_mujoco_ros2.g20_joint_monitor_gui:main',
],
},
)