Organize hand models and launchers; use linear active joint mapping

This commit is contained in:
2026-09-28 11:05:30 +08:00
parent 325dff6870
commit 01c9aa5c7a
712 changed files with 205077 additions and 6209 deletions
+10
View File
@@ -0,0 +1,10 @@
# G20
[统一入口与映射说明](../README.md)
- `compare_left.sh`
- `compare_right.sh`
- `run_left.sh`
- `run_right.sh`
`assets/` 是包内模型链接;`scripts/` 为内部实现及辅助工具。
+1
View File
@@ -0,0 +1 @@
../../src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/G20
+1
View File
@@ -0,0 +1 @@
../../../src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/G20/linker_hand_g20_left/g20_left_passive.json
+1
View File
@@ -0,0 +1 @@
../../../src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/G20/linker_hand_g20_right/g20_right_passive.json
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_g20_left_dual_compare.sh" "$@"
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_g20_right_dual_compare.sh" "$@"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_g20_mujoco_plan.sh" "$@"
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_g20_right_mujoco_plan.sh" "$@"
+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 "$@"
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env bash
# MuJoCo G20 左手 — 桌面 URDF 验证
# URDF: /home/lxqs/桌面/L20/LEFT/linkerhand_g20_left.urdf(已同步到包内)
# 默认 mapping=g20_urdf(四指侧摆无外部取负,跟 URDF 轴正向)
#
# 用法:
# ./hands/G20/scripts/run_g20_left_desktop_urdf.sh
# ./hands/G20/scripts/run_g20_left_desktop_urdf.sh --pd-drive
# 另开终端扫侧摆方向:
# ./hands/G20/scripts/run_g20_left_mcp_roll_check.sh
# 监视关节弧度:
# ./hands/G20/scripts/run_g20_joint_gui.sh
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}"
XML="$PKG_SRC/linker_hand_mujoco_ros2/urdf/G20/linker_hand_g20_left/linker_hand_g20_left_desktop.xml"
URDF="$PKG_SRC/linker_hand_mujoco_ros2/urdf/G20/linker_hand_g20_left/linkerhand_g20_left_desktop.urdf"
if [[ ! -f "$XML" ]]; then
echo "[error] missing MJCF: $XML" >&2
exit 1
fi
echo "[g20-left-desktop] urdf=$URDF"
echo "[g20-left-desktop] xml=$XML"
echo "[g20-left-desktop] cmd-topic=/cb_left_hand_control_cmd"
echo "[g20-left-desktop] sim-state=/sim/mujoco/g20/left/joint_state"
echo "[g20-left-desktop] mapping=g20_urdf (侧摆 cmd0→负限位, cmd255→正限位, 无外部取负)"
echo "[g20-left-desktop] URDF 四指 mcp_roll 轴: +X (未翻转;右手已改为 -X)"
echo "[g20-left-desktop] 侧摆验证: 另开终端运行 ./hands/G20/scripts/run_g20_left_mcp_roll_check.sh"
exec python3 -m linker_hand_mujoco_ros2.g20_plan_bridge \
--xml "$XML" \
--node-name g20_mujoco_plan_bridge_left_desktop \
--window-title "G20 left (desktop URDF)" \
--cmd-topic /cb_left_hand_control_cmd \
--sim-state-topic /sim/mujoco/g20/left/joint_state \
--nominal-topic /retarget/g20/left/joint_target_nominal \
--mapping g20_urdf \
"$@"
+404
View File
@@ -0,0 +1,404 @@
#!/usr/bin/env bash
# 同时开两个 MuJoCo:G20 左手标定 vs L20 原版 URDF(未标定)
#
# 标定: /home/lxqs/图片/linkerhand_g20_left_tag.urdf
# → 同步包内 linker_hand_g20_left.xml(限位/侧摆轴/body 零位);mapping=g20_calibrated
# (可用 G20_LEFT_URDF=... 覆盖)
# 未标定: L20 V10.1 左手 URDF → 运行时按左手模板刷新 linker_hand_l20v101_left.xml
# mapping=l20_urdf
#
# 主动关节统一按当前模型限位线性换算;g20_calibrated 仅保留可选被动 JSON 联动。
#
# 默认运动学硬写。需要 PD+自碰时手动加 --pd-drive(两窗均加)。
# 两个窗口共用 /cb_left_hand_control_cmd(20 维 0~255)。
#
# 用法:
# ./hands/G20/scripts/run_g20_left_dual_compare.sh
# ./hands/G20/scripts/run_g20_left_dual_compare.sh --cmd-topic /cb_left_hand_control_cmd --speed 1.0
# ./hands/G20/scripts/run_g20_left_dual_compare.sh --pd-drive
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_DIR="$PKG_SRC/urdf/G20/linker_hand_g20_left"
URDF_CALIBRATED="${G20_LEFT_URDF:-$ROOT/hands/G20/reference/left_calibrated.urdf}"
MESHDIR_CALIB="$G20_DIR/meshes"
URDF_UNCALIBRATED="$PKG_SRC/urdf/L20_V10.1/linker_hand_l20v101_left/linkerhand_L20v10.1_left.urdf"
MESHDIR_UNCAL="$PKG_SRC/urdf/L20_V10.1/linker_hand_l20v101_left/meshes"
CALIB_XML="$G20_DIR/linker_hand_g20_left.xml"
CALIB_URDF_PKG="$G20_DIR/linkerhand_g20_left.urdf"
L20_TEMPLATE="$PKG_SRC/urdf/L20_V10.1/linker_hand_l20v101_left/linker_hand_l20v101_left.xml"
STOCK_XML="$PKG_SRC/urdf/L20_V10.1/linker_hand_l20v101_left/linker_hand_l20v101_left.xml"
if [[ ! -f "$URDF_CALIBRATED" ]]; then
# 回退到包内副本
URDF_CALIBRATED="$CALIB_URDF_PKG"
fi
if [[ ! -f "$URDF_CALIBRATED" ]]; then
echo "[error] missing calibrated URDF: $URDF_CALIBRATED" >&2
exit 1
fi
if [[ ! -f "$CALIB_XML" ]]; then
echo "[error] missing calibrated MJCF template: $CALIB_XML" >&2
exit 1
fi
if [[ ! -f "$URDF_UNCALIBRATED" ]]; then
echo "[error] missing uncalibrated URDF: $URDF_UNCALIBRATED" >&2
exit 1
fi
if [[ ! -d "$MESHDIR_UNCAL" ]]; then
echo "[error] missing uncalibrated meshes: $MESHDIR_UNCAL" >&2
exit 1
fi
if [[ ! -f "$L20_TEMPLATE" ]]; then
echo "[error] missing L20 V10.1 left MJCF template: $L20_TEMPLATE" >&2
exit 1
fi
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
export PYTHONPATH="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2${PYTHONPATH:+:$PYTHONPATH}"
echo "[g20-left-dual] sync calibrated MJCF from G20 left URDF -> $CALIB_XML"
python3 - "$URDF_CALIBRATED" "$MESHDIR_CALIB" "$CALIB_XML" "$CALIB_URDF_PKG" <<'PY'
import re
import shutil
import sys
from pathlib import Path
import mujoco
import numpy as np
urdf_cal = Path(sys.argv[1])
meshdir_cal = Path(sys.argv[2])
calib_xml = Path(sys.argv[3])
calib_urdf_pkg = Path(sys.argv[4])
# 包内主副本 + tag 文件名副本
shutil.copy2(urdf_cal, calib_urdf_pkg)
shutil.copy2(urdf_cal, calib_urdf_pkg.parent / "linkerhand_g20_left_tag.urdf")
MCP_ROLL_JOINTS = (
"index_mcp_roll",
"middle_mcp_roll",
"ring_mcp_roll",
"pinky_mcp_roll",
)
BODY_FROM_JOINT = {
"thumb_cmc_roll": "thumb_metacarpals_base1",
"thumb_cmc_yaw": "thumb_metacarpals_base2",
"thumb_cmc_pitch": "thumb_metacarpals",
"thumb_mcp": "thumb_proximal",
"thumb_ip": "thumb_distal",
"index_mcp_roll": "index_metacarpals",
"index_mcp_pitch": "index_proximal",
"index_pip": "index_middle",
"index_dip": "index_distal",
"middle_mcp_roll": "middle_metacarpals",
"middle_mcp_pitch": "middle_proximal",
"middle_pip": "middle_middle",
"middle_dip": "middle_distal",
"ring_mcp_roll": "ring_metacarpals",
"ring_mcp_pitch": "ring_proximal",
"ring_pip": "ring_middle",
"ring_dip": "ring_distal",
"pinky_mcp_roll": "pinky_metacarpals",
"pinky_mcp_pitch": "pinky_proximal",
"pinky_pip": "pinky_middle",
"pinky_dip": "pinky_distal",
}
def rpy_to_quat(roll, pitch, yaw):
cr, sr = np.cos(roll / 2), np.sin(roll / 2)
cp, sp = np.cos(pitch / 2), np.sin(pitch / 2)
cy, sy = np.cos(yaw / 2), np.sin(yaw / 2)
return np.array(
[
cr * cp * cy + sr * sp * sy,
sr * cp * cy - cr * sp * sy,
cr * sp * cy + sr * cp * sy,
cr * cp * sy - sr * sp * cy,
]
)
def parse_urdf(path: Path) -> dict[str, dict]:
text = path.read_text()
joints: dict[str, dict] = {}
for m in re.finditer(r"<joint\b([^>]*)>(.*?)</joint>", text, re.S):
attrs, body = m.group(1), m.group(2)
name_m = re.search(r'name="([^"]+)"', attrs)
if not name_m:
continue
name = name_m.group(1)
if "revolute" not in attrs:
continue
origin = re.search(r'<origin[^>]*xyz="([^"]+)"[^>]*rpy="([^"]+)"', body)
lim = re.search(r'lower="([^"]+)"[^>]*upper="([^"]+)"', body)
# URDF 常写成 <axis\n xyz="..."/>,不能写死 "axis xyz="
axis = re.search(r'<axis[^>]*xyz="([^"]+)"', body)
xyz = [float(x) for x in origin.group(1).split()] if origin else [0.0, 0.0, 0.0]
rpy = [float(x) for x in origin.group(2).split()] if origin else [0.0, 0.0, 0.0]
joints[name] = {
"lo": float(lim.group(1)),
"hi": float(lim.group(2)),
"xyz": xyz,
"rpy": rpy,
"axis": axis.group(1) if axis else "1 0 0",
}
return joints
def apply_urdf_to_mjcf(template: str, joints: dict[str, dict]) -> str:
xml = template
for jname, info in joints.items():
lo, hi = info["lo"], info["hi"]
pat = rf'(<joint name="{re.escape(jname)}"[^>]*\srange=")([^"]+)(")'
xml = re.sub(
pat,
lambda m, lo=lo, hi=hi: f"{m.group(1)}{lo} {hi}{m.group(3)}",
xml,
count=1,
)
act_pat = rf'(<position name="{re.escape(jname)}_pos"[^>]*\sctrlrange=")([^"]+)(")'
xml = re.sub(
act_pat,
lambda m, lo=lo, hi=hi: f"{m.group(1)}{lo} {hi}{m.group(3)}",
xml,
count=1,
)
if jname in MCP_ROLL_JOINTS:
ax = info["axis"].strip()
ax_pat = rf'(<joint name="{re.escape(jname)}"[^>]*\saxis=")([^"]+)(")'
xml = re.sub(ax_pat, lambda m, ax=ax: f"{m.group(1)}{ax}{m.group(3)}", xml, count=1)
# 同步零位 body pos/quat(拇指 CMC 等);保留原有 visual/collision geom 结构
for jname, bname in BODY_FROM_JOINT.items():
if jname not in joints:
continue
j = joints[jname]
xyz, rpy = j["xyz"], j["rpy"]
q = rpy_to_quat(*rpy)
pos = f"{xyz[0]:g} {xyz[1]:g} {xyz[2]:g}"
m = re.search(rf'<body name="{re.escape(bname)}"[^>]*>', xml)
if not m:
continue
if np.allclose(q, [1, 0, 0, 0], atol=1e-9):
new = f'<body name="{bname}" pos="{pos}">'
else:
quat = "{:.8g} {:.8g} {:.8g} {:.8g}".format(*q)
new = f'<body name="{bname}" pos="{pos}" quat="{quat}">'
xml = xml[: m.start()] + new + xml[m.end() :]
return xml
joints = parse_urdf(urdf_cal)
xml = apply_urdf_to_mjcf(calib_xml.read_text(), joints)
calib_xml.write_text(xml)
mujoco.MjModel.from_xml_path(str(calib_xml))
for j in MCP_ROLL_JOINTS:
ax = re.search(rf'<joint name="{j}"[^>]*\saxis="([^"]+)"', calib_xml.read_text())
print(f"[g20-left-dual] synced {j} axis={ax.group(1) if ax else '?'}")
for j in ("thumb_cmc_roll", "thumb_cmc_yaw", "thumb_cmc_pitch"):
info = joints[j]
print(f"[g20-left-dual] {j} rpy={info['rpy']} lim=[{info['lo']}, {info['hi']}]")
print(f"[g20-left-dual] calibrated URDF: {urdf_cal}")
print(f"[g20-left-dual] calibrated MJCF: {calib_xml} (package template)")
if meshdir_cal.is_dir():
print(f"[g20-left-dual] calibrated meshes: {meshdir_cal}")
PY
echo "[g20-left-dual] refresh uncalibrated L20 V10.1 left MJCF from $URDF_UNCALIBRATED"
python3 - "$URDF_UNCALIBRATED" "$MESHDIR_UNCAL" "$L20_TEMPLATE" "$STOCK_XML" <<'PY'
import re
import shutil
import sys
from pathlib import Path
import mujoco
import numpy as np
urdf_path = Path(sys.argv[1])
meshdir = Path(sys.argv[2])
template_path = Path(sys.argv[3])
out_path = Path(sys.argv[4])
def rpy_to_quat(roll, pitch, yaw):
cr, sr = np.cos(roll / 2), np.sin(roll / 2)
cp, sp = np.cos(pitch / 2), np.sin(pitch / 2)
cy, sy = np.cos(yaw / 2), np.sin(yaw / 2)
return np.array([
cr * cp * cy + sr * sp * sy,
sr * cp * cy - cr * sp * sy,
cr * sp * cy + sr * cp * sy,
cr * cp * sy - sr * sp * cy,
])
def parse_joints(path: Path) -> dict:
text = path.read_text()
joints = {}
for m in re.finditer(r"<joint\b([^>]*)>(.*?)</joint>", text, re.S):
attrs, body = m.group(1), m.group(2)
name = re.search(r'name="([^"]+)"', attrs).group(1)
jtype = re.search(r'type="([^"]+)"', attrs).group(1)
if jtype != "revolute":
continue
origin = re.search(r'<origin[^>]*xyz="([^"]+)"[^>]*rpy="([^"]+)"', body)
lim = re.search(r'lower="([^"]+)"[^>]*upper="([^"]+)"', body)
axis = re.search(r'<axis[^>]*xyz="([^"]+)"', body)
joints[name] = {
"xyz": tuple(map(float, origin.group(1).split())),
"rpy": tuple(map(float, origin.group(2).split())),
"lo": float(lim.group(1)),
"hi": float(lim.group(2)),
"axis": tuple(map(float, axis.group(1).split())),
}
if "thumb_dip" in joints and "thumb_ip" not in joints:
joints["thumb_ip"] = dict(joints["thumb_dip"])
return joints
def fmt_axis(ax):
return " ".join(f"{v:g}" for v in ax)
def fmt_range(lo, hi):
return f"{lo:g} {hi:g}"
out_path.parent.mkdir(parents=True, exist_ok=True)
mesh_out = out_path.parent / "meshes"
mesh_out.mkdir(exist_ok=True)
for f in meshdir.glob("*"):
shutil.copy2(f, mesh_out / f.name)
shutil.copy2(urdf_path, out_path.parent / urdf_path.name)
# 先读模板全文(out 可能与 template 同路径)
xml = template_path.read_text()
l20 = parse_joints(urdf_path)
joint_specs = {n: (j["lo"], j["hi"], j["axis"]) for n, j in l20.items() if n != "thumb_dip"}
for name, (lo, hi, axis) in joint_specs.items():
pat = re.compile(rf'<joint name="{re.escape(name)}"[^>]*>')
m = pat.search(xml)
if not m:
raise SystemExit(f"missing joint {name}")
new = (
f'<joint name="{name}" pos="0 0 0" axis="{fmt_axis(axis)}" '
f'range="{fmt_range(lo, hi)}" actuatorfrcrange="-100 100"/>'
)
xml = xml[: m.start()] + new + xml[m.end() :]
for name, (lo, hi, _) in joint_specs.items():
pat = re.compile(
rf'(<position name="{re.escape(name)}_pos"[^>]*\bctrlrange=")[^"]+(")'
)
if not pat.search(xml):
raise SystemExit(f"missing actuator {name}_pos")
xml = pat.sub(rf"\g<1>{lo:g} {hi:g}\2", xml, count=1)
body_from_joint = {
"thumb_cmc_roll": "thumb_metacarpals_base1",
"thumb_cmc_yaw": "thumb_metacarpals_base2",
"thumb_cmc_pitch": "thumb_metacarpals",
"thumb_mcp": "thumb_proximal",
"thumb_ip": "thumb_distal",
"index_mcp_roll": "index_metacarpals",
"index_mcp_pitch": "index_proximal",
"index_pip": "index_middle",
"index_dip": "index_distal",
"middle_mcp_roll": "middle_metacarpals",
"middle_mcp_pitch": "middle_proximal",
"middle_pip": "middle_middle",
"middle_dip": "middle_distal",
"ring_mcp_roll": "ring_metacarpals",
"ring_mcp_pitch": "ring_proximal",
"ring_pip": "ring_middle",
"ring_dip": "ring_distal",
"pinky_mcp_roll": "pinky_metacarpals",
"pinky_mcp_pitch": "pinky_proximal",
"pinky_pip": "pinky_middle",
"pinky_dip": "pinky_distal",
}
for jname, bname in body_from_joint.items():
j = l20[jname]
xyz = j["xyz"]
rpy = j["rpy"]
q = rpy_to_quat(*rpy)
pos = f"{xyz[0]:g} {xyz[1]:g} {xyz[2]:g}"
pat = re.compile(rf'<body name="{re.escape(bname)}"[^>]*>')
m = pat.search(xml)
if not m:
raise SystemExit(f"missing body {bname}")
if np.allclose(q, [1, 0, 0, 0], atol=1e-9):
new = f'<body name="{bname}" pos="{pos}">'
else:
quat = "{:.6g} {:.6g} {:.6g} {:.6g}".format(*q)
new = f'<body name="{bname}" pos="{pos}" quat="{quat}">'
xml = xml[: m.start()] + new + xml[m.end() :]
out_path.write_text(xml)
mujoco.MjModel.from_xml_path(str(out_path))
print(f"[g20-left-dual] uncalibrated URDF: {urdf_path}")
print(f"[g20-left-dual] uncalibrated meshes: {meshdir} -> {mesh_out}")
print(f"[g20-left-dual] uncalibrated MJCF -> {out_path} (L20v101 left template + visual/collision split)")
PY
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 "[g20-left-dual] G20 calibrated xml=$CALIB_XML"
"$ROOT/hands/G20/scripts/run_g20_mujoco_plan.sh" \
--xml "$CALIB_XML" \
--mapping-urdf "$URDF_CALIBRATED" \
--node-name g20_calibrated_left \
--window-title "G20 left 标定 (linkerhand_g20_left_tag)" \
--cmd-topic /cb_left_hand_control_cmd \
--sim-state-topic /sim/mujoco/g20/left/calibrated/joint_state \
--mapping g20_calibrated \
"${EXTRA[@]}" &
PID_G20=$!
sleep 1
echo "[g20-left-dual] L20 stock xml=$STOCK_XML (L20v101 left, URDF-native mapping)"
"$ROOT/hands/G20/scripts/run_g20_mujoco_plan.sh" \
--xml "$STOCK_XML" \
--mapping-urdf "$URDF_UNCALIBRATED" \
--node-name l20_stock_left \
--window-title "L20 V10.1 left 未标定" \
--cmd-topic /cb_left_hand_control_cmd \
--sim-state-topic /sim/mujoco/l20/left/stock/joint_state \
--mapping l20_urdf \
"${EXTRA[@]}" &
PID_L20=$!
echo "[g20-left-dual] both started. cmd topic: /cb_left_hand_control_cmd"
echo "[g20-left-dual] mode: kinematic(运动学硬写;稳定跟 0~255)"
echo "[g20-left-dual] 标定 mapping=g20_calibrated(主动线性,仅被动 JSON);未标定 mapping=l20_urdf + collision_project=on"
echo "[g20-left-dual] 需要 PD+自碰: ./hands/G20/scripts/run_g20_left_dual_compare.sh --pd-drive"
echo "[g20-left-dual] Ctrl+C stops both."
wait "$PID_G20" "$PID_L20"
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# 左手四指侧摆方向检查:依次扫 SDK 通道 6~9(index/middle/ring/pinky mcp_roll)
# 需先运行: ./hands/G20/scripts/run_g20_left_desktop_urdf.sh
#
# 预期(g20_urdf, URDF 轴 +X):
# cmd=0 → 关节角为负(约 -0.23 rad,URDF lower)
# cmd=255 → 关节角为正(约 +0.23 rad,URDF upper)
# 若视觉与真机相反,说明 URDF 轴方向或映射仍不一致。
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}"
CMD_TOPIC="${CMD_TOPIC:-/cb_left_hand_control_cmd}"
STATE_TOPIC="${STATE_TOPIC:-/sim/mujoco/g20/left/joint_state}"
exec python3 -m linker_hand_mujoco_ros2.g20_left_mcp_roll_check \
--cmd-topic "$CMD_TOPIC" \
--state-topic "$STATE_TOPIC" \
"$@"
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# MuJoCo G20 左手仿真桥
# 默认订阅 /cb_left_hand_control_cmd(0~255)
# 发布 /sim/mujoco/g20/left/joint_state
# 默认 1.35× 墙钟 + 关节限速;可用 --speed / --slew-rad-s 再调
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
# 源码优先(含 urdf 资产)
PKG_SRC="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2"
export PYTHONPATH="$PKG_SRC${PYTHONPATH:+:$PYTHONPATH}"
exec python3 -m linker_hand_mujoco_ros2.g20_plan_bridge "$@"
+337
View File
@@ -0,0 +1,337 @@
#!/usr/bin/env bash
# 同时开两个 MuJoCo:G20 右手标定 vs L20 原版 URDF(未标定)
#
# 标定: 包内 linker_hand_g20_right.xml(启动前从 linkerhand_G20_RIGHT_tag.urdf 仅 patch 限位/侧摆轴;保留 _col 碰撞体与 body quat)
# 未标定: /home/lxqs/linkerhand_L20_V10 (2).1--0728/linkerhand_L20_V10.1_right.urdf → 运行时生成 *_stock.xml
#
# 默认运动学硬写(与 run_dual_mujoco_compare.sh / O6·L6 双开一致):稳定、跟 0~255 真机命令。
# 需要 PD+自碰时手动加 --pd-drive(两窗均加)。
#
# 两个窗口共用 /cb_right_hand_control_cmd(20 维 0~255)。
#
# 用法:
# ./hands/G20/scripts/run_g20_right_dual_compare.sh
# ./hands/G20/scripts/run_g20_right_dual_compare.sh --cmd-topic /cb_right_hand_control_cmd --speed 1.0
# ./hands/G20/scripts/run_g20_right_dual_compare.sh --pd-drive
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_DIR="$PKG_SRC/urdf/G20/linker_hand_g20_right"
URDF_CALIBRATED="${G20_RIGHT_URDF:-$ROOT/hands/G20/reference/right_calibrated.urdf}"
MESHDIR_CALIB="$G20_DIR/meshes"
URDF_UNCALIBRATED="$PKG_SRC/urdf/L20_V10.1/linker_hand_l20v101_right/linkerhand_L20v10.1_right.urdf"
MESHDIR_UNCAL="$PKG_SRC/urdf/L20_V10.1/linker_hand_l20v101_right/meshes"
CALIB_XML="$G20_DIR/linker_hand_g20_right.xml"
CALIB_URDF_PKG="$G20_DIR/linkerhand_g20_right.urdf"
L20_TEMPLATE="$PKG_SRC/urdf/L20_V10.1/linker_hand_l20v101_left/linker_hand_l20v101_left.xml"
STOCK_XML="$PKG_SRC/urdf/L20_V10.1/linker_hand_l20v101_right/linker_hand_l20v101_right.xml"
if [[ ! -f "$URDF_CALIBRATED" ]]; then
echo "[error] missing calibrated URDF: $URDF_CALIBRATED" >&2
exit 1
fi
if [[ ! -f "$CALIB_XML" ]]; then
echo "[error] missing calibrated MJCF template: $CALIB_XML" >&2
exit 1
fi
if [[ ! -f "$URDF_UNCALIBRATED" ]]; then
echo "[error] missing uncalibrated URDF: $URDF_UNCALIBRATED" >&2
exit 1
fi
if [[ ! -d "$MESHDIR_UNCAL" ]]; then
echo "[error] missing uncalibrated meshes: $MESHDIR_UNCAL" >&2
exit 1
fi
if [[ ! -f "$L20_TEMPLATE" ]]; then
echo "[error] missing L20 V10.1 left MJCF template: $L20_TEMPLATE" >&2
exit 1
fi
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
export PYTHONPATH="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2${PYTHONPATH:+:$PYTHONPATH}"
echo "[g20-right-dual] sync calibrated MJCF from G20_RIGHT_tag URDF -> $CALIB_XML"
python3 - "$URDF_CALIBRATED" "$MESHDIR_CALIB" "$CALIB_XML" "$CALIB_URDF_PKG" <<'PY'
import re
import shutil
import sys
from pathlib import Path
import mujoco
urdf_cal = Path(sys.argv[1])
meshdir_cal = Path(sys.argv[2])
calib_xml = Path(sys.argv[3])
calib_urdf_pkg = Path(sys.argv[4])
# 包内留一份 Linkerbot URDF 副本,便于对照
shutil.copy2(urdf_cal, calib_urdf_pkg)
# 只同步限位/轴;不要改 body quat(会破坏 MJCF 运动链,_col 与 visual 错位→对指穿模)
MCP_ROLL_JOINTS = (
"index_mcp_roll",
"middle_mcp_roll",
"ring_mcp_roll",
"pinky_mcp_roll",
)
def parse_urdf(path: Path) -> dict[str, dict]:
text = path.read_text()
joints: dict[str, dict] = {}
for m in re.finditer(r"<joint\b([^>]*)>(.*?)</joint>", text, re.S):
attrs, body = m.group(1), m.group(2)
name_m = re.search(r'name="([^"]+)"', attrs)
if not name_m:
continue
name = name_m.group(1)
if 'revolute' not in attrs:
continue
origin = re.search(r'<origin[^>]*xyz="([^"]+)"[^>]*rpy="([^"]+)"', body)
lim = re.search(r'lower="([^"]+)"[^>]*upper="([^"]+)"', body)
# URDF 常写成 <axis\n xyz="..."/>,不能写死 "axis xyz="
axis = re.search(r'<axis[^>]*xyz="([^"]+)"', body)
rpy = [float(x) for x in origin.group(2).split()] if origin else [0.0, 0.0, 0.0]
joints[name] = {
"lo": float(lim.group(1)),
"hi": float(lim.group(2)),
"rpy": rpy,
"axis": axis.group(1) if axis else "1 0 0",
}
return joints
def apply_urdf_to_mjcf(template: str, joints: dict[str, dict]) -> str:
xml = template
for jname, info in joints.items():
lo, hi = info["lo"], info["hi"]
pat = rf'(<joint name="{re.escape(jname)}"[^>]*\srange=")([^"]+)(")'
xml = re.sub(
pat,
lambda m, lo=lo, hi=hi: f"{m.group(1)}{lo} {hi}{m.group(3)}",
xml,
count=1,
)
act_pat = rf'(<position name="{re.escape(jname)}_pos"[^>]*\sctrlrange=")([^"]+)(")'
xml = re.sub(
act_pat,
lambda m, lo=lo, hi=hi: f"{m.group(1)}{lo} {hi}{m.group(3)}",
xml,
count=1,
)
if jname in MCP_ROLL_JOINTS:
ax = info["axis"].strip()
ax_pat = rf'(<joint name="{re.escape(jname)}"[^>]*\saxis=")([^"]+)(")'
xml = re.sub(ax_pat, lambda m, ax=ax: f"{m.group(1)}{ax}{m.group(3)}", xml, count=1)
return xml
joints = parse_urdf(urdf_cal)
xml = apply_urdf_to_mjcf(calib_xml.read_text(), joints)
calib_xml.write_text(xml)
mujoco.MjModel.from_xml_path(str(calib_xml))
for j in MCP_ROLL_JOINTS:
ax = re.search(rf'<joint name="{j}"[^>]*\saxis="([^"]+)"', calib_xml.read_text())
print(f"[g20-right-dual] synced {j} axis={ax.group(1) if ax else '?'}")
print(f"[g20-right-dual] calibrated URDF: {urdf_cal}")
print(f"[g20-right-dual] calibrated MJCF: {calib_xml} (package template + _col)")
if meshdir_cal.is_dir():
print(f"[g20-right-dual] calibrated meshes: {meshdir_cal} (package _col meshes)")
PY
echo "[g20-right-dual] build uncalibrated L20 V10.1 right MJCF from template + $URDF_UNCALIBRATED"
python3 - "$URDF_UNCALIBRATED" "$MESHDIR_UNCAL" "$L20_TEMPLATE" "$STOCK_XML" <<'PY'
import re
import shutil
import sys
from pathlib import Path
import mujoco
import numpy as np
urdf_path = Path(sys.argv[1])
meshdir = Path(sys.argv[2])
template_path = Path(sys.argv[3])
out_path = Path(sys.argv[4])
def rpy_to_quat(roll, pitch, yaw):
cr, sr = np.cos(roll / 2), np.sin(roll / 2)
cp, sp = np.cos(pitch / 2), np.sin(pitch / 2)
cy, sy = np.cos(yaw / 2), np.sin(yaw / 2)
return np.array([
cr * cp * cy + sr * sp * sy,
sr * cp * cy - cr * sp * sy,
cr * sp * cy + sr * cp * sy,
cr * cp * sy - sr * sp * cy,
])
def parse_joints(path: Path) -> dict:
text = path.read_text()
joints = {}
for m in re.finditer(r"<joint\b([^>]*)>(.*?)</joint>", text, re.S):
attrs, body = m.group(1), m.group(2)
name = re.search(r'name="([^"]+)"', attrs).group(1)
jtype = re.search(r'type="([^"]+)"', attrs).group(1)
if jtype != "revolute":
continue
origin = re.search(r'<origin[^>]*xyz="([^"]+)"[^>]*rpy="([^"]+)"', body)
lim = re.search(r'lower="([^"]+)"[^>]*upper="([^"]+)"', body)
axis = re.search(r'<axis[^>]*xyz="([^"]+)"', body)
joints[name] = {
"xyz": tuple(map(float, origin.group(1).split())),
"rpy": tuple(map(float, origin.group(2).split())),
"lo": float(lim.group(1)),
"hi": float(lim.group(2)),
"axis": tuple(map(float, axis.group(1).split())),
}
if "thumb_dip" in joints and "thumb_ip" not in joints:
joints["thumb_ip"] = dict(joints["thumb_dip"])
return joints
def fmt_axis(ax):
return " ".join(f"{v:g}" for v in ax)
def fmt_range(lo, hi):
return f"{lo:g} {hi:g}"
out_path.parent.mkdir(parents=True, exist_ok=True)
mesh_out = out_path.parent / "meshes"
mesh_out.mkdir(exist_ok=True)
for f in meshdir.glob("*"):
shutil.copy2(f, mesh_out / f.name)
shutil.copy2(urdf_path, out_path.parent / urdf_path.name)
l20 = parse_joints(urdf_path)
xml = template_path.read_text()
xml = xml.replace('model="linker_hand_l20v101_left"', 'model="linker_hand_l20v101_right"')
joint_specs = {n: (j["lo"], j["hi"], j["axis"]) for n, j in l20.items() if n != "thumb_dip"}
for name, (lo, hi, axis) in joint_specs.items():
pat = re.compile(rf'<joint name="{re.escape(name)}"[^>]*>')
m = pat.search(xml)
if not m:
raise SystemExit(f"missing joint {name}")
new = (
f'<joint name="{name}" pos="0 0 0" axis="{fmt_axis(axis)}" '
f'range="{fmt_range(lo, hi)}" actuatorfrcrange="-100 100"/>'
)
xml = xml[: m.start()] + new + xml[m.end() :]
for name, (lo, hi, _) in joint_specs.items():
pat = re.compile(
rf'(<position name="{re.escape(name)}_pos"[^>]*\bctrlrange=")[^"]+(")'
)
if not pat.search(xml):
raise SystemExit(f"missing actuator {name}_pos")
xml = pat.sub(rf"\g<1>{lo:g} {hi:g}\2", xml, count=1)
body_from_joint = {
"thumb_cmc_roll": "thumb_metacarpals_base1",
"thumb_cmc_yaw": "thumb_metacarpals_base2",
"thumb_cmc_pitch": "thumb_metacarpals",
"thumb_mcp": "thumb_proximal",
"thumb_ip": "thumb_distal",
"index_mcp_roll": "index_metacarpals",
"index_mcp_pitch": "index_proximal",
"index_pip": "index_middle",
"index_dip": "index_distal",
"middle_mcp_roll": "middle_metacarpals",
"middle_mcp_pitch": "middle_proximal",
"middle_pip": "middle_middle",
"middle_dip": "middle_distal",
"ring_mcp_roll": "ring_metacarpals",
"ring_mcp_pitch": "ring_proximal",
"ring_pip": "ring_middle",
"ring_dip": "ring_distal",
"pinky_mcp_roll": "pinky_metacarpals",
"pinky_mcp_pitch": "pinky_proximal",
"pinky_pip": "pinky_middle",
"pinky_dip": "pinky_distal",
}
for jname, bname in body_from_joint.items():
j = l20[jname]
xyz = j["xyz"]
rpy = j["rpy"]
q = rpy_to_quat(*rpy)
pos = f"{xyz[0]:g} {xyz[1]:g} {xyz[2]:g}"
pat = re.compile(rf'<body name="{re.escape(bname)}"[^>]*>')
m = pat.search(xml)
if not m:
raise SystemExit(f"missing body {bname}")
if np.allclose(q, [1, 0, 0, 0], atol=1e-9):
new = f'<body name="{bname}" pos="{pos}">'
else:
quat = "{:.6g} {:.6g} {:.6g} {:.6g}".format(*q)
new = f'<body name="{bname}" pos="{pos}" quat="{quat}">'
xml = xml[: m.start()] + new + xml[m.end() :]
out_path.write_text(xml)
mujoco.MjModel.from_xml_path(str(out_path))
print(f"[g20-right-dual] uncalibrated URDF: {urdf_path}")
print(f"[g20-right-dual] uncalibrated meshes: {meshdir} -> {mesh_out}")
print(f"[g20-right-dual] uncalibrated MJCF -> {out_path} (L20v101 template + visual/collision split)")
PY
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 "[g20-right-dual] G20 calibrated xml=$CALIB_XML"
"$ROOT/hands/G20/scripts/run_g20_right_mujoco_plan.sh" \
--xml "$CALIB_XML" \
--mapping-urdf "$URDF_CALIBRATED" \
--node-name g20_calibrated_right \
--window-title "G20 right 标定 (G20_RIGHT_tag)" \
--cmd-topic /cb_right_hand_control_cmd \
--sim-state-topic /sim/mujoco/g20/right/calibrated/joint_state \
--mapping g20_urdf \
"${EXTRA[@]}" &
PID_G20=$!
sleep 1
echo "[g20-right-dual] L20 stock xml=$STOCK_XML (L20v101 template, URDF-native mapping)"
"$ROOT/hands/G20/scripts/run_g20_right_mujoco_plan.sh" \
--xml "$STOCK_XML" \
--mapping-urdf "$URDF_UNCALIBRATED" \
--node-name l20_stock_right \
--window-title "L20 V10.1 right 未标定" \
--cmd-topic /cb_right_hand_control_cmd \
--sim-state-topic /sim/mujoco/l20/right/stock/joint_state \
--mapping l20_urdf \
"${EXTRA[@]}" &
PID_L20=$!
echo "[g20-right-dual] both started. cmd topic: /cb_right_hand_control_cmd"
echo "[g20-right-dual] mode: kinematic(运动学硬写,与左手双开一致;稳定跟 0~255)"
echo "[g20-right-dual] 标定 mapping=g20_urdf(仅按URDF,不查JSON);未标定 mapping=l20_urdf + collision_project=on"
echo "[g20-right-dual] 需要 PD+自碰: ./hands/G20/scripts/run_g20_right_dual_compare.sh --pd-drive"
echo "[g20-right-dual] Ctrl+C stops both."
wait "$PID_G20" "$PID_L20"
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
# G20 右手关节弧度监视 GUI(默认 6 位小数)
# 订阅 /sim/mujoco/g20/right/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 \
--topic /sim/mujoco/g20/right/joint_state \
"$@"
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
# MuJoCo G20 右手仿真桥
# 默认订阅 /cb_right_hand_control_cmd(0~255,与真机 SDK 一致)
# 发布 /sim/mujoco/g20/right/joint_state
# 模型:urdf/G20/linker_hand_g20_right/linker_hand_g20_right.xml
# 默认 1.35× 墙钟 + 关节限速;可用 --speed / --slew-rad-s 再调
#
# 用法:
# ./hands/G20/scripts/run_g20_right_mujoco_plan.sh
# ./hands/G20/scripts/run_g20_right_mujoco_plan.sh --cmd-topic /g20/cb_right_hand_control_cmd
# 监视关节(另开终端):
# ./hands/G20/scripts/run_g20_right_joint_gui.sh
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
# 源码优先(含 urdf 资产)
PKG_SRC="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2"
export PYTHONPATH="$PKG_SRC${PYTHONPATH:+:$PYTHONPATH}"
XML="$PKG_SRC/linker_hand_mujoco_ros2/urdf/G20/linker_hand_g20_right/linker_hand_g20_right.xml"
if [[ ! -f "$XML" ]]; then
echo "[error] missing G20 right MJCF: $XML" >&2
exit 1
fi
echo "[g20-right] xml=$XML"
echo "[g20-right] model=G20_RIGHT_tag"
echo "[g20-right] cmd-topic=/cb_right_hand_control_cmd (override with --cmd-topic)"
echo "[g20-right] sim-state=/sim/mujoco/g20/right/joint_state"
exec python3 -m linker_hand_mujoco_ros2.g20_plan_bridge \
--xml "$XML" \
--node-name g20_mujoco_plan_bridge_right \
--window-title "G20 right" \
--cmd-topic /cb_right_hand_control_cmd \
--sim-state-topic /sim/mujoco/g20/right/joint_state \
--nominal-topic /retarget/g20/right/joint_target_nominal \
--mapping g20_urdf \
"$@"
+6
View File
@@ -0,0 +1,6 @@
# L20_V10.1
[统一入口与映射说明](../README.md)
`assets/` 是包内模型链接;`scripts/` 为内部实现及辅助工具。
+1
View File
@@ -0,0 +1 @@
../../src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/L20_V10.1
+10
View File
@@ -0,0 +1,10 @@
# L6
[统一入口与映射说明](../README.md)
- `compare_left.sh`
- `compare_right.sh`
- `run_left.sh`
- `run_right.sh`
`assets/` 是包内模型链接;`scripts/` 为内部实现及辅助工具。
+1
View File
@@ -0,0 +1 @@
../../src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/L6
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_l6_left_dual_compare.sh" "$@"
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_l6_right_dual_compare.sh" "$@"
+956
View File
@@ -0,0 +1,956 @@
<?xml version="1.0" encoding="utf-8"?>
<robot
name="linkerhand_l6v3.1_left">
<!-- 手掌基座,作为整只手的根链接 -->
<link
name="lh_hand_base_link">
<inertial>
<origin
xyz="0.00199 -0.00091 0.058381"
rpy="0 0 0" />
<mass
value="0.4185" />
<inertia
ixx="0.0004998"
ixy="0.000012853"
ixz="-0.000008317"
iyy="0.0003567"
iyz="0.000010354"
izz="0.0002301" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/hand_base_link.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="lh_thumb_metacarpals_base1">
<inertial>
<origin
xyz="0.0018663 -0.0066406 0.001533"
rpy="0 0 0" />
<mass
value="0.01" />
<inertia
ixx="3.3724E-07"
ixy="1.11E-07"
ixz="-3.6039E-08"
iyy="2.9457E-07"
iyz="5.7181E-08"
izz="4.903E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="lh_thumb_cmc_roll"
type="revolute">
<origin
xyz="0.0078133 -0.029898 0.026692"
rpy="-0.0349060486431202 -0.000257767554592503 -0.00738147635662772" />
<parent
link="lh_hand_base_link" />
<child
link="lh_thumb_metacarpals_base1" />
<axis
xyz="0 0 1" />
<limit
lower="0"
upper="1.12177324"
effort="0.588"
velocity="5.95" />
<dynamics damping="0.02" friction="0.05416667"/>
</joint>
<link
name="lh_thumb_metacarpals">
<inertial>
<origin
xyz="0.00769270907060086 0.0859662184722988 0.0235283086675235"
rpy="0 0 0" />
<mass
value="0.065" />
<inertia
ixx="8.407E-06"
ixy="-1.1338E-09"
ixz="1.1136E-08"
iyy="8.3899E-06"
iyz="3.1171E-07"
izz="2.0091E-06" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="lh_thumb_cmc_pitch"
type="revolute">
<origin
xyz="0.0057298 -0.01332 0.0027456"
rpy="1.01778644 0 0.40143" />
<parent
link="lh_thumb_metacarpals_base1" />
<child
link="lh_thumb_metacarpals" />
<axis
xyz="-1 0 0" />
<limit
lower="0"
upper="0.65673088"
effort="1.008"
velocity="3.47" />
<dynamics damping="0.02" friction="0.0375"/>
</joint>
<link
name="lh_thumb_distal">
<inertial>
<origin
xyz="0.00769270907081708 0.0798662184721594 -0.0591716913325555"
rpy="0 0 0" />
<mass
value="0.018" />
<inertia
ixx="1.5546E-06"
ixy="2.4423E-09"
ixz="2.8147E-09"
iyy="1.7108E-06"
iyz="-7.2756E-08"
izz="9.1709E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<link
name="lh_thumb_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="lh_thumb_tip_fixed"
type="fixed">
<origin
xyz="0 0.0035 0.026"
rpy="0 0 1.5708" />
<parent
link="lh_thumb_distal" />
<child
link="lh_thumb_tip" />
</joint>
<joint
name="lh_thumb_dip"
type="revolute">
<origin
xyz="0 0.0061 0.0827"
rpy="0 0 0" />
<parent
link="lh_thumb_metacarpals" />
<child
link="lh_thumb_distal" />
<axis
xyz="-1 0 0" />
<limit
lower="0"
upper="0.963287"
effort="0"
velocity="1.7" />
<mimic
joint="lh_thumb_cmc_pitch"
multiplier="0.88524192477979"
offset="0" />
</joint>
<link
name="lh_index_proximal">
<inertial>
<origin
xyz="0.001776 -0.00011 0.012556"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="0.000000797543"
ixy="-0.000000001275"
ixz="0.000000138601"
iyy="0.000000800499"
iyz="0.000000000939"
izz="0.000000357001" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="lh_index_mcp_pitch"
type="revolute">
<origin
xyz="-0.00066821 -0.028967 0.11722"
rpy="0 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_index_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.23208487"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.04"/>
</joint>
<link
name="lh_index_distal">
<inertial>
<origin
xyz="0.0134 0.002262 0.016992"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="0.000002488712"
ixy="0.000000008249"
ixz="-0.000001523059"
iyy="0.000003723754"
iyz="0.000000014019"
izz="0.000001813582" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<link
name="lh_index_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="lh_index_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="lh_index_distal" />
<child
link="lh_index_tip" />
</joint>
<joint
name="lh_index_dip"
type="revolute">
<origin
xyz="0.00093749 0 0.028002"
rpy="0 0 0" />
<parent
link="lh_index_proximal" />
<child
link="lh_index_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="lh_index_mcp_pitch"
multiplier="0.923280528556446"
offset="0" />
</joint>
<link
name="lh_middle_proximal">
<inertial>
<origin
xyz="0.001776 -0.00011 0.012556"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="0.000000797543"
ixy="-0.000000001275"
ixz="0.000000138601"
iyy="0.000000800499"
iyz="0.000000000939"
izz="0.000000357001" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="lh_middle_mcp_pitch"
type="revolute">
<origin
xyz="-0.00066821 -0.0089013 0.12322"
rpy="0 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_middle_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.23208487"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.02708333"/>
</joint>
<link
name="lh_middle_distal">
<inertial>
<origin
xyz="0.0134 0.002262 0.016992"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="0.000002488712"
ixy="0.000000008249"
ixz="-0.000001523059"
iyy="0.000003723754"
iyz="0.000000014019"
izz="0.000001813582" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<link
name="lh_middle_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="lh_middle_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="lh_middle_distal" />
<child
link="lh_middle_tip" />
</joint>
<joint
name="lh_middle_dip"
type="revolute">
<origin
xyz="0.00093749 0 0.028002"
rpy="0 0 0" />
<parent
link="lh_middle_proximal" />
<child
link="lh_middle_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="lh_middle_mcp_pitch"
multiplier="0.923280528556446"
offset="0" />
</joint>
<link
name="lh_ring_proximal">
<inertial>
<origin
xyz="0.001776 -0.00011 0.012556"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="0.000000797543"
ixy="-0.000000001275"
ixz="0.000000138601"
iyy="0.000000800499"
iyz="0.000000000939"
izz="0.000000357001" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="lh_ring_mcp_pitch"
type="revolute">
<origin
xyz="-0.00067368 0.010717 0.11822"
rpy="0 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_ring_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.23208487"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.02291667"/>
</joint>
<link
name="lh_ring_distal">
<inertial>
<origin
xyz="0.0134 0.002262 0.016992"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="0.000002488712"
ixy="0.000000008249"
ixz="-0.000001523059"
iyy="0.000003723754"
iyz="0.000000014019"
izz="0.000001813582" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<link
name="lh_ring_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="lh_ring_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="lh_ring_distal" />
<child
link="lh_ring_tip" />
</joint>
<joint
name="lh_ring_dip"
type="revolute">
<origin
xyz="0.00093749 0 0.028002"
rpy="0 0 0" />
<parent
link="lh_ring_proximal" />
<child
link="lh_ring_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="lh_ring_mcp_pitch"
multiplier="0.923280528556446"
offset="0" />
</joint>
<link
name="lh_pinky_proximal">
<inertial>
<origin
xyz="0.001776 -0.00011 0.012556"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="0.000000797543"
ixy="-0.000000001275"
ixz="0.000000138601"
iyy="0.000000800499"
iyz="0.000000000939"
izz="0.000000357001" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="lh_pinky_mcp_pitch"
type="revolute">
<origin
xyz="-0.00067852 0.029814 0.11424"
rpy="0 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_pinky_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.23208487"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.01"/>
</joint>
<link
name="lh_pinky_distal">
<inertial>
<origin
xyz="0.0134 0.002262 0.016992"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="0.000002488712"
ixy="0.000000008249"
ixz="-0.000001523059"
iyy="0.000003723754"
iyz="0.000000014019"
izz="0.000001813582" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<link
name="lh_pinky_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="lh_pinky_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="lh_pinky_distal" />
<child
link="lh_pinky_tip" />
</joint>
<joint
name="lh_pinky_dip"
type="revolute">
<origin
xyz="0.00093749 0 0.028002"
rpy="0 0 0" />
<parent
link="lh_pinky_proximal" />
<child
link="lh_pinky_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="lh_pinky_mcp_pitch"
multiplier="0.923280528556446"
offset="0" />
</joint>
<transmission name="trans_thumb_cmc_roll">
<type>transmission_interface/SimpleTransmission</type>
<joint name="lh_thumb_cmc_roll">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_thumb_cmc_roll">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0084</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_index_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="lh_index_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_index_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_middle_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="lh_middle_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_middle_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_ring_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="lh_ring_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_ring_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_pinky_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="lh_pinky_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_pinky_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_thumb_cmc_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="lh_thumb_cmc_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_thumb_cmc_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0144</mechanicalReduction>
</actuator>
</transmission>
<mujoco>
<compiler meshdir="meshes/" balanceinertia="true" discardvisual="false"/>
<default>
<joint armature="3.4707e-08" damping="5.0" frictionloss="0.5"/>
</default>
<equality>
<!-- 四指DIP关节的mimic约束 - 统一使用1.125676 -->
<!-- polycoef格式: c0 c1 c2 c3 c4 c5,关系: q1 = c0 + c1*q2 + c2*q2^2 + ... -->
<!-- 对于 mimic: q_dip = 0 + 1.125676*q_mcp + 0*q_mcp^2 + ... -->
<joint name="lh_couple_index" joint1="lh_index_dip" joint2="lh_index_mcp_pitch" polycoef="0 1.17646620703189 -0.184902818001199 0 0 0"/>
<joint name="lh_couple_middle" joint1="lh_middle_dip" joint2="lh_middle_mcp_pitch" polycoef="0 1.17646620703189 -0.184902818001199 0 0 0"/>
<joint name="lh_couple_ring" joint1="lh_ring_dip" joint2="lh_ring_mcp_pitch" polycoef="0 1.17646620703189 -0.184902818001199 0 0 0"/>
<joint name="lh_couple_pinky" joint1="lh_pinky_dip" joint2="lh_pinky_mcp_pitch" polycoef="0 1.17646620703189 -0.184902818001199 0 0 0"/>
<!-- 大拇指DIP关节的mimic约束 - 使用1.226495 -->
<joint name="lh_couple_thumb" joint1="lh_thumb_dip" joint2="lh_thumb_cmc_pitch" polycoef="0 0.88524192477979 0 0 0 0"/>
</equality>
</mujoco>
<gazebo reference="lh_hand_base_link">
<mu1>1.5</mu1>
<mu2>1.5</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="lh_thumb_distal">
<mu1>1.5</mu1>
<mu2>1.5</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="lh_index_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="lh_middle_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="lh_ring_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="lh_pinky_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
</robot>
+956
View File
@@ -0,0 +1,956 @@
<?xml version="1.0" encoding="utf-8"?>
<robot
name="linkerhand_l6v3.1_left">
<!-- 手掌基座,作为整只手的根链接 -->
<link
name="lh_hand_base_link">
<inertial>
<origin
xyz="0.00199 -0.00091 0.058381"
rpy="0 0 0" />
<mass
value="0.4185" />
<inertia
ixx="0.0004998"
ixy="0.000012853"
ixz="-0.000008317"
iyy="0.0003567"
iyz="0.000010354"
izz="0.0002301" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/hand_base_link.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="lh_thumb_metacarpals_base1">
<inertial>
<origin
xyz="0.0018663 -0.0066406 0.001533"
rpy="0 0 0" />
<mass
value="0.01" />
<inertia
ixx="3.3724E-07"
ixy="1.11E-07"
ixz="-3.6039E-08"
iyy="2.9457E-07"
iyz="5.7181E-08"
izz="4.903E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="lh_thumb_cmc_roll"
type="revolute">
<origin
xyz="0.0078133 -0.029898 0.026692"
rpy="-0.034907 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_thumb_metacarpals_base1" />
<axis
xyz="0 0 1" />
<limit
lower="-0.087266"
upper="1.256637"
effort="0.588"
velocity="5.95" />
<dynamics damping="0.02" friction="0.05416667"/>
</joint>
<link
name="lh_thumb_metacarpals">
<inertial>
<origin
xyz="0.00769270907060086 0.0859662184722988 0.0235283086675235"
rpy="0 0 0" />
<mass
value="0.065" />
<inertia
ixx="8.407E-06"
ixy="-1.1338E-09"
ixz="1.1136E-08"
iyy="8.3899E-06"
iyz="3.1171E-07"
izz="2.0091E-06" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="lh_thumb_cmc_pitch"
type="revolute">
<origin
xyz="0.0057298 -0.01332 0.0027456"
rpy="1.1083 0 0.40143" />
<parent
link="lh_thumb_metacarpals_base1" />
<child
link="lh_thumb_metacarpals" />
<axis
xyz="-1 0 0" />
<limit
lower="0"
upper="0.837758"
effort="1.008"
velocity="3.47" />
<dynamics damping="0.02" friction="0.0375"/>
</joint>
<link
name="lh_thumb_distal">
<inertial>
<origin
xyz="0.00769270907081708 0.0798662184721594 -0.0591716913325555"
rpy="0 0 0" />
<mass
value="0.018" />
<inertia
ixx="1.5546E-06"
ixy="2.4423E-09"
ixz="2.8147E-09"
iyy="1.7108E-06"
iyz="-7.2756E-08"
izz="9.1709E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<link
name="lh_thumb_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="lh_thumb_tip_fixed"
type="fixed">
<origin
xyz="0 0.0035 0.026"
rpy="0 0 1.5708" />
<parent
link="lh_thumb_distal" />
<child
link="lh_thumb_tip" />
</joint>
<joint
name="lh_thumb_dip"
type="revolute">
<origin
xyz="0 0.0061 0.0827"
rpy="0 0 0" />
<parent
link="lh_thumb_metacarpals" />
<child
link="lh_thumb_distal" />
<axis
xyz="-1 0 0" />
<limit
lower="0"
upper="0.963287"
effort="0"
velocity="1.7" />
<mimic
joint="lh_thumb_cmc_pitch"
multiplier="1.226495"
offset="0" />
</joint>
<link
name="lh_index_proximal">
<inertial>
<origin
xyz="0.001776 -0.00011 0.012556"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="0.000000797543"
ixy="-0.000000001275"
ixz="0.000000138601"
iyy="0.000000800499"
iyz="0.000000000939"
izz="0.000000357001" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="lh_index_mcp_pitch"
type="revolute">
<origin
xyz="-0.00066821 -0.028967 0.11722"
rpy="0 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_index_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.134464"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.04"/>
</joint>
<link
name="lh_index_distal">
<inertial>
<origin
xyz="0.0134 0.002262 0.016992"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="0.000002488712"
ixy="0.000000008249"
ixz="-0.000001523059"
iyy="0.000003723754"
iyz="0.000000014019"
izz="0.000001813582" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<link
name="lh_index_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="lh_index_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="lh_index_distal" />
<child
link="lh_index_tip" />
</joint>
<joint
name="lh_index_dip"
type="revolute">
<origin
xyz="0.00093749 0 0.028002"
rpy="0 0 0" />
<parent
link="lh_index_proximal" />
<child
link="lh_index_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="lh_index_mcp_pitch"
multiplier="1.125676"
offset="0" />
</joint>
<link
name="lh_middle_proximal">
<inertial>
<origin
xyz="0.001776 -0.00011 0.012556"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="0.000000797543"
ixy="-0.000000001275"
ixz="0.000000138601"
iyy="0.000000800499"
iyz="0.000000000939"
izz="0.000000357001" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="lh_middle_mcp_pitch"
type="revolute">
<origin
xyz="-0.00066821 -0.0089013 0.12322"
rpy="0 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_middle_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.134464"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.02708333"/>
</joint>
<link
name="lh_middle_distal">
<inertial>
<origin
xyz="0.0134 0.002262 0.016992"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="0.000002488712"
ixy="0.000000008249"
ixz="-0.000001523059"
iyy="0.000003723754"
iyz="0.000000014019"
izz="0.000001813582" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<link
name="lh_middle_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="lh_middle_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="lh_middle_distal" />
<child
link="lh_middle_tip" />
</joint>
<joint
name="lh_middle_dip"
type="revolute">
<origin
xyz="0.00093749 0 0.028002"
rpy="0 0 0" />
<parent
link="lh_middle_proximal" />
<child
link="lh_middle_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="lh_middle_mcp_pitch"
multiplier="1.125676"
offset="0" />
</joint>
<link
name="lh_ring_proximal">
<inertial>
<origin
xyz="0.001776 -0.00011 0.012556"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="0.000000797543"
ixy="-0.000000001275"
ixz="0.000000138601"
iyy="0.000000800499"
iyz="0.000000000939"
izz="0.000000357001" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="lh_ring_mcp_pitch"
type="revolute">
<origin
xyz="-0.00067368 0.010717 0.11822"
rpy="0 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_ring_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.134464"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.02291667"/>
</joint>
<link
name="lh_ring_distal">
<inertial>
<origin
xyz="0.0134 0.002262 0.016992"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="0.000002488712"
ixy="0.000000008249"
ixz="-0.000001523059"
iyy="0.000003723754"
iyz="0.000000014019"
izz="0.000001813582" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<link
name="lh_ring_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="lh_ring_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="lh_ring_distal" />
<child
link="lh_ring_tip" />
</joint>
<joint
name="lh_ring_dip"
type="revolute">
<origin
xyz="0.00093749 0 0.028002"
rpy="0 0 0" />
<parent
link="lh_ring_proximal" />
<child
link="lh_ring_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="lh_ring_mcp_pitch"
multiplier="1.125676"
offset="0" />
</joint>
<link
name="lh_pinky_proximal">
<inertial>
<origin
xyz="0.001776 -0.00011 0.012556"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="0.000000797543"
ixy="-0.000000001275"
ixz="0.000000138601"
iyy="0.000000800499"
iyz="0.000000000939"
izz="0.000000357001" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="lh_pinky_mcp_pitch"
type="revolute">
<origin
xyz="-0.00067852 0.029814 0.11424"
rpy="0 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_pinky_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.134464"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.01"/>
</joint>
<link
name="lh_pinky_distal">
<inertial>
<origin
xyz="0.0134 0.002262 0.016992"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="0.000002488712"
ixy="0.000000008249"
ixz="-0.000001523059"
iyy="0.000003723754"
iyz="0.000000014019"
izz="0.000001813582" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<link
name="lh_pinky_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="lh_pinky_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="lh_pinky_distal" />
<child
link="lh_pinky_tip" />
</joint>
<joint
name="lh_pinky_dip"
type="revolute">
<origin
xyz="0.00093749 0 0.028002"
rpy="0 0 0" />
<parent
link="lh_pinky_proximal" />
<child
link="lh_pinky_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="lh_pinky_mcp_pitch"
multiplier="1.125676"
offset="0" />
</joint>
<transmission name="trans_thumb_cmc_roll">
<type>transmission_interface/SimpleTransmission</type>
<joint name="lh_thumb_cmc_roll">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_thumb_cmc_roll">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0084</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_index_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="lh_index_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_index_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_middle_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="lh_middle_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_middle_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_ring_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="lh_ring_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_ring_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_pinky_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="lh_pinky_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_pinky_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_thumb_cmc_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="lh_thumb_cmc_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_thumb_cmc_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0144</mechanicalReduction>
</actuator>
</transmission>
<mujoco>
<compiler meshdir="meshes/" balanceinertia="true" discardvisual="false"/>
<default>
<joint armature="3.4707e-08" damping="5.0" frictionloss="0.5"/>
</default>
<equality>
<!-- 四指DIP关节的mimic约束 - 统一使用1.125676 -->
<!-- polycoef格式: c0 c1 c2 c3 c4 c5,关系: q1 = c0 + c1*q2 + c2*q2^2 + ... -->
<!-- 对于 mimic: q_dip = 0 + 1.125676*q_mcp + 0*q_mcp^2 + ... -->
<joint name="lh_couple_index" joint1="lh_index_dip" joint2="lh_index_mcp_pitch" polycoef="0 1.125676 0 0 0 0"/>
<joint name="lh_couple_middle" joint1="lh_middle_dip" joint2="lh_middle_mcp_pitch" polycoef="0 1.125676 0 0 0 0"/>
<joint name="lh_couple_ring" joint1="lh_ring_dip" joint2="lh_ring_mcp_pitch" polycoef="0 1.125676 0 0 0 0"/>
<joint name="lh_couple_pinky" joint1="lh_pinky_dip" joint2="lh_pinky_mcp_pitch" polycoef="0 1.125676 0 0 0 0"/>
<!-- 大拇指DIP关节的mimic约束 - 使用1.226495 -->
<joint name="lh_couple_thumb" joint1="lh_thumb_dip" joint2="lh_thumb_cmc_pitch" polycoef="0 1.226495 0 0 0 0"/>
</equality>
</mujoco>
<gazebo reference="lh_hand_base_link">
<mu1>1.5</mu1>
<mu2>1.5</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="lh_thumb_distal">
<mu1>1.5</mu1>
<mu2>1.5</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="lh_index_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="lh_middle_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="lh_ring_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="lh_pinky_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
</robot>
+953
View File
@@ -0,0 +1,953 @@
<?xml version="1.0" encoding="utf-8"?>
<robot
name="linkerhand_l6v3.1_right">
<!-- 手掌基座,作为整只手的根链接 -->
<link
name="rh_hand_base_link">
<inertial>
<origin
xyz="0.0019900000 0.0009100000 0.0583810000"
rpy="0 0 0" />
<mass
value="0.4185" />
<inertia
ixx="0.0004998"
ixy="-1.2853e-05"
ixz="-8.317e-06"
iyy="0.0003567"
iyz="-1.0354e-05"
izz="0.0002301" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/hand_base_link.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="rh_thumb_metacarpals_base1">
<inertial>
<origin
xyz="0.0018663000 0.0066406000 0.0015330000"
rpy="0 0 0" />
<mass
value="0.01" />
<inertia
ixx="3.3724e-07"
ixy="-1.11e-07"
ixz="-3.6039e-08"
iyy="2.9457e-07"
iyz="-5.7181e-08"
izz="4.903e-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="rh_thumb_cmc_roll"
type="revolute">
<origin
xyz="0.0078133 0.030812 0.025678"
rpy="-0.0349069999759288 6.51804876738993e-09 0.00738597563689764" />
<parent
link="rh_hand_base_link" />
<child
link="rh_thumb_metacarpals_base1" />
<axis
xyz="0 0.034899 -0.99939" />
<limit
lower="0"
upper="1.12177324"
effort="0.588"
velocity="5.95" />
<dynamics damping="0.02" friction="0.05416667"/>
</joint>
<link
name="rh_thumb_metacarpals">
<inertial>
<origin
xyz="0.0076927091 -0.0859662185 0.0235283087"
rpy="0 0 0" />
<mass
value="0.065" />
<inertia
ixx="8.407e-06"
ixy="1.1338e-09"
ixz="1.1136e-08"
iyy="8.3899e-06"
iyz="-3.1171e-07"
izz="2.0091e-06" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="rh_thumb_cmc_pitch"
type="revolute">
<origin
xyz="0.0054997 0.013314 0.0032166"
rpy="-0.98568644 0.0136370000000001 -0.40121" />
<parent
link="rh_thumb_metacarpals_base1" />
<child
link="rh_thumb_metacarpals" />
<axis
xyz="1 0 0" />
<limit
lower="0"
upper="0.65673088"
effort="1.008"
velocity="3.47" />
<dynamics damping="0.02" friction="0.0375"/>
</joint>
<link
name="rh_thumb_distal">
<inertial>
<origin
xyz="0.0076927091 -0.0798662185 -0.0591716913"
rpy="0 0 0" />
<mass
value="0.018" />
<inertia
ixx="1.5546e-06"
ixy="-2.4423e-09"
ixz="2.8147e-09"
iyy="1.7108e-06"
iyz="7.2756e-08"
izz="9.1709e-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<link
name="rh_thumb_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="rh_thumb_tip_fixed"
type="fixed">
<origin
xyz="0 -0.0035 0.026"
rpy="0 0 -1.5708" />
<parent
link="rh_thumb_distal" />
<child
link="rh_thumb_tip" />
</joint>
<joint
name="rh_thumb_dip"
type="revolute">
<origin
xyz="0 -0.0061 0.0827"
rpy="0 0 0" />
<parent
link="rh_thumb_metacarpals" />
<child
link="rh_thumb_distal" />
<axis
xyz="1 0 0" />
<limit
lower="0"
upper="0.963287"
effort="0"
velocity="1.7" />
<mimic
joint="rh_thumb_cmc_pitch"
multiplier="0.88524192477979"
offset="0" />
</joint>
<link
name="rh_index_proximal">
<inertial>
<origin
xyz="0.0017760000 0.0001100000 0.0125560000"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="7.97543e-07"
ixy="1.275e-09"
ixz="1.38601e-07"
iyy="8.00499e-07"
iyz="-9.39e-10"
izz="3.57001e-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="rh_index_mcp_pitch"
type="revolute">
<origin
xyz="-0.00066821 0.033467 0.11622"
rpy="-0.087266 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_index_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.23208487"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.04"/>
</joint>
<link
name="rh_index_distal">
<inertial>
<origin
xyz="0.0134000000 -0.0022620000 0.0169920000"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="2.488712e-06"
ixy="-8.249e-09"
ixz="-1.523059e-06"
iyy="3.723754e-06"
iyz="-1.4019e-08"
izz="1.813582e-06" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<link
name="rh_index_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="rh_index_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="rh_index_distal" />
<child
link="rh_index_tip" />
</joint>
<joint
name="rh_index_dip"
type="revolute">
<origin
xyz="0.0009359 0 0.028002"
rpy="0 0 0" />
<parent
link="rh_index_proximal" />
<child
link="rh_index_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="rh_index_mcp_pitch"
multiplier="0.923280528556446"
offset="0" />
</joint>
<link
name="rh_middle_proximal">
<inertial>
<origin
xyz="0.0017760000 0.0001100000 0.0125560000"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="7.97543e-07"
ixy="1.275e-09"
ixz="1.38601e-07"
iyy="8.00499e-07"
iyz="-9.39e-10"
izz="3.57001e-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="rh_middle_mcp_pitch"
type="revolute">
<origin
xyz="-0.00066821 0.0134013 0.12272"
rpy="-0.034907 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_middle_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.23208487"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.02708333"/>
</joint>
<link
name="rh_middle_distal">
<inertial>
<origin
xyz="0.0134000000 -0.0022620000 0.0169920000"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="2.488712e-06"
ixy="-8.249e-09"
ixz="-1.523059e-06"
iyy="3.723754e-06"
iyz="-1.4019e-08"
izz="1.813582e-06" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<link
name="rh_middle_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="rh_middle_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="rh_middle_distal" />
<child
link="rh_middle_tip" />
</joint>
<joint
name="rh_middle_dip"
type="revolute">
<origin
xyz="0.0009359 0 0.028002"
rpy="0 0 0" />
<parent
link="rh_middle_proximal" />
<child
link="rh_middle_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="rh_middle_mcp_pitch"
multiplier="0.923280528556446"
offset="0" />
</joint>
<link
name="rh_ring_proximal">
<inertial>
<origin
xyz="0.0017760000 0.0001100000 0.0125560000"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="7.97543e-07"
ixy="1.275e-09"
ixz="1.38601e-07"
iyy="8.00499e-07"
iyz="-9.39e-10"
izz="3.57001e-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="rh_ring_mcp_pitch"
type="revolute">
<origin
xyz="-0.00067368 -0.006217 0.11872"
rpy="0.017453 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_ring_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.23208487"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.02291667"/>
</joint>
<link
name="rh_ring_distal">
<inertial>
<origin
xyz="0.0134000000 -0.0022620000 0.0169920000"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="2.488712e-06"
ixy="-8.249e-09"
ixz="-1.523059e-06"
iyy="3.723754e-06"
iyz="-1.4019e-08"
izz="1.813582e-06" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<link
name="rh_ring_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="rh_ring_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="rh_ring_distal" />
<child
link="rh_ring_tip" />
</joint>
<joint
name="rh_ring_dip"
type="revolute">
<origin
xyz="0.0009359 0 0.028002"
rpy="0 0 0" />
<parent
link="rh_ring_proximal" />
<child
link="rh_ring_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="rh_ring_mcp_pitch"
multiplier="0.923280528556446"
offset="0" />
</joint>
<link
name="rh_pinky_proximal">
<inertial>
<origin
xyz="0.0017760000 0.0001100000 0.0125560000"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="7.97543e-07"
ixy="1.275e-09"
ixz="1.38601e-07"
iyy="8.00499e-07"
iyz="-9.39e-10"
izz="3.57001e-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="rh_pinky_mcp_pitch"
type="revolute">
<origin
xyz="-0.00067852 -0.025314 0.11474"
rpy="0.05236 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_pinky_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.23208487"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.01"/>
</joint>
<link
name="rh_pinky_distal">
<inertial>
<origin
xyz="0.0134000000 -0.0022620000 0.0169920000"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="2.488712e-06"
ixy="-8.249e-09"
ixz="-1.523059e-06"
iyy="3.723754e-06"
iyz="-1.4019e-08"
izz="1.813582e-06" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<link
name="rh_pinky_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="rh_pinky_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="rh_pinky_distal" />
<child
link="rh_pinky_tip" />
</joint>
<joint
name="rh_pinky_dip"
type="revolute">
<origin
xyz="0.0009359 0 0.028002"
rpy="0 0 0" />
<parent
link="rh_pinky_proximal" />
<child
link="rh_pinky_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="rh_pinky_mcp_pitch"
multiplier="0.923280528556446"
offset="0" />
</joint>
<transmission name="trans_thumb_cmc_roll">
<type>transmission_interface/SimpleTransmission</type>
<joint name="rh_thumb_cmc_roll">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_thumb_cmc_roll">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0084</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_index_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="rh_index_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_index_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_middle_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="rh_middle_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_middle_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_ring_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="rh_ring_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_ring_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_pinky_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="rh_pinky_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_pinky_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_thumb_cmc_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="rh_thumb_cmc_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_thumb_cmc_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0144</mechanicalReduction>
</actuator>
</transmission>
<mujoco>
<compiler meshdir="meshes/" balanceinertia="true" discardvisual="false"/>
<default>
<joint armature="3.4707e-08" damping="5.0" frictionloss="0.5"/>
</default>
<equality>
<joint name="rh_couple_index" joint1="rh_index_dip" joint2="rh_index_mcp_pitch" polycoef="0 1.17646620703189 -0.184902818001199 0 0 0"/>
<joint name="rh_couple_middle" joint1="rh_middle_dip" joint2="rh_middle_mcp_pitch" polycoef="0 1.17646620703189 -0.184902818001199 0 0 0"/>
<joint name="rh_couple_ring" joint1="rh_ring_dip" joint2="rh_ring_mcp_pitch" polycoef="0 1.17646620703189 -0.184902818001199 0 0 0"/>
<joint name="rh_couple_pinky" joint1="rh_pinky_dip" joint2="rh_pinky_mcp_pitch" polycoef="0 1.17646620703189 -0.184902818001199 0 0 0"/>
<joint name="rh_couple_thumb" joint1="rh_thumb_dip" joint2="rh_thumb_cmc_pitch" polycoef="0 0.88524192477979 0 0 0 0"/>
</equality>
</mujoco>
<gazebo reference="rh_hand_base_link">
<mu1>1.5</mu1>
<mu2>1.5</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="rh_thumb_distal">
<mu1>1.5</mu1>
<mu2>1.5</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="rh_index_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="rh_middle_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="rh_ring_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="rh_pinky_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
</robot>
+953
View File
@@ -0,0 +1,953 @@
<?xml version="1.0" encoding="utf-8"?>
<robot
name="linkerhand_l6v3.1_right">
<!-- 手掌基座,作为整只手的根链接 -->
<link
name="rh_hand_base_link">
<inertial>
<origin
xyz="0.0019900000 0.0009100000 0.0583810000"
rpy="0 0 0" />
<mass
value="0.4185" />
<inertia
ixx="0.0004998"
ixy="-1.2853e-05"
ixz="-8.317e-06"
iyy="0.0003567"
iyz="-1.0354e-05"
izz="0.0002301" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/hand_base_link.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="rh_thumb_metacarpals_base1">
<inertial>
<origin
xyz="0.0018663000 0.0066406000 0.0015330000"
rpy="0 0 0" />
<mass
value="0.01" />
<inertia
ixx="3.3724e-07"
ixy="-1.11e-07"
ixz="-3.6039e-08"
iyy="2.9457e-07"
iyz="-5.7181e-08"
izz="4.903e-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals_base1.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="rh_thumb_cmc_roll"
type="revolute">
<origin
xyz="0.0078133 0.030812 0.025678"
rpy="-0.034907 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_thumb_metacarpals_base1" />
<axis
xyz="0 0.034899 -0.99939" />
<limit
lower="-0.087266"
upper="1.256637"
effort="0.588"
velocity="5.95" />
<dynamics damping="0.02" friction="0.05416667"/>
</joint>
<link
name="rh_thumb_metacarpals">
<inertial>
<origin
xyz="0.0076927091 -0.0859662185 0.0235283087"
rpy="0 0 0" />
<mass
value="0.065" />
<inertia
ixx="8.407e-06"
ixy="1.1338e-09"
ixz="1.1136e-08"
iyy="8.3899e-06"
iyz="-3.1171e-07"
izz="2.0091e-06" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="rh_thumb_cmc_pitch"
type="revolute">
<origin
xyz="0.0054997 0.013314 0.0032166"
rpy="-1.0762 0.013637 -0.40121" />
<parent
link="rh_thumb_metacarpals_base1" />
<child
link="rh_thumb_metacarpals" />
<axis
xyz="1 0 0" />
<limit
lower="0"
upper="0.837758"
effort="1.008"
velocity="3.47" />
<dynamics damping="0.02" friction="0.0375"/>
</joint>
<link
name="rh_thumb_distal">
<inertial>
<origin
xyz="0.0076927091 -0.0798662185 -0.0591716913"
rpy="0 0 0" />
<mass
value="0.018" />
<inertia
ixx="1.5546e-06"
ixy="-2.4423e-09"
ixz="2.8147e-09"
iyy="1.7108e-06"
iyz="7.2756e-08"
izz="9.1709e-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<link
name="rh_thumb_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="rh_thumb_tip_fixed"
type="fixed">
<origin
xyz="0 -0.0035 0.026"
rpy="0 0 -1.5708" />
<parent
link="rh_thumb_distal" />
<child
link="rh_thumb_tip" />
</joint>
<joint
name="rh_thumb_dip"
type="revolute">
<origin
xyz="0 -0.0061 0.0827"
rpy="0 0 0" />
<parent
link="rh_thumb_metacarpals" />
<child
link="rh_thumb_distal" />
<axis
xyz="1 0 0" />
<limit
lower="0"
upper="0.963287"
effort="0"
velocity="1.7" />
<mimic
joint="rh_thumb_cmc_pitch"
multiplier="1.226495"
offset="0" />
</joint>
<link
name="rh_index_proximal">
<inertial>
<origin
xyz="0.0017760000 0.0001100000 0.0125560000"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="7.97543e-07"
ixy="1.275e-09"
ixz="1.38601e-07"
iyy="8.00499e-07"
iyz="-9.39e-10"
izz="3.57001e-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="rh_index_mcp_pitch"
type="revolute">
<origin
xyz="-0.00066821 0.033467 0.11622"
rpy="-0.087266 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_index_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.134464"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.04"/>
</joint>
<link
name="rh_index_distal">
<inertial>
<origin
xyz="0.0134000000 -0.0022620000 0.0169920000"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="2.488712e-06"
ixy="-8.249e-09"
ixz="-1.523059e-06"
iyy="3.723754e-06"
iyz="-1.4019e-08"
izz="1.813582e-06" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<link
name="rh_index_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="rh_index_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="rh_index_distal" />
<child
link="rh_index_tip" />
</joint>
<joint
name="rh_index_dip"
type="revolute">
<origin
xyz="0.0009359 0 0.028002"
rpy="0 0 0" />
<parent
link="rh_index_proximal" />
<child
link="rh_index_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="rh_index_mcp_pitch"
multiplier="1.125676"
offset="0" />
</joint>
<link
name="rh_middle_proximal">
<inertial>
<origin
xyz="0.0017760000 0.0001100000 0.0125560000"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="7.97543e-07"
ixy="1.275e-09"
ixz="1.38601e-07"
iyy="8.00499e-07"
iyz="-9.39e-10"
izz="3.57001e-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="rh_middle_mcp_pitch"
type="revolute">
<origin
xyz="-0.00066821 0.0134013 0.12272"
rpy="-0.034907 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_middle_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.134464"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.02708333"/>
</joint>
<link
name="rh_middle_distal">
<inertial>
<origin
xyz="0.0134000000 -0.0022620000 0.0169920000"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="2.488712e-06"
ixy="-8.249e-09"
ixz="-1.523059e-06"
iyy="3.723754e-06"
iyz="-1.4019e-08"
izz="1.813582e-06" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<link
name="rh_middle_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="rh_middle_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="rh_middle_distal" />
<child
link="rh_middle_tip" />
</joint>
<joint
name="rh_middle_dip"
type="revolute">
<origin
xyz="0.0009359 0 0.028002"
rpy="0 0 0" />
<parent
link="rh_middle_proximal" />
<child
link="rh_middle_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="rh_middle_mcp_pitch"
multiplier="1.125676"
offset="0" />
</joint>
<link
name="rh_ring_proximal">
<inertial>
<origin
xyz="0.0017760000 0.0001100000 0.0125560000"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="7.97543e-07"
ixy="1.275e-09"
ixz="1.38601e-07"
iyy="8.00499e-07"
iyz="-9.39e-10"
izz="3.57001e-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="rh_ring_mcp_pitch"
type="revolute">
<origin
xyz="-0.00067368 -0.006217 0.11872"
rpy="0.017453 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_ring_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.134464"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.02291667"/>
</joint>
<link
name="rh_ring_distal">
<inertial>
<origin
xyz="0.0134000000 -0.0022620000 0.0169920000"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="2.488712e-06"
ixy="-8.249e-09"
ixz="-1.523059e-06"
iyy="3.723754e-06"
iyz="-1.4019e-08"
izz="1.813582e-06" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<link
name="rh_ring_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="rh_ring_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="rh_ring_distal" />
<child
link="rh_ring_tip" />
</joint>
<joint
name="rh_ring_dip"
type="revolute">
<origin
xyz="0.0009359 0 0.028002"
rpy="0 0 0" />
<parent
link="rh_ring_proximal" />
<child
link="rh_ring_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="rh_ring_mcp_pitch"
multiplier="1.125676"
offset="0" />
</joint>
<link
name="rh_pinky_proximal">
<inertial>
<origin
xyz="0.0017760000 0.0001100000 0.0125560000"
rpy="0 0 0" />
<mass
value="0.008553" />
<inertia
ixx="7.97543e-07"
ixy="1.275e-09"
ixz="1.38601e-07"
iyy="8.00499e-07"
iyz="-9.39e-10"
izz="3.57001e-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_proximal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 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="rh_pinky_mcp_pitch"
type="revolute">
<origin
xyz="-0.00067852 -0.025314 0.11474"
rpy="0.05236 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_pinky_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.134464"
effort="0.574"
velocity="6.10" />
<dynamics damping="0.01" friction="0.01"/>
</joint>
<link
name="rh_pinky_distal">
<inertial>
<origin
xyz="0.0134000000 -0.0022620000 0.0169920000"
rpy="0 0 0" />
<mass
value="0.018408" />
<inertia
ixx="2.488712e-06"
ixy="-8.249e-09"
ixz="-1.523059e-06"
iyy="3.723754e-06"
iyz="-1.4019e-08"
izz="1.813582e-06" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
<material
name="">
<color
rgba="0.796078431372549 0.823529411764706 0.937254901960784 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<link
name="rh_pinky_tip">
<inertial>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<mass
value="1e-06" />
<inertia
ixx="1e-09"
ixy="0"
ixz="0"
iyy="1e-09"
iyz="0"
izz="1e-09" />
</inertial>
</link>
<joint
name="rh_pinky_tip_fixed"
type="fixed">
<origin
xyz="0.0341 0 0.034"
rpy="0 0 0" />
<parent
link="rh_pinky_distal" />
<child
link="rh_pinky_tip" />
</joint>
<joint
name="rh_pinky_dip"
type="revolute">
<origin
xyz="0.0009359 0 0.028002"
rpy="0 0 0" />
<parent
link="rh_pinky_proximal" />
<child
link="rh_pinky_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.277039"
effort="0"
velocity="3.1" />
<mimic
joint="rh_pinky_mcp_pitch"
multiplier="1.125676"
offset="0" />
</joint>
<transmission name="trans_thumb_cmc_roll">
<type>transmission_interface/SimpleTransmission</type>
<joint name="rh_thumb_cmc_roll">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_thumb_cmc_roll">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0084</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_index_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="rh_index_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_index_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_middle_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="rh_middle_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_middle_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_ring_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="rh_ring_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_ring_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_pinky_mcp_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="rh_pinky_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_pinky_mcp_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0082</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_thumb_cmc_pitch">
<type>transmission_interface/SimpleTransmission</type>
<joint name="rh_thumb_cmc_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor_thumb_cmc_pitch">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>0.0144</mechanicalReduction>
</actuator>
</transmission>
<mujoco>
<compiler meshdir="meshes/" balanceinertia="true" discardvisual="false"/>
<default>
<joint armature="3.4707e-08" damping="5.0" frictionloss="0.5"/>
</default>
<equality>
<joint name="rh_couple_index" joint1="rh_index_dip" joint2="rh_index_mcp_pitch" polycoef="0 1.125676 0 0 0 0"/>
<joint name="rh_couple_middle" joint1="rh_middle_dip" joint2="rh_middle_mcp_pitch" polycoef="0 1.125676 0 0 0 0"/>
<joint name="rh_couple_ring" joint1="rh_ring_dip" joint2="rh_ring_mcp_pitch" polycoef="0 1.125676 0 0 0 0"/>
<joint name="rh_couple_pinky" joint1="rh_pinky_dip" joint2="rh_pinky_mcp_pitch" polycoef="0 1.125676 0 0 0 0"/>
<joint name="rh_couple_thumb" joint1="rh_thumb_dip" joint2="rh_thumb_cmc_pitch" polycoef="0 1.226495 0 0 0 0"/>
</equality>
</mujoco>
<gazebo reference="rh_hand_base_link">
<mu1>1.5</mu1>
<mu2>1.5</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="rh_thumb_distal">
<mu1>1.5</mu1>
<mu2>1.5</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="rh_index_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="rh_middle_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="rh_ring_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
<gazebo reference="rh_pinky_distal">
<mu1>1.1</mu1>
<mu2>1.1</mu2>
<selfCollide>true</selfCollide>
</gazebo>
</robot>
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_l6_left_mujoco_plan.sh" "$@"
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_l6_right_mujoco_plan.sh" "$@"
+216
View File
@@ -0,0 +1,216 @@
#!/usr/bin/env bash
# 同时开两个 MuJoCo:L6 v3.1 左手标定 vs 未标定 URDF
#
# 未标定: /home/lxqs/linkerhand-urdf/l6/left
# 标定: /home/lxqs/Linkerbot_URDF/L6/LEFT(直接用 linker_hand_l6v31_left.xml,与单开相同)
#
# 两个窗口共用同一条 6 维 SDK 控制话题(/cb_left_hand_control_cmd)。
#
# 用法:
# ./hands/L6/scripts/run_l6_left_dual_compare.sh
# ./hands/L6/scripts/run_l6_left_dual_compare.sh --pd-drive
# ./hands/L6/scripts/run_l6_left_dual_compare.sh --cmd-topic /cb_left_hand_control_cmd --speed 1.0
set -eo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
cd "$ROOT"
URDF_UNCALIBRATED="$ROOT/hands/L6/reference/left_uncalibrated.urdf"
URDF_CALIBRATED="$ROOT/hands/L6/reference/left_calibrated.urdf"
PKG_L6="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/L6/linker_hand_l6v31_left"
CALIB_XML="$PKG_L6/linker_hand_l6v31_left.xml"
STOCK_XML="$PKG_L6/linker_hand_l6v31_left_stock.xml"
if [[ ! -f "$URDF_UNCALIBRATED" ]]; then
echo "[error] missing uncalibrated URDF: $URDF_UNCALIBRATED" >&2
exit 1
fi
if [[ ! -f "$URDF_CALIBRATED" ]]; then
echo "[error] missing calibrated URDF: $URDF_CALIBRATED" >&2
exit 1
fi
if [[ ! -f "$CALIB_XML" ]]; then
echo "[error] missing calibrated MJCF: $CALIB_XML" >&2
exit 1
fi
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}"
echo "[l6-left-dual] calibrated xml=$CALIB_XML (same as single-run)"
echo "[l6-left-dual] build uncalibrated MJCF from $URDF_UNCALIBRATED"
python3 - "$CALIB_XML" "$URDF_UNCALIBRATED" "$STOCK_XML" <<'PY'
import math
import re
import sys
from pathlib import Path
template_xml = Path(sys.argv[1])
urdf_uncal = Path(sys.argv[2])
out_uncal = Path(sys.argv[3])
joint_body = {
"lh_thumb_cmc_roll": "lh_thumb_metacarpals_base1",
"lh_thumb_cmc_pitch": "lh_thumb_metacarpals",
}
MESHDIR_UNCAL = "/home/lxqs/linkerhand-urdf/l6/left/meshes"
def parse_urdf_limits(path: Path) -> dict[str, dict]:
text = path.read_text()
joints: dict[str, dict] = {}
for m in re.finditer(r"<joint\b([^>]*)>(.*?)</joint>", text, re.S):
attrs, body = m.group(1), m.group(2)
name_m = re.search(r'name="([^"]+)"', attrs)
if not name_m:
continue
name = name_m.group(1)
jtype_m = re.search(r'type="([^"]+)"', attrs)
if not jtype_m or jtype_m.group(1) != "revolute":
continue
origin = re.search(r'<origin[^>]*xyz="([^"]+)"[^>]*rpy="([^"]+)"', body)
lim = re.search(r'lower="([^"]+)"[^>]*upper="([^"]+)"', body)
rpy = [float(x) for x in origin.group(2).split()] if origin else [0.0, 0.0, 0.0]
joints[name] = {
"lo": float(lim.group(1)),
"hi": float(lim.group(2)),
"rpy": rpy,
}
return joints
def rpy_to_quat(r: float, p: float, y: float) -> tuple[float, float, float, float]:
cr, sr = math.cos(r * 0.5), math.sin(r * 0.5)
cp, sp = math.cos(p * 0.5), math.sin(p * 0.5)
cy, sy = math.cos(y * 0.5), math.sin(y * 0.5)
w = cr * cp * cy + sr * sp * sy
x = sr * cp * cy - cr * sp * sy
yq = cr * sp * cy + sr * cp * sy
z = cr * cp * sy - sr * sp * cy
return w, x, yq, z
def apply_urdf_to_mjcf(template: str, urdf_path: Path, model_name: str) -> str:
joints = parse_urdf_limits(urdf_path)
xml = template.replace(
'model="linkerhand_l6v31_left"',
f'model="{model_name}"',
1,
)
xml = re.sub(
r'(<compiler[^>]*meshdir=")[^"]+(")',
rf"\1{MESHDIR_UNCAL}\2",
xml,
count=1,
)
for jname, info in joints.items():
lo, hi = info["lo"], info["hi"]
pat = rf'(<joint name="{re.escape(jname)}"[^>]*\srange=")([^"]+)(")'
xml = re.sub(
pat,
lambda m, lo=lo, hi=hi: f"{m.group(1)}{lo} {hi}{m.group(3)}",
xml,
count=1,
)
r, p, y = info["rpy"]
bname = joint_body.get(jname)
if bname and abs(r) + abs(p) + abs(y) > 1e-9:
w, x, yq, z = rpy_to_quat(r, p, y)
quat = f"{w:.9f} {x:.9f} {yq:.9f} {z:.9f}"
bpat = rf'(<body name="{re.escape(bname)}"[^>]*)(>)'
def add_quat(m, quat=quat):
tag = m.group(1)
if 'quat="' in tag:
tag = re.sub(r'quat="[^"]+"', f'quat="{quat}"', tag, count=1)
else:
tag = tag + f' quat="{quat}"'
return tag + m.group(2)
xml = re.sub(bpat, add_quat, xml, count=1)
elif bname:
bpat = rf'(<body name="{re.escape(bname)}"[^>]*)\squat="[^"]+"([^>]*>)'
xml = re.sub(bpat, r"\1\2", xml, count=1)
act_map = {
"lh_thumb_cmc_roll_pos": "lh_thumb_cmc_roll",
"lh_thumb_cmc_pitch_pos": "lh_thumb_cmc_pitch",
"lh_index_mcp_pitch_pos": "lh_index_mcp_pitch",
"lh_middle_mcp_pitch_pos": "lh_middle_mcp_pitch",
"lh_ring_mcp_pitch_pos": "lh_ring_mcp_pitch",
"lh_pinky_mcp_pitch_pos": "lh_pinky_mcp_pitch",
}
for act, jname in act_map.items():
lo, hi = joints[jname]["lo"], joints[jname]["hi"]
pat = rf'(<position name="{act}"[^>]*\sctrlrange=")([^"]+)(")'
xml = re.sub(
pat,
lambda m, lo=lo, hi=hi: f"{m.group(1)}{lo} {hi}{m.group(3)}",
xml,
count=1,
)
return xml
import mujoco
out_uncal.write_text(
apply_urdf_to_mjcf(template_xml.read_text(), urdf_uncal, "linkerhand_l6v31_left_uncalibrated")
)
mujoco.MjModel.from_xml_path(str(out_uncal))
print(f"[l6-left-dual] uncalibrated URDF: {urdf_uncal}")
print(f"[l6-left-dual] uncalibrated meshes: {MESHDIR_UNCAL}")
print(f"[l6-left-dual] uncalibrated MJCF -> {out_uncal}")
PY
echo "[l6-left-dual] calibrated URDF: $URDF_CALIBRATED"
echo "[l6-left-dual] calibrated meshes: $PKG_L6/meshes (package, 标定安装)"
EXTRA=("$@")
cleanup() {
[[ -n "${PID_UNCAL:-}" ]] && kill "$PID_UNCAL" 2>/dev/null || true
[[ -n "${PID_CALIB:-}" ]] && kill "$PID_CALIB" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
echo "[l6-left-dual] launch uncalibrated window"
"$ROOT/hands/L6/scripts/run_l6_left_mujoco_plan.sh" \
--xml "$STOCK_XML" \
--mapping-urdf "$URDF_UNCALIBRATED" \
--node-name l6_left_uncalibrated \
--window-title "L6 left 未标定 (linkerhand-urdf)" \
--cmd-topic /cb_left_hand_control_cmd \
--sim-state-topic /sim/mujoco/l6/left/uncalibrated/joint_state \
"${EXTRA[@]}" &
PID_UNCAL=$!
sleep 1
echo "[l6-left-dual] launch calibrated window"
"$ROOT/hands/L6/scripts/run_l6_left_mujoco_plan.sh" \
--xml "$CALIB_XML" \
--mapping-urdf "$URDF_CALIBRATED" \
--node-name l6_left_calibrated \
--window-title "L6 left 标定 (Linkerbot_URDF)" \
--cmd-topic /cb_left_hand_control_cmd \
--sim-state-topic /sim/mujoco/l6/left/calibrated/joint_state \
"${EXTRA[@]}" &
PID_CALIB=$!
echo "[l6-left-dual] both started. cmd topic: /cb_left_hand_control_cmd"
echo "[l6-left-dual] Ctrl+C stops both."
wait "$PID_UNCAL" "$PID_CALIB"
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# MuJoCo L6 v3.1 左手仿真桥
# URDF 来源: /home/lxqs/Linkerbot_URDF/L6/LEFT/linkerhand_l6v3.1_left_transferred_from_L6_RIGHT_001.urdf
# 默认订阅 /l6/cb_left_hand_control_cmd(6 维 SDK 0~255)
# 发布 /sim/mujoco/l6/left/joint_state
#
# 用法:
# ./hands/L6/scripts/run_l6_left_mujoco_plan.sh
# ./hands/L6/scripts/run_l6_left_mujoco_plan.sh --pd-drive
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}"
XML="$PKG_SRC/linker_hand_mujoco_ros2/urdf/L6/linker_hand_l6v31_left/linker_hand_l6v31_left.xml"
URDF="$PKG_SRC/linker_hand_mujoco_ros2/urdf/L6/linker_hand_l6v31_left/linkerhand_l6_left.urdf"
if [[ ! -f "$XML" ]]; then
echo "[error] missing L6 left MJCF: $XML" >&2
exit 1
fi
echo "[l6-left] urdf=$URDF"
echo "[l6-left] xml=$XML"
echo "[l6-left] cmd-topic=/l6/cb_left_hand_control_cmd"
echo "[l6-left] sim-state=/sim/mujoco/l6/left/joint_state"
echo "[l6-left] SDK 6ch: pitch, roll, index, middle, ring, pinky"
exec python3 -m linker_hand_mujoco_ros2.l6_left_plan_bridge \
--xml "$XML" \
--node-name l6_mujoco_plan_bridge_left \
--window-title "L6 v3.1 left" \
--cmd-topic /l6/cb_left_hand_control_cmd \
--sim-state-topic /sim/mujoco/l6/left/joint_state \
--nominal-topic /retarget/l6/left/joint_target_nominal \
--mapping g20_urdf \
"$@"
+216
View File
@@ -0,0 +1,216 @@
#!/usr/bin/env bash
# 同时开两个 MuJoCo:L6 v3.1 右手标定 vs 未标定 URDF
#
# 未标定: /home/lxqs/linkerhand-urdf/l6/right
# 标定: /home/lxqs/Linkerbot_URDF/L6/RIGHT(直接用 linker_hand_l6v31_right.xml,与单开相同)
#
# 两个窗口共用同一条 6 维 SDK 控制话题(/cb_right_hand_control_cmd)。
#
# 用法:
# ./hands/L6/scripts/run_l6_right_dual_compare.sh
# ./hands/L6/scripts/run_l6_right_dual_compare.sh --pd-drive
# ./hands/L6/scripts/run_l6_right_dual_compare.sh --cmd-topic /cb_right_hand_control_cmd --speed 1.0
set -eo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
cd "$ROOT"
URDF_UNCALIBRATED="$ROOT/hands/L6/reference/right_uncalibrated.urdf"
URDF_CALIBRATED="$ROOT/hands/L6/reference/right_calibrated.urdf"
PKG_L6="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/L6/linker_hand_l6v31_right"
CALIB_XML="$PKG_L6/linker_hand_l6v31_right.xml"
STOCK_XML="$PKG_L6/linker_hand_l6v31_right_stock.xml"
if [[ ! -f "$URDF_UNCALIBRATED" ]]; then
echo "[error] missing uncalibrated URDF: $URDF_UNCALIBRATED" >&2
exit 1
fi
if [[ ! -f "$URDF_CALIBRATED" ]]; then
echo "[error] missing calibrated URDF: $URDF_CALIBRATED" >&2
exit 1
fi
if [[ ! -f "$CALIB_XML" ]]; then
echo "[error] missing calibrated MJCF: $CALIB_XML" >&2
exit 1
fi
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}"
echo "[l6-right-dual] calibrated xml=$CALIB_XML (same as single-run)"
echo "[l6-right-dual] build uncalibrated MJCF from $URDF_UNCALIBRATED"
python3 - "$CALIB_XML" "$URDF_UNCALIBRATED" "$STOCK_XML" <<'PY'
import math
import re
import sys
from pathlib import Path
template_xml = Path(sys.argv[1])
urdf_uncal = Path(sys.argv[2])
out_uncal = Path(sys.argv[3])
joint_body = {
"rh_thumb_cmc_roll": "rh_thumb_metacarpals_base1",
"rh_thumb_cmc_pitch": "rh_thumb_metacarpals",
}
MESHDIR_UNCAL = "/home/lxqs/linkerhand-urdf/l6/right/meshes"
def parse_urdf_limits(path: Path) -> dict[str, dict]:
text = path.read_text()
joints: dict[str, dict] = {}
for m in re.finditer(r"<joint\b([^>]*)>(.*?)</joint>", text, re.S):
attrs, body = m.group(1), m.group(2)
name_m = re.search(r'name="([^"]+)"', attrs)
if not name_m:
continue
name = name_m.group(1)
jtype_m = re.search(r'type="([^"]+)"', attrs)
if not jtype_m or jtype_m.group(1) != "revolute":
continue
origin = re.search(r'<origin[^>]*xyz="([^"]+)"[^>]*rpy="([^"]+)"', body)
lim = re.search(r'lower="([^"]+)"[^>]*upper="([^"]+)"', body)
rpy = [float(x) for x in origin.group(2).split()] if origin else [0.0, 0.0, 0.0]
joints[name] = {
"lo": float(lim.group(1)),
"hi": float(lim.group(2)),
"rpy": rpy,
}
return joints
def rpy_to_quat(r: float, p: float, y: float) -> tuple[float, float, float, float]:
cr, sr = math.cos(r * 0.5), math.sin(r * 0.5)
cp, sp = math.cos(p * 0.5), math.sin(p * 0.5)
cy, sy = math.cos(y * 0.5), math.sin(y * 0.5)
w = cr * cp * cy + sr * sp * sy
x = sr * cp * cy - cr * sp * sy
yq = cr * sp * cy + sr * cp * sy
z = cr * cp * sy - sr * sp * cy
return w, x, yq, z
def apply_urdf_to_mjcf(template: str, urdf_path: Path, model_name: str) -> str:
joints = parse_urdf_limits(urdf_path)
xml = template.replace(
'model="linkerhand_l6v31_right"',
f'model="{model_name}"',
1,
)
xml = re.sub(
r'(<compiler[^>]*meshdir=")[^"]+(")',
rf"\1{MESHDIR_UNCAL}\2",
xml,
count=1,
)
for jname, info in joints.items():
lo, hi = info["lo"], info["hi"]
pat = rf'(<joint name="{re.escape(jname)}"[^>]*\srange=")([^"]+)(")'
xml = re.sub(
pat,
lambda m, lo=lo, hi=hi: f"{m.group(1)}{lo} {hi}{m.group(3)}",
xml,
count=1,
)
r, p, y = info["rpy"]
bname = joint_body.get(jname)
if bname and abs(r) + abs(p) + abs(y) > 1e-9:
w, x, yq, z = rpy_to_quat(r, p, y)
quat = f"{w:.9f} {x:.9f} {yq:.9f} {z:.9f}"
bpat = rf'(<body name="{re.escape(bname)}"[^>]*)(>)'
def add_quat(m, quat=quat):
tag = m.group(1)
if 'quat="' in tag:
tag = re.sub(r'quat="[^"]+"', f'quat="{quat}"', tag, count=1)
else:
tag = tag + f' quat="{quat}"'
return tag + m.group(2)
xml = re.sub(bpat, add_quat, xml, count=1)
elif bname:
bpat = rf'(<body name="{re.escape(bname)}"[^>]*)\squat="[^"]+"([^>]*>)'
xml = re.sub(bpat, r"\1\2", xml, count=1)
act_map = {
"rh_thumb_cmc_roll_pos": "rh_thumb_cmc_roll",
"rh_thumb_cmc_pitch_pos": "rh_thumb_cmc_pitch",
"rh_index_mcp_pitch_pos": "rh_index_mcp_pitch",
"rh_middle_mcp_pitch_pos": "rh_middle_mcp_pitch",
"rh_ring_mcp_pitch_pos": "rh_ring_mcp_pitch",
"rh_pinky_mcp_pitch_pos": "rh_pinky_mcp_pitch",
}
for act, jname in act_map.items():
lo, hi = joints[jname]["lo"], joints[jname]["hi"]
pat = rf'(<position name="{act}"[^>]*\sctrlrange=")([^"]+)(")'
xml = re.sub(
pat,
lambda m, lo=lo, hi=hi: f"{m.group(1)}{lo} {hi}{m.group(3)}",
xml,
count=1,
)
return xml
import mujoco
out_uncal.write_text(
apply_urdf_to_mjcf(template_xml.read_text(), urdf_uncal, "linkerhand_l6v31_right_uncalibrated")
)
mujoco.MjModel.from_xml_path(str(out_uncal))
print(f"[l6-right-dual] uncalibrated URDF: {urdf_uncal}")
print(f"[l6-right-dual] uncalibrated meshes: {MESHDIR_UNCAL}")
print(f"[l6-right-dual] uncalibrated MJCF -> {out_uncal}")
PY
echo "[l6-right-dual] calibrated URDF: $URDF_CALIBRATED"
echo "[l6-right-dual] calibrated meshes: $PKG_L6/meshes (package, 标定安装)"
EXTRA=("$@")
cleanup() {
[[ -n "${PID_UNCAL:-}" ]] && kill "$PID_UNCAL" 2>/dev/null || true
[[ -n "${PID_CALIB:-}" ]] && kill "$PID_CALIB" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
echo "[l6-right-dual] launch uncalibrated window"
"$ROOT/hands/L6/scripts/run_l6_right_mujoco_plan.sh" \
--xml "$STOCK_XML" \
--mapping-urdf "$URDF_UNCALIBRATED" \
--node-name l6_right_uncalibrated \
--window-title "L6 right 未标定 (linkerhand-urdf)" \
--cmd-topic /cb_right_hand_control_cmd \
--sim-state-topic /sim/mujoco/l6/right/uncalibrated/joint_state \
"${EXTRA[@]}" &
PID_UNCAL=$!
sleep 1
echo "[l6-right-dual] launch calibrated window"
"$ROOT/hands/L6/scripts/run_l6_right_mujoco_plan.sh" \
--xml "$CALIB_XML" \
--mapping-urdf "$URDF_CALIBRATED" \
--node-name l6_right_calibrated \
--window-title "L6 right 标定 (Linkerbot_URDF)" \
--cmd-topic /cb_right_hand_control_cmd \
--sim-state-topic /sim/mujoco/l6/right/calibrated/joint_state \
"${EXTRA[@]}" &
PID_CALIB=$!
echo "[l6-right-dual] both started. cmd topic: /cb_right_hand_control_cmd"
echo "[l6-right-dual] Ctrl+C stops both."
wait "$PID_UNCAL" "$PID_CALIB"
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# MuJoCo L6 v3.1 右手仿真桥
# URDF 来源: /home/lxqs/下载/linkerhand_l6v3.1_right_partial_zero_calibrated_L6_RIGHT_001_20260902_142125.urdf
# 默认订阅 /cb_right_hand_control_cmd(6 维 SDK 0~255)
# 发布 /sim/mujoco/l6/right/joint_state
#
# 用法:
# ./hands/L6/scripts/run_l6_right_mujoco_plan.sh
# ./hands/L6/scripts/run_l6_right_mujoco_plan.sh --pd-drive
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}"
XML="$PKG_SRC/linker_hand_mujoco_ros2/urdf/L6/linker_hand_l6v31_right/linker_hand_l6v31_right.xml"
URDF="$PKG_SRC/linker_hand_mujoco_ros2/urdf/L6/linker_hand_l6v31_right/linkerhand_l6_right.urdf"
if [[ ! -f "$XML" ]]; then
echo "[error] missing L6 MJCF: $XML" >&2
exit 1
fi
echo "[l6-right] urdf=$URDF"
echo "[l6-right] xml=$XML"
echo "[l6-right] cmd-topic=/cb_right_hand_control_cmd"
echo "[l6-right] sim-state=/sim/mujoco/l6/right/joint_state"
echo "[l6-right] SDK 6ch: pitch, roll, index, middle, ring, pinky"
exec python3 -m linker_hand_mujoco_ros2.l6_plan_bridge \
--xml "$XML" \
--node-name l6_mujoco_plan_bridge_right \
--window-title "L6 v3.1 right" \
--cmd-topic /cb_right_hand_control_cmd \
--sim-state-topic /sim/mujoco/l6/right/joint_state \
--nominal-topic /retarget/l6/right/joint_target_nominal \
--mapping g20_urdf \
"$@"
+8
View File
@@ -0,0 +1,8 @@
# O12
[统一入口与映射说明](../README.md)
- `compare_right.sh`
- `run_right.sh`
`assets/` 是包内模型链接;`scripts/` 为内部实现及辅助工具。
+1
View File
@@ -0,0 +1 @@
../../src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/O12
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_o12_right_dual_compare.sh" "$@"
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_o12_right_mujoco_plan.sh" "$@"
+281
View File
@@ -0,0 +1,281 @@
#!/usr/bin/env bash
# 同时开两个 MuJoCo:O12 右手标定 vs 未标定 URDF
#
# 标定: /home/lxqs/Linkerbot_URDF/012/right/linkerhand_o12_right_biaoding.urdf
# 未标定: /home/lxqs/Linkerbot_URDF/012/right/linkerhand_o12_right.urdf
#
# 启动前各自同步 MJCF(限位/轴/body 零位);两窗各自用对应 URDF 的限位+mimic 映射。
# 共用 /o12/right/joint_cmd(12 路 SDK 弧度)。
#
# 用法:
# ./hands/O12/scripts/run_o12_right_dual_compare.sh
# ./hands/O12/scripts/run_o12_right_dual_compare.sh --no-pd-drive
# ./hands/O12/scripts/run_o12_right_dual_compare.sh --pd-drive --slew-rad-s 4
set -eo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
cd "$ROOT"
URDF_DIR="${O12_RIGHT_URDF_DIR:-$ROOT/assets/vendor/Linkerbot_URDF/012/right}"
URDF_CALIBRATED="${O12_RIGHT_URDF_CALIBRATED:-$URDF_DIR/linkerhand_o12_right_biaoding.urdf}"
URDF_UNCALIBRATED="${O12_RIGHT_URDF_UNCALIBRATED:-$URDF_DIR/linkerhand_o12_right.urdf}"
MESHDIR="${O12_RIGHT_MESHDIR:-$URDF_DIR/meshes}"
PKG_SRC="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2"
O12_DIR="$PKG_SRC/urdf/O12/linker_hand_o12_right"
TEMPLATE_XML="$O12_DIR/linker_hand_o12_right.xml"
CALIB_XML="$O12_DIR/linker_hand_o12_right.xml"
STOCK_XML="$O12_DIR/linker_hand_o12_right_stock.xml"
CALIB_URDF_PKG="$O12_DIR/linkerhand_o12_right.urdf"
STOCK_URDF_PKG="$O12_DIR/linkerhand_o12_right_stock.urdf"
if [[ ! -f "$URDF_CALIBRATED" ]]; then
echo "[error] missing calibrated URDF: $URDF_CALIBRATED" >&2
exit 1
fi
if [[ ! -f "$URDF_UNCALIBRATED" ]]; then
echo "[error] missing uncalibrated URDF: $URDF_UNCALIBRATED" >&2
exit 1
fi
if [[ ! -f "$TEMPLATE_XML" ]]; then
echo "[error] missing O12 MJCF template: $TEMPLATE_XML" >&2
exit 1
fi
if [[ ! -d "$MESHDIR" ]]; then
echo "[error] missing meshes: $MESHDIR" >&2
exit 1
fi
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
export PYTHONPATH="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2${PYTHONPATH:+:$PYTHONPATH}"
echo "[o12-right-dual] sync calibrated + uncalibrated MJCF from Linkerbot_URDF"
python3 - "$URDF_CALIBRATED" "$URDF_UNCALIBRATED" "$MESHDIR" "$TEMPLATE_XML" \
"$CALIB_XML" "$STOCK_XML" "$CALIB_URDF_PKG" "$STOCK_URDF_PKG" <<'PY'
import math
import re
import shutil
import sys
from pathlib import Path
import mujoco
import numpy as np
urdf_cal = Path(sys.argv[1])
urdf_unc = Path(sys.argv[2])
meshdir = Path(sys.argv[3])
template_xml = Path(sys.argv[4])
calib_xml = Path(sys.argv[5])
stock_xml = Path(sys.argv[6])
calib_urdf_pkg = Path(sys.argv[7])
stock_urdf_pkg = Path(sys.argv[8])
BODY_FROM_JOINT = {
"thumb_cmc_roll": "thumb_metacarpals_base1",
"thumb_cmc_yaw": "thumb_metacarpals_base2",
"thumb_cmc_pitch": "thumb_metacarpals",
"thumb_mcp": "thumb_proximal",
"thumb_dip": "thumb_distal",
"index_mcp_roll": "index_metacarpals",
"index_mcp_pitch": "index_proximal",
"index_pip": "index_middle",
"index_dip": "index_distal",
"middle_mcp_roll": "middle_metacarpals",
"middle_mcp_pitch": "middle_proximal",
"middle_pip": "middle_middle",
"middle_dip": "middle_distal",
"ring_mcp_pitch": "ring_proximal",
"ring_pip": "ring_middle",
"ring_dip": "ring_distal",
"pinky_mcp_pitch": "pinky_proximal",
"pinky_pip": "pinky_middle",
"pinky_dip": "pinky_distal",
}
def rpy_to_quat(roll, pitch, yaw):
cr, sr = np.cos(roll / 2), np.sin(roll / 2)
cp, sp = np.cos(pitch / 2), np.sin(pitch / 2)
cy, sy = np.cos(yaw / 2), np.sin(yaw / 2)
return np.array(
[
cr * cp * cy + sr * sp * sy,
sr * cp * cy - cr * sp * sy,
cr * sp * cy + sr * cp * sy,
cr * cp * sy - sr * sp * cy,
]
)
def localize_urdf(src: Path, dst: Path) -> None:
text = src.read_text()
text = re.sub(r"package://[^/]+/meshes/", "meshes/", text)
dst.write_text(text)
def parse_urdf(path: Path) -> dict[str, dict]:
text = path.read_text()
joints: dict[str, dict] = {}
for m in re.finditer(r"<joint\b([^>]*)>(.*?)</joint>", text, re.S):
attrs, body = m.group(1), m.group(2)
name_m = re.search(r'name="([^"]+)"', attrs)
if not name_m:
continue
name = name_m.group(1)
if "revolute" not in attrs:
continue
origin = re.search(r'<origin[^>]*xyz="([^"]+)"[^>]*rpy="([^"]+)"', body)
lim = re.search(r'lower="([^"]+)"[^>]*upper="([^"]+)"', body)
axis = re.search(r'<axis[^>]*xyz="([^"]+)"', body)
xyz = [float(x) for x in origin.group(1).split()] if origin else [0.0, 0.0, 0.0]
rpy = [float(x) for x in origin.group(2).split()] if origin else [0.0, 0.0, 0.0]
joints[name] = {
"lo": float(lim.group(1)),
"hi": float(lim.group(2)),
"xyz": xyz,
"rpy": rpy,
"axis": axis.group(1).strip() if axis else "0 0 1",
}
return joints
def apply_urdf_to_mjcf(template: str, joints: dict[str, dict], model_name: str) -> str:
xml = re.sub(
r'model="[^"]+"',
f'model="{model_name}"',
template,
count=1,
)
for jname, info in joints.items():
lo, hi = info["lo"], info["hi"]
ax = info["axis"]
jpat = re.compile(rf'<joint name="{re.escape(jname)}"[^>]*/?>')
m = jpat.search(xml)
if not m:
raise SystemExit(f"missing joint in MJCF: {jname}")
attrs = m.group(0)
# keep damping/frictionloss if present
damp = re.search(r'\bdamping="[^"]+"', attrs)
fric = re.search(r'\bfrictionloss="[^"]+"', attrs)
extra = ""
if damp:
extra += f" {damp.group(0)}"
if fric:
extra += f" {fric.group(0)}"
new = (
f'<joint name="{jname}" pos="0 0 0" axis="{ax}" '
f'range="{lo:g} {hi:g}"{extra}/>'
)
xml = xml[: m.start()] + new + xml[m.end() :]
act_pat = re.compile(
rf'(<position name="{re.escape(jname)}_pos"[^>]*\bctrlrange=")[^"]+(")'
)
if act_pat.search(xml):
xml = act_pat.sub(rf"\g<1>{lo:g} {hi:g}\2", xml, count=1)
for jname, bname in BODY_FROM_JOINT.items():
if jname not in joints:
continue
j = joints[jname]
xyz, rpy = j["xyz"], j["rpy"]
q = rpy_to_quat(*rpy)
pos = f"{xyz[0]:g} {xyz[1]:g} {xyz[2]:g}"
m = re.search(rf'<body name="{re.escape(bname)}"[^>]*>', xml)
if not m:
raise SystemExit(f"missing body in MJCF: {bname}")
if np.allclose(q, [1, 0, 0, 0], atol=1e-9):
new = f'<body name="{bname}" pos="{pos}">'
else:
quat = "{:.8g} {:.8g} {:.8g} {:.8g}".format(*q)
new = f'<body name="{bname}" pos="{pos}" quat="{quat}">'
xml = xml[: m.start()] + new + xml[m.end() :]
return xml
# meshes → 包内(两窗共用)
mesh_out = calib_xml.parent / "meshes"
mesh_out.mkdir(exist_ok=True)
for f in meshdir.glob("*"):
if f.is_file():
shutil.copy2(f, mesh_out / f.name)
localize_urdf(urdf_cal, calib_urdf_pkg)
localize_urdf(urdf_unc, stock_urdf_pkg)
shutil.copy2(urdf_cal, calib_urdf_pkg.parent / "linkerhand_o12_right_biaoding.urdf")
template = template_xml.read_text()
cal_joints = parse_urdf(urdf_cal)
unc_joints = parse_urdf(urdf_unc)
calib_xml.write_text(
apply_urdf_to_mjcf(template, cal_joints, "linkerhand_o12_right_calibrated")
)
stock_xml.write_text(
apply_urdf_to_mjcf(template, unc_joints, "linkerhand_o12_right_uncalibrated")
)
mujoco.MjModel.from_xml_path(str(calib_xml))
mujoco.MjModel.from_xml_path(str(stock_xml))
print(f"[o12-right-dual] calibrated URDF: {urdf_cal}")
print(f"[o12-right-dual] calibrated MJCF: {calib_xml}")
print(f"[o12-right-dual] uncalibrated URDF: {urdf_unc}")
print(f"[o12-right-dual] uncalibrated MJCF: {stock_xml}")
for j in ("thumb_cmc_roll", "thumb_cmc_yaw", "index_mcp_roll", "index_mcp_pitch"):
c, u = cal_joints[j], unc_joints[j]
print(
f"[o12-right-dual] {j}: cal lim=[{c['lo']:g},{c['hi']:g}] rpy={c['rpy']} | "
f"unc lim=[{u['lo']:g},{u['hi']:g}] rpy={u['rpy']}"
)
PY
EXTRA=("$@")
cleanup() {
[[ -n "${PID_CALIB:-}" ]] && kill "$PID_CALIB" 2>/dev/null || true
[[ -n "${PID_UNCAL:-}" ]] && kill "$PID_UNCAL" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
echo "[o12-right-dual] launch calibrated window"
"$ROOT/hands/O12/scripts/run_o12_right_mujoco_plan.sh" \
--xml "$CALIB_XML" \
--mapping-urdf "$URDF_CALIBRATED" \
--o12-urdf "$URDF_CALIBRATED" \
--node-name o12_right_calibrated \
--window-title "O12 right 标定 (biaoding)" \
--cmd-topic /o12/right/joint_cmd \
--sim-state-topic /sim/mujoco/o12/right/calibrated/joint_states \
"${EXTRA[@]}" &
PID_CALIB=$!
sleep 1
echo "[o12-right-dual] launch uncalibrated window"
"$ROOT/hands/O12/scripts/run_o12_right_mujoco_plan.sh" \
--xml "$STOCK_XML" \
--mapping-urdf "$URDF_UNCALIBRATED" \
--o12-urdf "$URDF_UNCALIBRATED" \
--node-name o12_right_uncalibrated \
--window-title "O12 right 未标定 (stock)" \
--cmd-topic /o12/right/joint_cmd \
--sim-state-topic /sim/mujoco/o12/right/uncalibrated/joint_states \
"${EXTRA[@]}" &
PID_UNCAL=$!
echo "[o12-right-dual] both started. shared cmd: /o12/right/joint_cmd (12 rad)"
echo "[o12-right-dual] calibrated: $URDF_CALIBRATED"
echo "[o12-right-dual] uncalibrated: $URDF_UNCALIBRATED"
echo "[o12-right-dual] Ctrl+C stops both."
wait "$PID_CALIB" "$PID_UNCAL"
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
# MuJoCo O12 T3 右手仿真桥(OmniHand Pro 2025 ROS2 接口)
# URDF 来源: /home/lxqs/下载/linkerhand_o12_t3_right-0703_calibrated_O12_RIGHT_001_REVIEW_ONLY_20260909_112041.urdf
# (包内: urdf/O12/linker_hand_o12_right/linkerhand_o12_right.urdf)
# 订阅 /o12/right/joint_cmd(12 路弧度)
# 发布 /o12/right/joint_states(12 路弧度)
#
# 用法:
# ./hands/O12/scripts/run_o12_right_mujoco_plan.sh # 默认 --pd-drive(可见接触力,按 F)
# ./hands/O12/scripts/run_o12_right_mujoco_plan.sh --no-pd-drive # 纯 kinematic,最稳
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}"
XML="$PKG_SRC/linker_hand_mujoco_ros2/urdf/O12/linker_hand_o12_right/linker_hand_o12_right.xml"
URDF="$PKG_SRC/linker_hand_mujoco_ros2/urdf/O12/linker_hand_o12_right/linkerhand_o12_right.urdf"
if [[ ! -f "$XML" ]]; then
echo "[error] missing O12 MJCF: $XML" >&2
exit 1
fi
echo "[o12-right] urdf=$URDF"
echo "[o12-right] xml=$XML"
echo "[o12-right] cmd-topic=/o12/right/joint_cmd (12 rad)"
echo "[o12-right] state-topic=/o12/right/joint_states (12 rad)"
echo "[o12-right] default mode=pd-drive (viewer 按 F 看接触力; 默认 slew=4 rad/s 防穿模)"
echo "[o12-right] order (API_CPP_O12): thumb_roll,yaw,pitch,mcp,idx_roll,idx_pitch,idx_pip,mid_roll,mid_pitch,mid_pip,ring,pinky"
exec python3 -m linker_hand_mujoco_ros2.o12_plan_bridge \
--xml "$XML" \
--node-name o12_mujoco_plan_bridge_right \
--window-title "O12 T3 right" \
--cmd-topic /o12/right/joint_cmd \
--sim-state-topic /o12/right/joint_states \
--no-legacy-sdk \
--mapping g20_urdf \
"$@"
+8
View File
@@ -0,0 +1,8 @@
# O30
[统一入口与映射说明](../README.md)
- `run_left.sh`
- `run_right.sh`
`assets/` 是包内模型链接;`scripts/` 为内部实现及辅助工具。
+1
View File
@@ -0,0 +1 @@
../../src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/O30
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
# Historical V5 asset only: active piecewise mapping is disabled by current linear policy.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
export O30_RIGHT_URDF="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/O30/linker_hand_o30_right/linkerhand_o30_right_endpoint_calibrated.urdf"
exec "$ROOT/hands/O30/scripts/run_o30_right_mujoco_plan.sh" "$@"
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
# Run the preserved V6 pure linear URDF.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
export O30_RIGHT_URDF="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/O30/linker_hand_o30_right/linkerhand_o30_right_linear_calibrated.urdf"
exec "$ROOT/hands/O30/scripts/run_o30_right_mujoco_plan.sh" "$@"
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
# Run the preserved V7 pure linear URDF.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
export O30_RIGHT_URDF="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/O30/linker_hand_o30_right/linkerhand_o30_right_two_joint_calibrated.urdf"
exec "$ROOT/hands/O30/scripts/run_o30_right_mujoco_plan.sh" "$@"
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
# Run the preserved V8 pure linear URDF.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
export O30_RIGHT_URDF="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/O30/linker_hand_o30_right/linkerhand_o30_right_pinky_root_calibrated.urdf"
exec "$ROOT/hands/O30/scripts/run_o30_right_mujoco_plan.sh" "$@"
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
# Run the preserved V9 pure linear URDF.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
export O30_RIGHT_URDF="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/O30/linker_hand_o30_right/linkerhand_o30_right_middle_calibrated.urdf"
exec "$ROOT/hands/O30/scripts/run_o30_right_mujoco_plan.sh" "$@"
+1
View File
@@ -0,0 +1 @@
../../assets/exports/O30_V10_left_right
+1
View File
@@ -0,0 +1 @@
../../outputs/reports/O30
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_o30_left_mujoco_plan.sh" "$@"
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_o30_right_mujoco_plan.sh" "$@"
+292
View File
@@ -0,0 +1,292 @@
#!/usr/bin/env bash
# MuJoCo O30i 左手单窗仿真
# URDF: exports/O30_V9_left_right/left/linkerhand_o30_left_v9.urdf (V10 right mirrored; existing asset path retained)
# 订阅 /cb_left_hand_control_cmd(20 路 SDK 0~255,按 URDF 限位线性换算)
# 发布 /o30/left/joint_states(20 路弧度)
#
# 用法:
# ./hands/O30/scripts/run_o30_left_mujoco_plan.sh
# ./hands/O30/scripts/run_o30_left_mujoco_plan.sh --pd-drive
set -eo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
cd "$ROOT"
URDF_SRC="${O30_LEFT_URDF:-$ROOT/assets/exports/O30_V10_left_right/left/linkerhand_o30_left_v10.urdf}"
MESH_SRC="${O30_LEFT_MESHDIR:-$(dirname "$URDF_SRC")/meshes}"
PKG_SRC="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2"
O30_DIR="$PKG_SRC/urdf/O30/linker_hand_o30_left"
XML="$O30_DIR/linker_hand_o30_left.xml"
URDF_PKG="$O30_DIR/linkerhand_o30_left.urdf"
if [[ ! -f "$URDF_SRC" ]]; then
echo "[error] missing O30 URDF: $URDF_SRC" >&2
exit 1
fi
if [[ ! -d "$MESH_SRC" ]]; then
echo "[error] missing O30 meshes: $MESH_SRC" >&2
exit 1
fi
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
export PYTHONPATH="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2${PYTHONPATH:+:$PYTHONPATH}"
echo "[o30-left] sync MJCF from $URDF_SRC"
python3 - "$URDF_SRC" "$MESH_SRC" "$O30_DIR" "$XML" "$URDF_PKG" <<'PY'
import re
import shutil
import sys
from pathlib import Path
import mujoco
urdf_src = Path(sys.argv[1])
mesh_src = Path(sys.argv[2])
out_dir = Path(sys.argv[3])
xml_path = Path(sys.argv[4])
urdf_pkg = Path(sys.argv[5])
out_dir.mkdir(parents=True, exist_ok=True)
mesh_out = out_dir / "meshes"
mesh_out.mkdir(exist_ok=True)
for f in mesh_src.iterdir():
if f.is_file():
shutil.copy2(f, mesh_out / f.name)
text = urdf_src.read_text()
text = re.sub(r"package://[^/]+/meshes/", "meshes/", text)
urdf_pkg.write_text(text)
def attrs_of(chunk: str) -> dict[str, str]:
return dict(re.findall(r'(\w+)="([^"]*)"', chunk))
def parse_links(urdf: str) -> dict[str, dict]:
links: dict[str, dict] = {}
for m in re.finditer(r'<link\s+name="([^"]+)"[^>]*>(.*?)</link>', urdf, re.S):
name, body = m.group(1), m.group(2)
inertial = re.search(r"<inertial>(.*?)</inertial>", body, re.S)
pos = "0 0 0"
mass = "0.01"
fullinertia = "1e-6 1e-6 1e-6 0 0 0"
if inertial:
ib = inertial.group(1)
origin = re.search(r"<origin\b([^/]*)/>", ib)
mass_m = re.search(r'<mass\b([^/]*)/>', ib)
inertia = re.search(r"<inertia\b([^/]*)/>", ib)
if origin:
pos = attrs_of(origin.group(1)).get("xyz", pos)
if mass_m:
mass = attrs_of(mass_m.group(1)).get("value", mass)
if inertia:
ia = attrs_of(inertia.group(1))
fullinertia = " ".join(
ia.get(k, "0") for k in ("ixx", "iyy", "izz", "ixy", "ixz", "iyz")
)
color = "0.7 0.7 0.7 1"
vis = re.search(r"<visual>(.*?)</visual>", body, re.S)
if vis:
col = re.search(r'rgba="([^"]+)"', vis.group(1))
if col:
color = col.group(1)
mesh = None
mesh_m = re.search(r'<mesh\b([^>]*)/>', body)
if mesh_m:
fn = attrs_of(mesh_m.group(1)).get("filename", "")
mesh = Path(fn).name
links[name] = {
"pos": pos,
"mass": mass,
"fullinertia": fullinertia,
"color": color,
"mesh": mesh,
}
return links
def parse_joints(urdf: str) -> list[dict]:
joints = []
for m in re.finditer(r"<joint\b([^>]*)>(.*?)</joint>", urdf, re.S):
attrs, body = m.group(1), m.group(2)
name = re.search(r'name="([^"]+)"', attrs)
jtype = re.search(r'type="([^"]+)"', attrs)
if not name or not jtype or jtype.group(1) != "revolute":
continue
origin = re.search(r"<origin\b([^/]*)/>", body)
axis = re.search(r"<axis\b([^/]*)/>", body)
lim = re.search(r"<limit\b([^/]*)/>", body)
parent = re.search(r'<parent[^>]*link="([^"]+)"', body)
child = re.search(r'<child[^>]*link="([^"]+)"', body)
oa = attrs_of(origin.group(1)) if origin else {}
la = attrs_of(lim.group(1)) if lim else {}
joints.append(
{
"name": name.group(1),
"xyz": oa.get("xyz", "0 0 0"),
"rpy": oa.get("rpy", "0 0 0"),
"axis": attrs_of(axis.group(1)).get("xyz", "0 0 1") if axis else "0 0 1",
"lo": float(la.get("lower", "0")),
"hi": float(la.get("upper", "0")),
"parent": parent.group(1) if parent else "",
"child": child.group(1) if child else "",
}
)
return joints
def rpy_to_quat(rpy: str) -> str | None:
import math
r, p, y = [float(x) for x in rpy.split()]
if abs(r) + abs(p) + abs(y) < 1e-12:
return None
cr, sr = math.cos(r / 2), math.sin(r / 2)
cp, sp = math.cos(p / 2), math.sin(p / 2)
cy, sy = math.cos(y / 2), math.sin(y / 2)
w = cr * cp * cy + sr * sp * sy
x = sr * cp * cy - cr * sp * sy
yq = cr * sp * cy + sr * cp * sy
z = cr * cp * sy - sr * sp * cy
return f"{w:.16g} {x:.16g} {yq:.16g} {z:.16g}"
links = parse_links(text)
joints = parse_joints(text)
children = {j["parent"]: [] for j in joints}
for j in joints:
children.setdefault(j["parent"], []).append(j)
# keep URDF order
order = [j["name"] for j in joints]
by_parent: dict[str, list[dict]] = {}
for j in joints:
by_parent.setdefault(j["parent"], []).append(j)
root = "hand_base_link"
meshes = sorted({info["mesh"] for info in links.values() if info["mesh"]})
lines = [
'<mujoco model="linkerhand_o30_left">',
' <compiler angle="radian" meshdir="meshes"/>',
' <option timestep="0.002" gravity="0 0 -9.81" integrator="implicitfast"/>',
' <statistic center="0.01 0 0.08" extent="0.16"/>',
" <default>",
' <joint damping="0.15" armature="0.01"/>',
' <position kp="80" kv="8" forcelimited="true" forcerange="-8 8"/>',
' <default class="visual">',
' <geom type="mesh" contype="0" conaffinity="0" group="2"/>',
" </default>",
' <default class="collision">',
' <geom type="mesh" contype="1" conaffinity="1" condim="3" group="3"',
' rgba="0 1 0 0.15" friction="1 0.5 0.01"',
' solref="0.004 1" solimp="0.95 0.99 0.001" margin="0"/>',
" </default>",
" </default>",
" <asset>",
]
# Respect the asset scale: V10 STL vertices are already mirrored.
import xml.etree.ElementTree as ET
mesh_scales = {}
for element in ET.fromstring(text).findall('.//mesh'):
filename = Path(element.get('filename')).name
scale = element.get('scale', '1 1 1')
if filename in mesh_scales and mesh_scales[filename] != scale:
raise ValueError(f'Conflicting mesh scales: {filename}')
mesh_scales[filename] = scale
for mesh in meshes:
scale = mesh_scales[Path(mesh).name]
lines.append(f' <mesh name="{Path(mesh).stem}" file="{mesh}" scale="{scale}"/>')
lines.append(" </asset>")
lines.append(" <worldbody>")
def emit_body(link: str, indent: int, joint: dict | None) -> None:
pad = " " * indent
info = links[link]
if joint is None:
lines.append(f'{pad}<body name="{link}">')
else:
quat = rpy_to_quat(joint["rpy"])
pose = f'pos="{joint["xyz"]}"'
if quat:
pose += f' quat="{quat}"'
lines.append(f'{pad}<body name="{link}" {pose}>')
lines.append(
f'{pad} <inertial pos="{info["pos"]}" mass="{info["mass"]}" '
f'fullinertia="{info["fullinertia"]}"/>'
)
if joint is not None:
lines.append(
f'{pad} <joint name="{joint["name"]}" pos="0 0 0" axis="{joint["axis"]}" '
f'range="{joint["lo"]:.16g} {joint["hi"]:.16g}"/>'
)
mesh = Path(info["mesh"]).stem if info["mesh"] else None
if mesh:
lines.append(
f'{pad} <geom class="visual" rgba="{info["color"]}" mesh="{mesh}"/>'
)
lines.append(f'{pad} <geom class="collision" mesh="{mesh}"/>')
for child_j in by_parent.get(link, []):
emit_body(child_j["child"], indent + 1, child_j)
lines.append(f"{pad}</body>")
emit_body(root, 2, None)
lines.append(" </worldbody>")
lines.append(" <contact>")
for j in joints:
lines.append(f' <exclude body1="{j["parent"]}" body2="{j["child"]}"/>')
# 张开位掌心与近节 mesh 贴合,否则 PD 会把手指顶开
for other in (
"thumb_metacarpals_base2",
"index_proximal",
"middle_proximal",
"ring_proximal",
"pinky_proximal",
):
lines.append(f' <exclude body1="hand_base_link" body2="{other}"/>')
lines.append(" </contact>")
lines.append(" <actuator>")
for j in joints:
lines.append(
f' <position name="{j["name"]}_pos" joint="{j["name"]}" '
f'ctrlrange="{j["lo"]:.16g} {j["hi"]:.16g}"/>'
)
lines.append(" </actuator>")
lines.append("</mujoco>")
xml_path.write_text("\n".join(lines) + "\n")
model = mujoco.MjModel.from_xml_path(str(xml_path))
print(f"[o30-left] wrote {xml_path} njnt={model.njnt} nbody={model.nbody}")
PY
if [[ "${O30_LEFT_PREPARE_ONLY:-0}" == "1" ]]; then
exit 0
fi
echo "[o30-left] urdf=$URDF_PKG"
echo "[o30-left] xml=$XML"
echo "[o30-left] cmd-topic=/cb_left_hand_control_cmd (20 SDK values, 0..255)"
echo "[o30-left] state-topic=/o30/left/joint_states (20 rad)"
echo "[o30-left] SDK order: thumb_roll,thumb_yaw,index_yaw,middle_yaw,ring_yaw,pinky_yaw, thumb_root,index_root,middle_root,ring_root,pinky_root, index_pip,middle_pip,ring_pip,pinky_pip, thumb_tip,index_tip,middle_tip,ring_tip,pinky_tip"
echo "[o30-left] default mode=kinematic; 加 --pd-drive 看接触"
exec python3 -m linker_hand_mujoco_ros2.o30_left_plan_bridge \
--xml "$XML" \
--node-name o30_mujoco_plan_bridge_left \
--window-title "O30i left V10 mirrored" \
--cmd-topic /cb_left_hand_control_cmd \
--sim-state-topic /o30/left/joint_states \
--no-legacy-sdk \
--mapping g20_urdf \
"$@"
+312
View File
@@ -0,0 +1,312 @@
#!/usr/bin/env bash
# MuJoCo O30i 右手单窗仿真(V2_0819)
# 默认使用 V10 纯 URDF:保留对指锚点,调整新零位,保持线性换算。
# O30_RIGHT_URDF 可覆盖;启动姿态对应第五张照片的零位数组。
# 订阅 /cb_right_hand_control_cmd(20 维 SDK 0~255,顺序见 linkerhand-o30-ros2 JOINT_MAP)
# 发布 /sim/mujoco/o30/right/joint_state
#
# 用法:
# ./hands/O30/scripts/run_o30_right_mujoco_plan.sh
# ./hands/O30/scripts/run_o30_right_mujoco_plan.sh --pd-drive
# O30_RIGHT_PREPARE_ONLY=1 ./hands/O30/scripts/run_o30_right_mujoco_plan.sh # 只生成/校验模型
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"
O30_DIR="$PKG_SRC/urdf/O30/linker_hand_o30_right"
URDF_SRC="${O30_RIGHT_URDF:-$O30_DIR/linkerhand_o30_right_zero_v10_calibrated.urdf}"
MESH_SRC="${O30_RIGHT_MESHDIR:-$O30_DIR/meshes}"
XML="$O30_DIR/linker_hand_o30_right.xml"
URDF_PKG="$O30_DIR/linkerhand_o30_right.urdf"
if [[ ! -f "$URDF_SRC" ]]; then
echo "[error] missing O30 right URDF: $URDF_SRC" >&2
exit 1
fi
if [[ ! -d "$MESH_SRC" ]]; then
echo "[error] missing O30 right meshes: $MESH_SRC" >&2
exit 1
fi
VENV_SITE="$ROOT/venv/lib/python3.12/site-packages"
if [[ -d "$VENV_SITE" ]]; then
export PYTHONPATH="$VENV_SITE${PYTHONPATH:+:$PYTHONPATH}"
fi
export PYTHONPATH="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2${PYTHONPATH:+:$PYTHONPATH}"
echo "[o30-right] sync MJCF from $URDF_SRC"
python3 - "$URDF_SRC" "$MESH_SRC" "$O30_DIR" "$XML" "$URDF_PKG" <<'PY'
import re
import shutil
import sys
import xml.etree.ElementTree as ET
from pathlib import Path
import mujoco
urdf_src = Path(sys.argv[1])
mesh_src = Path(sys.argv[2])
out_dir = Path(sys.argv[3])
xml_path = Path(sys.argv[4])
urdf_pkg = Path(sys.argv[5])
out_dir.mkdir(parents=True, exist_ok=True)
mesh_out = out_dir / "meshes"
mesh_out.mkdir(exist_ok=True)
for f in mesh_src.iterdir():
if f.is_file() and f.resolve() != (mesh_out / f.name).resolve():
shutil.copy2(f, mesh_out / f.name)
text = urdf_src.read_text()
text = re.sub(r"package://[^/]+/meshes/", "meshes/", text)
urdf_pkg.write_text(text)
def attrs_of(chunk: str) -> dict[str, str]:
return dict(re.findall(r'(\w+)="([^"]*)"', chunk))
def parse_links(urdf: str) -> dict[str, dict]:
links: dict[str, dict] = {}
for m in re.finditer(r'<link\s+name="([^"]+)"[^>]*>(.*?)</link>', urdf, re.S):
name, body = m.group(1), m.group(2)
inertial = re.search(r"<inertial>(.*?)</inertial>", body, re.S)
pos = "0 0 0"
mass = "0.01"
fullinertia = "1e-6 1e-6 1e-6 0 0 0"
if inertial:
ib = inertial.group(1)
origin = re.search(r"<origin\b([^/]*)/>", ib)
mass_m = re.search(r"<mass\b([^/]*)/>", ib)
inertia = re.search(r"<inertia\b([^/]*)/>", ib)
if origin:
pos = attrs_of(origin.group(1)).get("xyz", pos)
if mass_m:
mass = attrs_of(mass_m.group(1)).get("value", mass)
if inertia:
ia = attrs_of(inertia.group(1))
fullinertia = " ".join(
ia.get(k, "0") for k in ("ixx", "iyy", "izz", "ixy", "ixz", "iyz")
)
color = "0.7 0.7 0.7 1"
vis = re.search(r"<visual>(.*?)</visual>", body, re.S)
if vis:
col = re.search(r'rgba="([^"]+)"', vis.group(1))
if col:
color = col.group(1)
mesh = None
mesh_scale = None
mesh_m = re.search(r"<mesh\b([^>]*)/>", body)
if mesh_m:
ma = attrs_of(mesh_m.group(1))
fn = ma.get("filename", "")
mesh = Path(fn).name
mesh_scale = ma.get("scale")
links[name] = {
"pos": pos,
"mass": mass,
"fullinertia": fullinertia,
"color": color,
"mesh": mesh,
"mesh_scale": mesh_scale,
}
return links
def parse_joints(urdf: str) -> list[dict]:
joints = []
for m in re.finditer(r"<joint\b([^>]*)>(.*?)</joint>", urdf, re.S):
attrs, body = m.group(1), m.group(2)
name = re.search(r'name="([^"]+)"', attrs)
jtype = re.search(r'type="([^"]+)"', attrs)
if not name or not jtype or jtype.group(1) != "revolute":
continue
origin = re.search(r"<origin\b([^/]*)/>", body)
axis = re.search(r"<axis\b([^/]*)/>", body)
lim = re.search(r"<limit\b([^/]*)/>", body)
parent = re.search(r'<parent[^>]*link="([^"]+)"', body)
child = re.search(r'<child[^>]*link="([^"]+)"', body)
oa = attrs_of(origin.group(1)) if origin else {}
la = attrs_of(lim.group(1)) if lim else {}
joints.append(
{
"name": name.group(1),
"xyz": oa.get("xyz", "0 0 0"),
"rpy": oa.get("rpy", "0 0 0"),
"axis": attrs_of(axis.group(1)).get("xyz", "0 0 1") if axis else "0 0 1",
"lo": float(la.get("lower", "0")),
"hi": float(la.get("upper", "0")),
"parent": parent.group(1) if parent else "",
"child": child.group(1) if child else "",
}
)
return joints
def rpy_to_quat(rpy: str) -> str | None:
import math
r, p, y = [float(x) for x in rpy.split()]
if abs(r) + abs(p) + abs(y) < 1e-12:
return None
cr, sr = math.cos(r / 2), math.sin(r / 2)
cp, sp = math.cos(p / 2), math.sin(p / 2)
cy, sy = math.cos(y / 2), math.sin(y / 2)
w = cr * cp * cy + sr * sp * sy
x = sr * cp * cy - cr * sp * sy
yq = cr * sp * cy + sr * cp * sy
z = cr * cp * sy - sr * sp * cy
return f"{w:.12g} {x:.12g} {yq:.12g} {z:.12g}"
links = parse_links(text)
joints = parse_joints(text)
by_parent: dict[str, list[dict]] = {}
for j in joints:
by_parent.setdefault(j["parent"], []).append(j)
root = "hand_base_link"
# mesh name -> scale (from first link that uses it)
mesh_scale: dict[str, str | None] = {}
for info in links.values():
if info["mesh"] and info["mesh"] not in mesh_scale:
mesh_scale[info["mesh"]] = info["mesh_scale"]
meshes = sorted(mesh_scale.keys())
lines = [
'<mujoco model="linkerhand_o30_right">',
' <compiler angle="radian" meshdir="meshes"/>',
' <option timestep="0.002" gravity="0 0 -9.81" integrator="implicitfast"/>',
' <statistic center="0.01 0 0.08" extent="0.16"/>',
" <default>",
' <joint damping="0.15" armature="0.01"/>',
' <position kp="80" kv="8" forcelimited="true" forcerange="-8 8"/>',
' <default class="visual">',
' <geom type="mesh" contype="0" conaffinity="0" group="2"/>',
" </default>",
' <default class="collision">',
' <geom type="mesh" contype="1" conaffinity="1" condim="3" group="3"',
' rgba="0 1 0 0.15" friction="1 0.5 0.01"',
' solref="0.004 1" solimp="0.95 0.99 0.001" margin="0"/>',
" </default>",
" </default>",
" <asset>",
]
for mesh in meshes:
stem = Path(mesh).stem
sc = mesh_scale.get(mesh)
if sc:
lines.append(f' <mesh name="{stem}" file="{mesh}" scale="{sc}"/>')
else:
lines.append(f' <mesh name="{stem}" file="{mesh}"/>')
lines.append(" </asset>")
lines.append(" <worldbody>")
def emit_body(link: str, indent: int, joint: dict | None) -> None:
pad = " " * indent
info = links[link]
if joint is None:
lines.append(f'{pad}<body name="{link}">')
else:
quat = rpy_to_quat(joint["rpy"])
pose = f'pos="{joint["xyz"]}"'
if quat:
pose += f' quat="{quat}"'
lines.append(f'{pad}<body name="{link}" {pose}>')
lines.append(
f'{pad} <inertial pos="{info["pos"]}" mass="{info["mass"]}" '
f'fullinertia="{info["fullinertia"]}"/>'
)
if joint is not None:
lines.append(
f'{pad} <joint name="{joint["name"]}" pos="0 0 0" axis="{joint["axis"]}" '
f'range="{joint["lo"]:.12g} {joint["hi"]:.12g}"/>'
)
mesh = Path(info["mesh"]).stem if info["mesh"] else None
if mesh:
lines.append(
f'{pad} <geom class="visual" rgba="{info["color"]}" mesh="{mesh}"/>'
)
lines.append(f'{pad} <geom class="collision" mesh="{mesh}"/>')
for child_j in by_parent.get(link, []):
emit_body(child_j["child"], indent + 1, child_j)
lines.append(f"{pad}</body>")
emit_body(root, 2, None)
lines.append(" </worldbody>")
lines.append(" <contact>")
for j in joints:
lines.append(f' <exclude body1="{j["parent"]}" body2="{j["child"]}"/>')
for other in (
"thumb_metacarpals_base2",
"index_proximal",
"middle_proximal",
"ring_proximal",
"pinky_proximal",
):
lines.append(f' <exclude body1="hand_base_link" body2="{other}"/>')
lines.append(" </contact>")
lines.append(" <actuator>")
for j in joints:
lines.append(
f' <position name="{j["name"]}_pos" joint="{j["name"]}" '
f'ctrlrange="{j["lo"]:.12g} {j["hi"]:.12g}"/>'
)
lines.append(" </actuator>")
sdk_mapping = ET.fromstring(text).find('o30_sdk_mapping')
if sdk_mapping is not None:
if sdk_mapping.get('version') != '1' or sdk_mapping.get('interpolation') != 'linear':
raise ValueError('Unsupported O30 SDK mapping format')
lines.append(' <custom>')
for item in sdk_mapping.findall('map'):
name = item.get('joint', '')
if name not in {j['name'] for j in joints}:
raise ValueError(f'Unknown mapped joint: {name}')
sdk = item.get('sdk', '').split()
radians = item.get('radians', '').split()
if len(sdk) != len(radians):
raise ValueError(f'O30 mapping knot count mismatch: {name}')
data = ' '.join(format(float(v), '.16g') for pair in zip(sdk, radians) for v in pair)
lines.append(f' <numeric name="o30_sdk_map_{name}" data="{data}"/>')
lines.append(' </custom>')
lines.append("</mujoco>")
xml_path.write_text("\n".join(lines) + "\n")
model = mujoco.MjModel.from_xml_path(str(xml_path))
from linker_hand_mujoco_ros2.utils.o30_right_joints import configure_o30_right_limits
configure_o30_right_limits(xml_path)
print(f"[o30-right] wrote {xml_path} njnt={model.njnt} nbody={model.nbody} ngeom={model.ngeom}")
PY
if [[ "${O30_RIGHT_PREPARE_ONLY:-0}" == "1" ]]; then
exit 0
fi
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
# Keep this checkout ahead of installed copies after sourcing ROS workspaces.
export PYTHONPATH="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2${PYTHONPATH:+:$PYTHONPATH}"
echo "[o30-right] urdf=$URDF_PKG"
echo "[o30-right] xml=$XML"
echo "[o30-right] cmd-topic=/cb_right_hand_control_cmd (20ch SDK 0~255, JOINT_MAP 顺序)"
echo "[o30-right] state-topic=/sim/mujoco/o30/right/joint_state"
echo "[o30-right] order: thumb_roll,yaw, idx/mid/ring/little_yaw, thumb/idx/mid/ring/little_root1, idx/mid/ring/little_root2, thumb/idx/mid/ring/little_tip"
echo "[o30-right] default mode=kinematic; 加 --pd-drive 看接触"
exec python3 -m linker_hand_mujoco_ros2.o30_right_plan_bridge \
--xml "$XML" \
--node-name o30_mujoco_plan_bridge_right \
--window-title "O30i right V10 zero calibrated" \
--cmd-topic /cb_right_hand_control_cmd \
--sim-state-topic /sim/mujoco/o30/right/joint_state \
--mapping g20_urdf \
"$@"
+10
View File
@@ -0,0 +1,10 @@
# O6
[统一入口与映射说明](../README.md)
- `compare_left.sh`
- `compare_right.sh`
- `run_left.sh`
- `run_right.sh`
`assets/` 是包内模型链接;`scripts/` 为内部实现及辅助工具。
+1
View File
@@ -0,0 +1 @@
../../src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/O6
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_o6_left_dual_compare.sh" "$@"
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_o6_right_dual_compare.sh" "$@"
+666
View File
@@ -0,0 +1,666 @@
<?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_o6_left">
<material name="silver">
<color rgba="0.5 0.5 0.52 1.0"/>
</material>
<material name="BlackAccent">
<color rgba="0.15 0.15 0.15 1.0"/>
</material>
<link
name="lh_hand_base_link">
<inertial>
<origin
xyz="-0.00157788568070457 -0.00034876725935859 0.0538452219801211"
rpy="0 0 0" />
<mass
value="0.0672935238166485" />
<inertia
ixx="5.22525212160954E-05"
ixy="3.06955729584715E-07"
ixz="-3.90341166950521E-07"
iyy="3.33522349207724E-05"
iyz="-7.76680063574627E-07"
izz="2.28469461105385E-05" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/hand_base_link.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/hand_base_link.STL" />
</geometry>
</collision>
</link>
<link
name="lh_thumb_metacarpals_base2">
<inertial>
<origin
xyz="0.00162800681276937 -0.0027668017246415 -0.00184234438641236"
rpy="0 0 0" />
<mass
value="0.00138588296544333" />
<inertia
ixx="5.26292160685909E-08"
ixy="-3.92888681640568E-09"
ixz="-2.43767371295222E-09"
iyy="4.82620491570326E-08"
iyz="3.71869784206147E-09"
izz="2.88550286112821E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
<material name="BlackAccent"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_thumb_cmc_yaw"
type="revolute">
<origin
xyz="0.011508 -0.022975 0.032794"
rpy="0 0 0.167545678324764" />
<parent
link="lh_hand_base_link" />
<child
link="lh_thumb_metacarpals_base2" />
<axis
xyz="0 0 1" />
<limit
lower="0"
upper="1.2431454"
effort="100"
velocity="1" />
</joint>
<link
name="lh_thumb_metacarpals">
<inertial>
<origin
xyz="4.24816204999817E-05 1.74183907812875E-05 0.0228341640631254"
rpy="0 0 0" />
<mass
value="0.0123381586619931" />
<inertia
ixx="1.10292537368909E-06"
ixy="-5.99642232777877E-10"
ixz="-1.14233573940545E-07"
iyy="1.76757772551884E-06"
iyz="-4.03963570823777E-10"
izz="9.36347792411098E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_thumb_cmc_pitch"
type="revolute">
<origin
xyz="0.0061649 -0.010678 -0.004891"
rpy="0 -1.1529 2.0944" />
<parent
link="lh_thumb_metacarpals_base2" />
<child
link="lh_thumb_metacarpals" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="0.588901345"
effort="100"
velocity="1" />
</joint>
<link
name="lh_thumb_distal">
<inertial>
<origin
xyz="-0.00413725702921841 -5.90704520436747E-08 0.023011401589736"
rpy="0 0 0" />
<mass
value="0.00763332957284057" />
<inertia
ixx="1.3057487813702E-06"
ixy="-1.19781508112714E-11"
ixz="-7.19551697268541E-08"
iyy="1.2248908905341E-06"
iyz="4.55347974652679E-12"
izz="2.77271910592615E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_thumb_ip"
type="revolute">
<origin
xyz="0.0037776 0 0.045368"
rpy="0 0 0" />
<parent
link="lh_thumb_metacarpals" />
<child
link="lh_thumb_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.16064834"
effort="100"
velocity="1" />
<mimic
joint="lh_thumb_cmc_pitch"
multiplier="1.96832996705076"
offset="0" />
</joint>
<link
name="lh_index_proximal">
<inertial>
<origin
xyz="-0.00208656556336452 2.53838713251686E-06 0.0172734941980264"
rpy="0 0 0" />
<mass
value="0.00253174651934627" />
<inertia
ixx="2.65988714157799E-07"
ixy="-1.67174578825824E-11"
ixz="-4.31047725546034E-08"
iyy="2.72914069166233E-07"
iyz="1.8756709532167E-10"
izz="8.56017305636748E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_index_mcp_pitch"
type="revolute">
<origin
xyz="0.0024758 -0.02419 0.098779"
rpy="0.05236 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_index_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.609333425"
effort="100"
velocity="1" />
</joint>
<link
name="lh_index_distal">
<inertial>
<origin
xyz="0.00658986616416549 5.13855642059191E-06 0.0194393600039178"
rpy="0 0 0" />
<mass
value="0.00376951085886838" />
<inertia
ixx="3.22347106085043E-07"
ixy="1.3801788663582E-11"
ixz="1.14352857317951E-07"
iyy="3.65328133721509E-07"
iyz="-2.11505011705634E-10"
izz="1.24287996386442E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_index_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="lh_index_proximal" />
<child
link="lh_index_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.49597186651782"
effort="100"
velocity="1" />
<mimic
joint="lh_index_mcp_pitch"
multiplier="0.904318245922221"
offset="0" />
</joint>
<link
name="lh_middle_proximal">
<inertial>
<origin
xyz="-0.00208660654138235 2.56918180119303E-06 0.0172735011723351"
rpy="0 0 0" />
<mass
value="0.00253177375206983" />
<inertia
ixx="2.65989021304483E-07"
ixy="-1.69816046635662E-11"
ixz="-4.31046683215273E-08"
iyy="2.72914402745606E-07"
iyz="1.87484652324395E-10"
izz="8.5602213431865E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_middle_mcp_pitch"
type="revolute">
<origin
xyz="0.00052576 -0.00634 0.1027"
rpy="0 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_middle_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.609333425"
effort="100"
velocity="1" />
</joint>
<link
name="lh_middle_distal">
<inertial>
<origin
xyz="0.00658986516319194 5.10862924271716E-06 0.0194393315699782"
rpy="0 0 0" />
<mass
value="0.00376952789210256" />
<inertia
ixx="3.22348591068053E-07"
ixy="1.37122394856588E-11"
ixz="1.1435291391668E-07"
iyy="3.65329535087755E-07"
iyz="-2.10453810291991E-10"
izz="1.24288425862546E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_middle_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="lh_middle_proximal" />
<child
link="lh_middle_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.49597186651782"
effort="100"
velocity="1" />
<mimic
joint="lh_middle_mcp_pitch"
multiplier="0.904318245922221"
offset="0" />
</joint>
<link
name="lh_ring_proximal">
<inertial>
<origin
xyz="-0.00208667931345812 2.63000419462034E-06 0.0172734926448377"
rpy="0 0 0" />
<mass
value="0.00253182223645066" />
<inertia
ixx="2.65989756076584E-07"
ixy="-1.74912952703656E-11"
ixz="-4.31043325594253E-08"
iyy="2.7291510093393E-07"
iyz="1.87158326740895E-10"
izz="8.56031522765909E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_ring_mcp_pitch"
type="revolute">
<origin
xyz="0.0010258 0.011135 0.098767"
rpy="-0.05236 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_ring_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.609333425"
effort="100"
velocity="1" />
</joint>
<link
name="lh_ring_distal">
<inertial>
<origin
xyz="0.00658986648895491 5.05883528165163E-06 0.019439397215532"
rpy="0 0 0" />
<mass
value="0.00376949588316609" />
<inertia
ixx="3.2234524157181E-07"
ixy="1.35766976056011E-11"
ixz="1.14353011012842E-07"
iyy="3.65326570102147E-07"
iyz="-2.08672149138738E-10"
izz="1.24287770218135E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_ring_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="lh_ring_proximal" />
<child
link="lh_ring_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.49597186651782"
effort="100"
velocity="1" />
<mimic
joint="lh_ring_mcp_pitch"
multiplier="0.904318245922221"
offset="0" />
</joint>
<link
name="lh_pinky_proximal">
<inertial>
<origin
xyz="-0.00208673599799501 2.68062251165824E-06 0.0172734848131236"
rpy="0 0 0" />
<mass
value="0.00253185993162408" />
<inertia
ixx="2.6599033687183E-07"
ixy="-1.79030790016555E-11"
ixz="-4.31040829565781E-08"
iyy="2.72915617381577E-07"
iyz="1.86906787420486E-10"
izz="8.56039175034871E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_pinky_mcp_pitch"
type="revolute">
<origin
xyz="0.0024758 0.028372 0.092741"
rpy="-0.087266 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_pinky_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.609333425"
effort="100"
velocity="1" />
</joint>
<link
name="lh_pinky_distal">
<inertial>
<origin
xyz="0.00658987588903027 5.03087534178825E-06 0.0194394491065091"
rpy="0 0 0" />
<mass
value="0.00376948071276134" />
<inertia
ixx="3.22343583730488E-07"
ixy="1.35768173270357E-11"
ixz="1.14353289593012E-07"
iyy="3.65325180884183E-07"
iyz="-2.0744198062439E-10"
izz="1.24287567374229E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_pinky_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="lh_pinky_proximal" />
<child
link="lh_pinky_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.49597186651782"
effort="100"
velocity="1" />
<mimic
joint="lh_pinky_mcp_pitch"
multiplier="0.904318245922221"
offset="0" />
</joint>
</robot>
+666
View File
@@ -0,0 +1,666 @@
<?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_o6_left">
<material name="silver">
<color rgba="0.5 0.5 0.52 1.0"/>
</material>
<material name="BlackAccent">
<color rgba="0.15 0.15 0.15 1.0"/>
</material>
<link
name="lh_hand_base_link">
<inertial>
<origin
xyz="-0.00157788568070457 -0.00034876725935859 0.0538452219801211"
rpy="0 0 0" />
<mass
value="0.0672935238166485" />
<inertia
ixx="5.22525212160954E-05"
ixy="3.06955729584715E-07"
ixz="-3.90341166950521E-07"
iyy="3.33522349207724E-05"
iyz="-7.76680063574627E-07"
izz="2.28469461105385E-05" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/hand_base_link.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/hand_base_link.STL" />
</geometry>
</collision>
</link>
<link
name="lh_thumb_metacarpals_base2">
<inertial>
<origin
xyz="0.00162800681276937 -0.0027668017246415 -0.00184234438641236"
rpy="0 0 0" />
<mass
value="0.00138588296544333" />
<inertia
ixx="5.26292160685909E-08"
ixy="-3.92888681640568E-09"
ixz="-2.43767371295222E-09"
iyy="4.82620491570326E-08"
iyz="3.71869784206147E-09"
izz="2.88550286112821E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
<material name="BlackAccent"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_thumb_cmc_yaw"
type="revolute">
<origin
xyz="0.011508 -0.022975 0.032794"
rpy="0 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_thumb_metacarpals_base2" />
<axis
xyz="0 0 1" />
<limit
lower="0"
upper="1.3"
effort="100"
velocity="1" />
</joint>
<link
name="lh_thumb_metacarpals">
<inertial>
<origin
xyz="4.24816204999817E-05 1.74183907812875E-05 0.0228341640631254"
rpy="0 0 0" />
<mass
value="0.0123381586619931" />
<inertia
ixx="1.10292537368909E-06"
ixy="-5.99642232777877E-10"
ixz="-1.14233573940545E-07"
iyy="1.76757772551884E-06"
iyz="-4.03963570823777E-10"
izz="9.36347792411098E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_thumb_cmc_pitch"
type="revolute">
<origin
xyz="0.0061649 -0.010678 -0.004891"
rpy="0 -1.1529 2.0944" />
<parent
link="lh_thumb_metacarpals_base2" />
<child
link="lh_thumb_metacarpals" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="0.58"
effort="100"
velocity="1" />
</joint>
<link
name="lh_thumb_distal">
<inertial>
<origin
xyz="-0.00413725702921841 -5.90704520436747E-08 0.023011401589736"
rpy="0 0 0" />
<mass
value="0.00763332957284057" />
<inertia
ixx="1.3057487813702E-06"
ixy="-1.19781508112714E-11"
ixz="-7.19551697268541E-08"
iyy="1.2248908905341E-06"
iyz="4.55347974652679E-12"
izz="2.77271910592615E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_thumb_ip"
type="revolute">
<origin
xyz="0.0037776 0 0.045368"
rpy="0 0 0" />
<parent
link="lh_thumb_metacarpals" />
<child
link="lh_thumb_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.08"
effort="100"
velocity="1" />
<mimic
joint="lh_thumb_cmc_pitch"
multiplier="2.29"
offset="0" />
</joint>
<link
name="lh_index_proximal">
<inertial>
<origin
xyz="-0.00208656556336452 2.53838713251686E-06 0.0172734941980264"
rpy="0 0 0" />
<mass
value="0.00253174651934627" />
<inertia
ixx="2.65988714157799E-07"
ixy="-1.67174578825824E-11"
ixz="-4.31047725546034E-08"
iyy="2.72914069166233E-07"
iyz="1.8756709532167E-10"
izz="8.56017305636748E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_index_mcp_pitch"
type="revolute">
<origin
xyz="0.0024758 -0.02419 0.098779"
rpy="0.05236 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_index_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.60"
effort="100"
velocity="1" />
</joint>
<link
name="lh_index_distal">
<inertial>
<origin
xyz="0.00658986616416549 5.13855642059191E-06 0.0194393600039178"
rpy="0 0 0" />
<mass
value="0.00376951085886838" />
<inertia
ixx="3.22347106085043E-07"
ixy="1.3801788663582E-11"
ixz="1.14352857317951E-07"
iyy="3.65328133721509E-07"
iyz="-2.11505011705634E-10"
izz="1.24287996386442E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_index_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="lh_index_proximal" />
<child
link="lh_index_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.43"
effort="100"
velocity="1" />
<mimic
joint="lh_index_mcp_pitch"
multiplier="0.89"
offset="0" />
</joint>
<link
name="lh_middle_proximal">
<inertial>
<origin
xyz="-0.00208660654138235 2.56918180119303E-06 0.0172735011723351"
rpy="0 0 0" />
<mass
value="0.00253177375206983" />
<inertia
ixx="2.65989021304483E-07"
ixy="-1.69816046635662E-11"
ixz="-4.31046683215273E-08"
iyy="2.72914402745606E-07"
iyz="1.87484652324395E-10"
izz="8.5602213431865E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_middle_mcp_pitch"
type="revolute">
<origin
xyz="0.00052576 -0.00634 0.1027"
rpy="0 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_middle_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.60"
effort="100"
velocity="1" />
</joint>
<link
name="lh_middle_distal">
<inertial>
<origin
xyz="0.00658986516319194 5.10862924271716E-06 0.0194393315699782"
rpy="0 0 0" />
<mass
value="0.00376952789210256" />
<inertia
ixx="3.22348591068053E-07"
ixy="1.37122394856588E-11"
ixz="1.1435291391668E-07"
iyy="3.65329535087755E-07"
iyz="-2.10453810291991E-10"
izz="1.24288425862546E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_middle_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="lh_middle_proximal" />
<child
link="lh_middle_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.43"
effort="100"
velocity="1" />
<mimic
joint="lh_middle_mcp_pitch"
multiplier="0.89"
offset="0" />
</joint>
<link
name="lh_ring_proximal">
<inertial>
<origin
xyz="-0.00208667931345812 2.63000419462034E-06 0.0172734926448377"
rpy="0 0 0" />
<mass
value="0.00253182223645066" />
<inertia
ixx="2.65989756076584E-07"
ixy="-1.74912952703656E-11"
ixz="-4.31043325594253E-08"
iyy="2.7291510093393E-07"
iyz="1.87158326740895E-10"
izz="8.56031522765909E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_ring_mcp_pitch"
type="revolute">
<origin
xyz="0.0010258 0.011135 0.098767"
rpy="-0.05236 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_ring_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.60"
effort="100"
velocity="1" />
</joint>
<link
name="lh_ring_distal">
<inertial>
<origin
xyz="0.00658986648895491 5.05883528165163E-06 0.019439397215532"
rpy="0 0 0" />
<mass
value="0.00376949588316609" />
<inertia
ixx="3.2234524157181E-07"
ixy="1.35766976056011E-11"
ixz="1.14353011012842E-07"
iyy="3.65326570102147E-07"
iyz="-2.08672149138738E-10"
izz="1.24287770218135E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_ring_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="lh_ring_proximal" />
<child
link="lh_ring_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.43"
effort="100"
velocity="1" />
<mimic
joint="lh_ring_mcp_pitch"
multiplier="0.89"
offset="0" />
</joint>
<link
name="lh_pinky_proximal">
<inertial>
<origin
xyz="-0.00208673599799501 2.68062251165824E-06 0.0172734848131236"
rpy="0 0 0" />
<mass
value="0.00253185993162408" />
<inertia
ixx="2.6599033687183E-07"
ixy="-1.79030790016555E-11"
ixz="-4.31040829565781E-08"
iyy="2.72915617381577E-07"
iyz="1.86906787420486E-10"
izz="8.56039175034871E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_pinky_mcp_pitch"
type="revolute">
<origin
xyz="0.0024758 0.028372 0.092741"
rpy="-0.087266 0 0" />
<parent
link="lh_hand_base_link" />
<child
link="lh_pinky_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.60"
effort="100"
velocity="1" />
</joint>
<link
name="lh_pinky_distal">
<inertial>
<origin
xyz="0.00658987588903027 5.03087534178825E-06 0.0194394491065091"
rpy="0 0 0" />
<mass
value="0.00376948071276134" />
<inertia
ixx="3.22343583730488E-07"
ixy="1.35768173270357E-11"
ixz="1.14353289593012E-07"
iyy="3.65325180884183E-07"
iyz="-2.0744198062439E-10"
izz="1.24287567374229E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="lh_pinky_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="lh_pinky_proximal" />
<child
link="lh_pinky_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.43"
effort="100"
velocity="1" />
<mimic
joint="lh_pinky_mcp_pitch"
multiplier="0.89"
offset="0" />
</joint>
</robot>
+664
View File
@@ -0,0 +1,664 @@
<?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_o6_right">
<material name="silver">
<color rgba="0.5 0.5 0.52 1.0"/>
</material>
<material name="BlackAccent">
<color rgba="0.15 0.15 0.15 1.0"/>
</material>
<link
name="rh_hand_base_link">
<inertial>
<origin
xyz="-0.00160320773490556 0.000358462699237561 0.0538002704027215"
rpy="0 0 0" />
<mass
value="0.06740077960995" />
<inertia
ixx="5.22359941041901E-05"
ixy="3.0391535852764E-07"
ixz="3.88322215371643E-07"
iyy="3.33473043513648E-05"
iyz="-7.77555280794861E-07"
izz="2.28342677422006E-05" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/hand_base_link.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/hand_base_link.STL" />
</geometry>
</collision>
</link>
<link
name="rh_thumb_metacarpals_base2">
<inertial>
<origin
xyz="0.00162795738393507 0.00276671978336815 -0.00184227725602707"
rpy="0 0 0" />
<mass
value="0.00138591070212997" />
<inertia
ixx="5.26294625624223E-08"
ixy="-3.9288013799604E-09"
ixz="2.43766268199571E-09"
iyy="4.82623724532942E-08"
iyz="3.71867754115443E-09"
izz="2.88555522060562E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
<material name="BlackAccent"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_thumb_cmc_yaw"
type="revolute">
<origin
xyz="0.011508 0.022975 0.032794"
rpy="0 0 -0.167545678324764" />
<parent
link="rh_hand_base_link" />
<child
link="rh_thumb_metacarpals_base2" />
<axis
xyz="0 0 -1" />
<limit
lower="0"
upper="1.2431454"
effort="100"
velocity="1" />
</joint>
<link
name="rh_thumb_metacarpals">
<inertial>
<origin
xyz="4.24920612856275E-05 -1.67885666835232E-05 0.0228341522590467"
rpy="0 0 0" />
<mass
value="0.0123381607727595" />
<inertia
ixx="1.10292488693766E-06"
ixy="-6.14241939151719E-10"
ixz="1.14233108676795E-07"
iyy="1.76757658485787E-06"
iyz="-4.24576599367025E-10"
izz="9.36348143657406E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_thumb_cmc_pitch"
type="revolute">
<origin
xyz="0.0061649 0.010678 -0.004891"
rpy="0 -1.1529 -2.0944" />
<parent
link="rh_thumb_metacarpals_base2" />
<child
link="rh_thumb_metacarpals" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="0.588901345"
effort="100"
velocity="1" />
</joint>
<link
name="rh_thumb_distal">
<inertial>
<origin
xyz="-0.00413690727496815 -4.54842793429222E-07 0.0230097179163771"
rpy="0 0 0" />
<mass
value="0.00763302603723749" />
<inertia
ixx="1.30546842791578E-06"
ixy="-2.61617511029467E-12"
ixz="7.19030685242407E-08"
iyy="1.22457212713176E-06"
iyz="1.66931068485225E-11"
izz="2.7729897944409E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_thumb_ip"
type="revolute">
<origin
xyz="0.0037776 0 0.045368"
rpy="0 0 0" />
<parent
link="rh_thumb_metacarpals" />
<child
link="rh_thumb_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.16064834"
effort="100"
velocity="1" />
<mimic
joint="rh_thumb_cmc_pitch"
multiplier="1.96832996705076"
offset="0" />
</joint>
<link
name="rh_index_proximal">
<inertial>
<origin
xyz="-0.00208665596033792 -4.8964990644329E-06 0.0172736718900068"
rpy="0 0 0" />
<mass
value="0.00253178529301663" />
<inertia
ixx="2.65990159539482E-07"
ixy="-3.55165625722842E-11"
ixz="4.31046335852167E-08"
iyy="2.72915460277804E-07"
iyz="1.75935978708959E-10"
izz="8.56026306687528E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_index_mcp_pitch"
type="revolute">
<origin
xyz="0.0024758 0.02419 0.098779"
rpy="-0.05236 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_index_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.609333425"
effort="100"
velocity="1" />
</joint>
<link
name="rh_index_distal">
<inertial>
<origin
xyz="0.00658940011630532 -4.59976557022623E-06 0.0194434136420913"
rpy="0 0 0" />
<mass
value="0.00376787129082313" />
<inertia
ixx="3.22116619426455E-07"
ixy="1.03152735699049E-11"
ixz="-1.14363506430985E-07"
iyy="3.65131844686317E-07"
iyz="-1.97263639269362E-10"
izz="1.24256890612907E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_index_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="rh_index_proximal" />
<child
link="rh_index_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.49597186651782"
effort="100"
velocity="1" />
<mimic
joint="rh_index_mcp_pitch"
multiplier="0.904318245922221"
offset="0" />
</joint>
<link
name="rh_middle_proximal">
<inertial>
<origin
xyz="-0.00208661081773556 -4.93451838476378E-06 0.0172736805321904"
rpy="0 0 0" />
<mass
value="0.00253175497594429" />
<inertia
ixx="2.65989683690695E-07"
ixy="-3.58343951791586E-11"
ixz="4.31048601085216E-08"
iyy="2.72915006047006E-07"
iyz="1.75718024412593E-10"
izz="8.56020467202518E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_middle_mcp_pitch"
type="revolute">
<origin
xyz="0.00052576 0.00634 0.1027"
rpy="0 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_middle_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.609333425"
effort="100"
velocity="1" />
</joint>
<link
name="rh_middle_distal">
<inertial>
<origin
xyz="0.00658940304809306 -4.5885743593238E-06 0.0194434523296449"
rpy="0 0 0" />
<mass
value="0.00376786259758948" />
<inertia
ixx="3.221153658306E-07"
ixy="1.02639617308432E-11"
ixz="-1.14363547272711E-07"
iyy="3.65130723083104E-07"
iyz="-1.96904618797577E-10"
izz="1.24256787913797E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_middle_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="rh_middle_proximal" />
<child
link="rh_middle_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.49597186651782"
effort="100"
velocity="1" />
<mimic
joint="rh_middle_mcp_pitch"
multiplier="0.904318245922221"
offset="0" />
</joint>
<link
name="rh_ring_proximal">
<inertial>
<origin
xyz="-0.00208654311325356 -4.98538564003281E-06 0.0172736743497407"
rpy="0 0 0" />
<mass
value="0.00253170963529411" />
<inertia
ixx="2.6598913795678E-07"
ixy="-3.62757108675266E-11"
ixz="4.31050611555507E-08"
iyy="2.72914417061517E-07"
iyz="1.75554984053478E-10"
izz="8.56012458677515E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_ring_mcp_pitch"
type="revolute">
<origin
xyz="0.0010258 -0.011135 0.098767"
rpy="0.05236 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_ring_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.609333425"
effort="100"
velocity="1" />
</joint>
<link
name="rh_ring_distal">
<inertial>
<origin
xyz="0.00658940273069729 -4.56628863591824E-06 0.0194435005686854"
rpy="0 0 0" />
<mass
value="0.0037678395356105" />
<inertia
ixx="3.22112632437858E-07"
ixy="1.01848408913558E-11"
ixz="-1.1436348915342E-07"
iyy="3.65128262095686E-07"
iyz="-1.96145632992445E-10"
izz="1.24256284608064E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_ring_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="rh_ring_proximal" />
<child
link="rh_ring_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.49597186651782"
effort="100"
velocity="1" />
<mimic
joint="rh_ring_mcp_pitch"
multiplier="0.904318245922221"
offset="0" />
</joint>
<link
name="rh_pinky_proximal">
<inertial>
<origin
xyz="-0.00208649034130813 -5.02314582560837E-06 0.0172736680793697"
rpy="0 0 0" />
<mass
value="0.00253167400540778" />
<inertia
ixx="2.65988718820404E-07"
ixy="-3.66012295280295E-11"
ixz="4.31052220484445E-08"
iyy="2.72913936904813E-07"
iyz="1.75447396222308E-10"
izz="8.56006424720129E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_pinky_mcp_pitch"
type="revolute">
<origin
xyz="0.0024758 -0.028372 0.092741"
rpy="0.087266 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_pinky_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.609333425"
effort="100"
velocity="1" />
</joint>
<link
name="rh_pinky_distal">
<inertial>
<origin
xyz="0.00658940431519205 -4.55268314210333E-06 0.0194435341098305"
rpy="0 0 0" />
<mass
value="0.00376782669873412" />
<inertia
ixx="3.22111112339498E-07"
ixy="1.01707985908333E-11"
ixz="-1.14363524419061E-07"
iyy="3.65126916899176E-07"
iyz="-1.95589215866356E-10"
izz="1.24256004854426E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_pinky_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="rh_pinky_proximal" />
<child
link="rh_pinky_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.49597186651782"
effort="100"
velocity="1" />
<mimic
joint="rh_pinky_mcp_pitch"
multiplier="0.904318245922221"
offset="0" />
</joint>
</robot>
+664
View File
@@ -0,0 +1,664 @@
<?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_o6_right">
<material name="silver">
<color rgba="0.5 0.5 0.52 1.0"/>
</material>
<material name="BlackAccent">
<color rgba="0.15 0.15 0.15 1.0"/>
</material>
<link
name="rh_hand_base_link">
<inertial>
<origin
xyz="-0.00160320773490556 0.000358462699237561 0.0538002704027215"
rpy="0 0 0" />
<mass
value="0.06740077960995" />
<inertia
ixx="5.22359941041901E-05"
ixy="3.0391535852764E-07"
ixz="3.88322215371643E-07"
iyy="3.33473043513648E-05"
iyz="-7.77555280794861E-07"
izz="2.28342677422006E-05" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/hand_base_link.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/hand_base_link.STL" />
</geometry>
</collision>
</link>
<link
name="rh_thumb_metacarpals_base2">
<inertial>
<origin
xyz="0.00162795738393507 0.00276671978336815 -0.00184227725602707"
rpy="0 0 0" />
<mass
value="0.00138591070212997" />
<inertia
ixx="5.26294625624223E-08"
ixy="-3.9288013799604E-09"
ixz="2.43766268199571E-09"
iyy="4.82623724532942E-08"
iyz="3.71867754115443E-09"
izz="2.88555522060562E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
<material name="BlackAccent"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals_base2.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_thumb_cmc_yaw"
type="revolute">
<origin
xyz="0.011508 0.022975 0.032794"
rpy="0 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_thumb_metacarpals_base2" />
<axis
xyz="0 0 -1" />
<limit
lower="0"
upper="1.36"
effort="100"
velocity="1" />
</joint>
<link
name="rh_thumb_metacarpals">
<inertial>
<origin
xyz="4.24920612856275E-05 -1.67885666835232E-05 0.0228341522590467"
rpy="0 0 0" />
<mass
value="0.0123381607727595" />
<inertia
ixx="1.10292488693766E-06"
ixy="-6.14241939151719E-10"
ixz="1.14233108676795E-07"
iyy="1.76757658485787E-06"
iyz="-4.24576599367025E-10"
izz="9.36348143657406E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_metacarpals.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_thumb_cmc_pitch"
type="revolute">
<origin
xyz="0.0061649 0.010678 -0.004891"
rpy="0 -1.1529 -2.0944" />
<parent
link="rh_thumb_metacarpals_base2" />
<child
link="rh_thumb_metacarpals" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="0.58"
effort="100"
velocity="1" />
</joint>
<link
name="rh_thumb_distal">
<inertial>
<origin
xyz="-0.00413690727496815 -4.54842793429222E-07 0.0230097179163771"
rpy="0 0 0" />
<mass
value="0.00763302603723749" />
<inertia
ixx="1.30546842791578E-06"
ixy="-2.61617511029467E-12"
ixz="7.19030685242407E-08"
iyy="1.22457212713176E-06"
iyz="1.66931068485225E-11"
izz="2.7729897944409E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/thumb_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_thumb_ip"
type="revolute">
<origin
xyz="0.0037776 0 0.045368"
rpy="0 0 0" />
<parent
link="rh_thumb_metacarpals" />
<child
link="rh_thumb_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.08"
effort="100"
velocity="1" />
<mimic
joint="rh_thumb_cmc_pitch"
multiplier="1.86"
offset="0" />
</joint>
<link
name="rh_index_proximal">
<inertial>
<origin
xyz="-0.00208665596033792 -4.8964990644329E-06 0.0172736718900068"
rpy="0 0 0" />
<mass
value="0.00253178529301663" />
<inertia
ixx="2.65990159539482E-07"
ixy="-3.55165625722842E-11"
ixz="4.31046335852167E-08"
iyy="2.72915460277804E-07"
iyz="1.75935978708959E-10"
izz="8.56026306687528E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_index_mcp_pitch"
type="revolute">
<origin
xyz="0.0024758 0.02419 0.098779"
rpy="-0.05236 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_index_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.60"
effort="100"
velocity="1" />
</joint>
<link
name="rh_index_distal">
<inertial>
<origin
xyz="0.00658940011630532 -4.59976557022623E-06 0.0194434136420913"
rpy="0 0 0" />
<mass
value="0.00376787129082313" />
<inertia
ixx="3.22116619426455E-07"
ixy="1.03152735699049E-11"
ixz="-1.14363506430985E-07"
iyy="3.65131844686317E-07"
iyz="-1.97263639269362E-10"
izz="1.24256890612907E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/index_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_index_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="rh_index_proximal" />
<child
link="rh_index_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.43"
effort="100"
velocity="1" />
<mimic
joint="rh_index_mcp_pitch"
multiplier="0.89"
offset="0" />
</joint>
<link
name="rh_middle_proximal">
<inertial>
<origin
xyz="-0.00208661081773556 -4.93451838476378E-06 0.0172736805321904"
rpy="0 0 0" />
<mass
value="0.00253175497594429" />
<inertia
ixx="2.65989683690695E-07"
ixy="-3.58343951791586E-11"
ixz="4.31048601085216E-08"
iyy="2.72915006047006E-07"
iyz="1.75718024412593E-10"
izz="8.56020467202518E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_middle_mcp_pitch"
type="revolute">
<origin
xyz="0.00052576 0.00634 0.1027"
rpy="0 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_middle_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.60"
effort="100"
velocity="1" />
</joint>
<link
name="rh_middle_distal">
<inertial>
<origin
xyz="0.00658940304809306 -4.5885743593238E-06 0.0194434523296449"
rpy="0 0 0" />
<mass
value="0.00376786259758948" />
<inertia
ixx="3.221153658306E-07"
ixy="1.02639617308432E-11"
ixz="-1.14363547272711E-07"
iyy="3.65130723083104E-07"
iyz="-1.96904618797577E-10"
izz="1.24256787913797E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/middle_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_middle_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="rh_middle_proximal" />
<child
link="rh_middle_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.43"
effort="100"
velocity="1" />
<mimic
joint="rh_middle_mcp_pitch"
multiplier="0.89"
offset="0" />
</joint>
<link
name="rh_ring_proximal">
<inertial>
<origin
xyz="-0.00208654311325356 -4.98538564003281E-06 0.0172736743497407"
rpy="0 0 0" />
<mass
value="0.00253170963529411" />
<inertia
ixx="2.6598913795678E-07"
ixy="-3.62757108675266E-11"
ixz="4.31050611555507E-08"
iyy="2.72914417061517E-07"
iyz="1.75554984053478E-10"
izz="8.56012458677515E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_ring_mcp_pitch"
type="revolute">
<origin
xyz="0.0010258 -0.011135 0.098767"
rpy="0.05236 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_ring_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.60"
effort="100"
velocity="1" />
</joint>
<link
name="rh_ring_distal">
<inertial>
<origin
xyz="0.00658940273069729 -4.56628863591824E-06 0.0194435005686854"
rpy="0 0 0" />
<mass
value="0.0037678395356105" />
<inertia
ixx="3.22112632437858E-07"
ixy="1.01848408913558E-11"
ixz="-1.1436348915342E-07"
iyy="3.65128262095686E-07"
iyz="-1.96145632992445E-10"
izz="1.24256284608064E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/ring_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_ring_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="rh_ring_proximal" />
<child
link="rh_ring_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.43"
effort="100"
velocity="1" />
<mimic
joint="rh_ring_mcp_pitch"
multiplier="0.89"
offset="0" />
</joint>
<link
name="rh_pinky_proximal">
<inertial>
<origin
xyz="-0.00208649034130813 -5.02314582560837E-06 0.0172736680793697"
rpy="0 0 0" />
<mass
value="0.00253167400540778" />
<inertia
ixx="2.65988718820404E-07"
ixy="-3.66012295280295E-11"
ixz="4.31052220484445E-08"
iyy="2.72913936904813E-07"
iyz="1.75447396222308E-10"
izz="8.56006424720129E-08" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_proximal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_proximal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_pinky_mcp_pitch"
type="revolute">
<origin
xyz="0.0024758 -0.028372 0.092741"
rpy="0.087266 0 0" />
<parent
link="rh_hand_base_link" />
<child
link="rh_pinky_proximal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.60"
effort="100"
velocity="1" />
</joint>
<link
name="rh_pinky_distal">
<inertial>
<origin
xyz="0.00658940431519205 -4.55268314210333E-06 0.0194435341098305"
rpy="0 0 0" />
<mass
value="0.00376782669873412" />
<inertia
ixx="3.22111112339498E-07"
ixy="1.01707985908333E-11"
ixz="-1.14363524419061E-07"
iyy="3.65126916899176E-07"
iyz="-1.95589215866356E-10"
izz="1.24256004854426E-07" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
<material name="silver"/>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="meshes/pinky_distal.STL" />
</geometry>
</collision>
</link>
<joint
name="rh_pinky_dip"
type="revolute">
<origin
xyz="-0.0052516 0 0.036625"
rpy="0 0 0" />
<parent
link="rh_pinky_proximal" />
<child
link="rh_pinky_distal" />
<axis
xyz="0 1 0" />
<limit
lower="0"
upper="1.43"
effort="100"
velocity="1" />
<mimic
joint="rh_pinky_mcp_pitch"
multiplier="0.89"
offset="0" />
</joint>
</robot>
+1
View File
@@ -0,0 +1 @@
../../outputs/reports/O6
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_o6_left_mujoco_plan.sh" "$@"
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scripts/run_o6_right_mujoco_plan.sh" "$@"
+200
View File
@@ -0,0 +1,200 @@
#!/usr/bin/env bash
# 同时开两个 MuJoCo:O6 左手标定 vs 未标定 URDF
#
# 未标定: /home/lxqs/linkerhand-urdf/o6/left
# 标定: /home/lxqs/Linkerbot_URDF/06/LEFT(9/3 版,直接用 linker_hand_o6_left.xml)
#
# 两个窗口共用同一条 6 维 SDK 控制话题(默认 /cb_left_hand_control_cmd)。
#
# 用法:
# ./hands/O6/scripts/run_o6_left_dual_compare.sh
# ./hands/O6/scripts/run_o6_left_dual_compare.sh --pd-drive
# ./hands/O6/scripts/run_o6_left_dual_compare.sh --cmd-topic /cb_left_hand_control_cmd --speed 1.0
set -eo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
cd "$ROOT"
URDF_UNCALIBRATED="$ROOT/hands/O6/reference/left_uncalibrated.urdf"
URDF_CALIBRATED="$ROOT/hands/O6/reference/left_calibrated.urdf"
PKG_O6="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/O6/linker_hand_o6_left"
# 9/3 标定 MJCF:与 ./hands/O6/scripts/run_o6_left_mujoco_plan.sh 单开完全相同
CALIB_XML="$PKG_O6/linker_hand_o6_left.xml"
STOCK_XML="$PKG_O6/linker_hand_o6_left_stock.xml"
if [[ ! -f "$URDF_UNCALIBRATED" ]]; then
echo "[error] missing uncalibrated URDF: $URDF_UNCALIBRATED" >&2
exit 1
fi
if [[ ! -f "$URDF_CALIBRATED" ]]; then
echo "[error] missing calibrated URDF: $URDF_CALIBRATED" >&2
exit 1
fi
if [[ ! -f "$CALIB_XML" ]]; then
echo "[error] missing calibrated MJCF (9/3): $CALIB_XML" >&2
exit 1
fi
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}"
echo "[o6-dual] calibrated xml=$CALIB_XML (9/3, same as single-run)"
echo "[o6-dual] build uncalibrated MJCF from $URDF_UNCALIBRATED"
python3 - "$CALIB_XML" "$URDF_UNCALIBRATED" "$STOCK_XML" <<'PY'
import math
import re
import sys
from pathlib import Path
template_xml = Path(sys.argv[1])
urdf_uncal = Path(sys.argv[2])
out_uncal = Path(sys.argv[3])
joint_body = {"lh_thumb_cmc_yaw": "lh_thumb_metacarpals_base2"}
def parse_urdf_limits(path: Path) -> dict[str, dict]:
text = path.read_text()
joints: dict[str, dict] = {}
for m in re.finditer(r"<joint\b([^>]*)>(.*?)</joint>", text, re.S):
attrs, body = m.group(1), m.group(2)
name_m = re.search(r'name="([^"]+)"', attrs)
if not name_m:
continue
name = name_m.group(1)
jtype_m = re.search(r'type="([^"]+)"', attrs)
if not jtype_m or jtype_m.group(1) != "revolute":
continue
origin = re.search(r'<origin[^>]*xyz="([^"]+)"[^>]*rpy="([^"]+)"', body)
lim = re.search(r'lower="([^"]+)"[^>]*upper="([^"]+)"', body)
rpy = [float(x) for x in origin.group(2).split()] if origin else [0.0, 0.0, 0.0]
joints[name] = {
"lo": float(lim.group(1)),
"hi": float(lim.group(2)),
"rpy": rpy,
}
return joints
def apply_urdf_to_mjcf(template: str, urdf_path: Path, model_name: str) -> str:
joints = parse_urdf_limits(urdf_path)
xml = template.replace(
'model="linkerhand_o6_left"',
f'model="{model_name}"',
1,
)
for jname, info in joints.items():
lo, hi = info["lo"], info["hi"]
pat = rf'(<joint name="{re.escape(jname)}"[^>]*\srange=")([^"]+)(")'
xml = re.sub(
pat,
lambda m, lo=lo, hi=hi: f"{m.group(1)}{lo} {hi}{m.group(3)}",
xml,
count=1,
)
r, p, y = info["rpy"]
bname = joint_body.get(jname)
if bname and abs(r) + abs(p) + abs(y) > 1e-9:
w = math.cos(r * 0.5) * math.cos(p * 0.5) * math.cos(y * 0.5) + math.sin(
r * 0.5
) * math.sin(p * 0.5) * math.sin(y * 0.5)
x = math.sin(r * 0.5) * math.cos(p * 0.5) * math.cos(y * 0.5) - math.cos(
r * 0.5
) * math.sin(p * 0.5) * math.sin(y * 0.5)
yq = math.cos(r * 0.5) * math.sin(p * 0.5) * math.cos(y * 0.5) + math.sin(
r * 0.5
) * math.cos(p * 0.5) * math.sin(y * 0.5)
z = math.cos(r * 0.5) * math.cos(p * 0.5) * math.sin(y * 0.5) - math.sin(
r * 0.5
) * math.sin(p * 0.5) * math.cos(y * 0.5)
quat = f"{w:.9f} {x:.9f} {yq:.9f} {z:.9f}"
bpat = rf'(<body name="{re.escape(bname)}"[^>]*)(>)'
def add_quat(m, quat=quat):
tag = m.group(1)
if 'quat="' in tag:
tag = re.sub(r'quat="[^"]+"', f'quat="{quat}"', tag, count=1)
else:
tag = tag + f' quat="{quat}"'
return tag + m.group(2)
xml = re.sub(bpat, add_quat, xml, count=1)
elif bname:
bpat = rf'(<body name="{re.escape(bname)}"[^>]*)\squat="[^"]+"([^>]*>)'
xml = re.sub(bpat, r"\1\2", xml, count=1)
act_map = {
"lh_thumb_cmc_yaw_pos": "lh_thumb_cmc_yaw",
"lh_thumb_cmc_pitch_pos": "lh_thumb_cmc_pitch",
"lh_index_mcp_pitch_pos": "lh_index_mcp_pitch",
"lh_middle_mcp_pitch_pos": "lh_middle_mcp_pitch",
"lh_ring_mcp_pitch_pos": "lh_ring_mcp_pitch",
"lh_pinky_mcp_pitch_pos": "lh_pinky_mcp_pitch",
}
for act, jname in act_map.items():
lo, hi = joints[jname]["lo"], joints[jname]["hi"]
pat = rf'(<position name="{act}"[^>]*\sctrlrange=")([^"]+)(")'
xml = re.sub(
pat,
lambda m, lo=lo, hi=hi: f"{m.group(1)}{lo} {hi}{m.group(3)}",
xml,
count=1,
)
return xml
import mujoco
out_uncal.write_text(
apply_urdf_to_mjcf(template_xml.read_text(), urdf_uncal, "linkerhand_o6_left_uncalibrated")
)
mujoco.MjModel.from_xml_path(str(out_uncal))
print(f"[o6-dual] uncalibrated MJCF -> {out_uncal}")
PY
EXTRA=("$@")
cleanup() {
[[ -n "${PID_UNCAL:-}" ]] && kill "$PID_UNCAL" 2>/dev/null || true
[[ -n "${PID_CALIB:-}" ]] && kill "$PID_CALIB" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
echo "[o6-dual] launch uncalibrated window"
"$ROOT/hands/O6/scripts/run_o6_left_mujoco_plan.sh" \
--xml "$STOCK_XML" \
--mapping-urdf "$URDF_UNCALIBRATED" \
--node-name o6_left_uncalibrated \
--window-title "O6 left 未标定 (linkerhand-urdf)" \
--sim-state-topic /sim/mujoco/o6/left/uncalibrated/joint_state \
"${EXTRA[@]}" &
PID_UNCAL=$!
sleep 1
echo "[o6-dual] launch calibrated window (9/3 MJCF)"
"$ROOT/hands/O6/scripts/run_o6_left_mujoco_plan.sh" \
--xml "$CALIB_XML" \
--mapping-urdf "$URDF_CALIBRATED" \
--node-name o6_left_calibrated \
--window-title "O6 left 标定 (Linkerbot_URDF 9/3)" \
--sim-state-topic /sim/mujoco/o6/left/calibrated/joint_state \
"${EXTRA[@]}" &
PID_CALIB=$!
echo "[o6-dual] both started. same cmd topic (default /cb_left_hand_control_cmd)."
echo "[o6-dual] Ctrl+C stops both."
wait "$PID_UNCAL" "$PID_CALIB"
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# MuJoCo O6 左手仿真桥
# URDF/MJCF 来源: /home/lxqs/Linkerbot_URDF/06/LEFT(9/3 标定版)
# 默认订阅 /cb_left_hand_control_cmd(6 维 SDK 0~255)
# 发布 /sim/mujoco/o6/left/joint_state
#
# 用法:
# ./hands/O6/scripts/run_o6_left_mujoco_plan.sh
# ./hands/O6/scripts/run_o6_left_mujoco_plan.sh --pd-drive
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}"
XML="$PKG_SRC/linker_hand_mujoco_ros2/urdf/O6/linker_hand_o6_left/linker_hand_o6_left.xml"
URDF="$PKG_SRC/linker_hand_mujoco_ros2/urdf/O6/linker_hand_o6_left/linkerhand_o6_left.urdf"
if [[ ! -f "$XML" ]]; then
echo "[error] missing O6 left MJCF: $XML" >&2
exit 1
fi
echo "[o6-left] urdf=$URDF"
echo "[o6-left] xml=$XML"
echo "[o6-left] cmd-topic=/cb_left_hand_control_cmd"
echo "[o6-left] sim-state=/sim/mujoco/o6/left/joint_state"
echo "[o6-left] SDK 6ch: pitch, yaw, index, middle, ring, pinky"
exec python3 -m linker_hand_mujoco_ros2.o6_left_plan_bridge \
--xml "$XML" \
--node-name o6_mujoco_plan_bridge_left \
--window-title "O6 left" \
--cmd-topic /cb_left_hand_control_cmd \
--sim-state-topic /sim/mujoco/o6/left/joint_state \
--nominal-topic /retarget/o6/left/joint_target_nominal \
--mapping g20_urdf \
"$@"
+199
View File
@@ -0,0 +1,199 @@
#!/usr/bin/env bash
# 同时开两个 MuJoCo:O6 右手标定 vs 未标定 URDF
#
# 未标定: /home/lxqs/linkerhand-urdf/o6/right
# 标定: /home/lxqs/Linkerbot_URDF/06/RIGHT(直接用 linker_hand_o6_right.xml,与单开相同)
#
# 两个窗口共用同一条 6 维 SDK 控制话题(默认 /cb_right_hand_control_cmd)。
#
# 用法:
# ./hands/O6/scripts/run_o6_right_dual_compare.sh
# ./hands/O6/scripts/run_o6_right_dual_compare.sh --pd-drive
# ./hands/O6/scripts/run_o6_right_dual_compare.sh --cmd-topic /cb_right_hand_control_cmd --speed 1.0
set -eo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
cd "$ROOT"
URDF_UNCALIBRATED="$ROOT/hands/O6/reference/right_uncalibrated.urdf"
URDF_CALIBRATED="$ROOT/hands/O6/reference/right_calibrated.urdf"
PKG_O6="$ROOT/src/linkerhand-sim/linker_hand_mujoco_ros2/linker_hand_mujoco_ros2/urdf/O6/linker_hand_o6_right"
CALIB_XML="$PKG_O6/linker_hand_o6_right.xml"
STOCK_XML="$PKG_O6/linker_hand_o6_right_stock.xml"
if [[ ! -f "$URDF_UNCALIBRATED" ]]; then
echo "[error] missing uncalibrated URDF: $URDF_UNCALIBRATED" >&2
exit 1
fi
if [[ ! -f "$URDF_CALIBRATED" ]]; then
echo "[error] missing calibrated URDF: $URDF_CALIBRATED" >&2
exit 1
fi
if [[ ! -f "$CALIB_XML" ]]; then
echo "[error] missing calibrated MJCF: $CALIB_XML" >&2
exit 1
fi
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}"
echo "[o6-right-dual] calibrated xml=$CALIB_XML (same as single-run)"
echo "[o6-right-dual] build uncalibrated MJCF from $URDF_UNCALIBRATED"
python3 - "$CALIB_XML" "$URDF_UNCALIBRATED" "$STOCK_XML" <<'PY'
import math
import re
import sys
from pathlib import Path
template_xml = Path(sys.argv[1])
urdf_uncal = Path(sys.argv[2])
out_uncal = Path(sys.argv[3])
joint_body = {"rh_thumb_cmc_yaw": "rh_thumb_metacarpals_base2"}
def parse_urdf_limits(path: Path) -> dict[str, dict]:
text = path.read_text()
joints: dict[str, dict] = {}
for m in re.finditer(r"<joint\b([^>]*)>(.*?)</joint>", text, re.S):
attrs, body = m.group(1), m.group(2)
name_m = re.search(r'name="([^"]+)"', attrs)
if not name_m:
continue
name = name_m.group(1)
jtype_m = re.search(r'type="([^"]+)"', attrs)
if not jtype_m or jtype_m.group(1) != "revolute":
continue
origin = re.search(r'<origin[^>]*xyz="([^"]+)"[^>]*rpy="([^"]+)"', body)
lim = re.search(r'lower="([^"]+)"[^>]*upper="([^"]+)"', body)
rpy = [float(x) for x in origin.group(2).split()] if origin else [0.0, 0.0, 0.0]
joints[name] = {
"lo": float(lim.group(1)),
"hi": float(lim.group(2)),
"rpy": rpy,
}
return joints
def rpy_to_quat(r: float, p: float, y: float) -> tuple[float, float, float, float]:
cr, sr = math.cos(r * 0.5), math.sin(r * 0.5)
cp, sp = math.cos(p * 0.5), math.sin(p * 0.5)
cy, sy = math.cos(y * 0.5), math.sin(y * 0.5)
w = cr * cp * cy + sr * sp * sy
x = sr * cp * cy - cr * sp * sy
yq = cr * sp * cy + sr * cp * sy
z = cr * cp * sy - sr * sp * cy
return w, x, yq, z
def apply_urdf_to_mjcf(template: str, urdf_path: Path, model_name: str) -> str:
joints = parse_urdf_limits(urdf_path)
xml = template.replace(
'model="linkerhand_o6_right"',
f'model="{model_name}"',
1,
)
for jname, info in joints.items():
lo, hi = info["lo"], info["hi"]
pat = rf'(<joint name="{re.escape(jname)}"[^>]*\srange=")([^"]+)(")'
xml = re.sub(
pat,
lambda m, lo=lo, hi=hi: f"{m.group(1)}{lo} {hi}{m.group(3)}",
xml,
count=1,
)
r, p, y = info["rpy"]
bname = joint_body.get(jname)
if bname and abs(r) + abs(p) + abs(y) > 1e-9:
w, x, yq, z = rpy_to_quat(r, p, y)
quat = f"{w:.9f} {x:.9f} {yq:.9f} {z:.9f}"
bpat = rf'(<body name="{re.escape(bname)}"[^>]*)(>)'
def add_quat(m, quat=quat):
tag = m.group(1)
if 'quat="' in tag:
tag = re.sub(r'quat="[^"]+"', f'quat="{quat}"', tag, count=1)
else:
tag = tag + f' quat="{quat}"'
return tag + m.group(2)
xml = re.sub(bpat, add_quat, xml, count=1)
elif bname:
bpat = rf'(<body name="{re.escape(bname)}"[^>]*)\squat="[^"]+"([^>]*>)'
xml = re.sub(bpat, r"\1\2", xml, count=1)
act_map = {
"rh_thumb_cmc_yaw_pos": "rh_thumb_cmc_yaw",
"rh_thumb_cmc_pitch_pos": "rh_thumb_cmc_pitch",
"rh_index_mcp_pitch_pos": "rh_index_mcp_pitch",
"rh_middle_mcp_pitch_pos": "rh_middle_mcp_pitch",
"rh_ring_mcp_pitch_pos": "rh_ring_mcp_pitch",
"rh_pinky_mcp_pitch_pos": "rh_pinky_mcp_pitch",
}
for act, jname in act_map.items():
lo, hi = joints[jname]["lo"], joints[jname]["hi"]
pat = rf'(<position name="{act}"[^>]*\sctrlrange=")([^"]+)(")'
xml = re.sub(
pat,
lambda m, lo=lo, hi=hi: f"{m.group(1)}{lo} {hi}{m.group(3)}",
xml,
count=1,
)
return xml
import mujoco
out_uncal.write_text(
apply_urdf_to_mjcf(template_xml.read_text(), urdf_uncal, "linkerhand_o6_right_uncalibrated")
)
mujoco.MjModel.from_xml_path(str(out_uncal))
print(f"[o6-right-dual] uncalibrated MJCF -> {out_uncal}")
PY
EXTRA=("$@")
cleanup() {
[[ -n "${PID_UNCAL:-}" ]] && kill "$PID_UNCAL" 2>/dev/null || true
[[ -n "${PID_CALIB:-}" ]] && kill "$PID_CALIB" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
echo "[o6-right-dual] launch uncalibrated window"
"$ROOT/hands/O6/scripts/run_o6_right_mujoco_plan.sh" \
--xml "$STOCK_XML" \
--mapping-urdf "$URDF_UNCALIBRATED" \
--node-name o6_right_uncalibrated \
--window-title "O6 right 未标定 (linkerhand-urdf)" \
--sim-state-topic /sim/mujoco/o6/right/uncalibrated/joint_state \
"${EXTRA[@]}" &
PID_UNCAL=$!
sleep 1
echo "[o6-right-dual] launch calibrated window"
"$ROOT/hands/O6/scripts/run_o6_right_mujoco_plan.sh" \
--xml "$CALIB_XML" \
--mapping-urdf "$URDF_CALIBRATED" \
--node-name o6_right_calibrated \
--window-title "O6 right 标定 (Linkerbot_URDF)" \
--sim-state-topic /sim/mujoco/o6/right/calibrated/joint_state \
"${EXTRA[@]}" &
PID_CALIB=$!
echo "[o6-right-dual] both started. same cmd topic (default /cb_right_hand_control_cmd)."
echo "[o6-right-dual] Ctrl+C stops both."
wait "$PID_UNCAL" "$PID_CALIB"
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# MuJoCo O6 右手仿真桥
# URDF 来源: /home/lxqs/下载/linkerhand_o6_right_O6_RIGHT_001_partial_zero_calibrated+copy.urdf
# 默认订阅 /cb_right_hand_control_cmd(与 SDK / GUI 一致,6 维 SDK 0~255)
# 发布 /sim/mujoco/o6/right/joint_state
#
# 用法:
# ./hands/O6/scripts/run_o6_right_mujoco_plan.sh
# ./hands/O6/scripts/run_o6_right_mujoco_plan.sh --pd-drive
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}"
XML="$PKG_SRC/linker_hand_mujoco_ros2/urdf/O6/linker_hand_o6_right/linker_hand_o6_right.xml"
URDF="$PKG_SRC/linker_hand_mujoco_ros2/urdf/O6/linker_hand_o6_right/linkerhand_o6_right.urdf"
if [[ ! -f "$XML" ]]; then
echo "[error] missing O6 MJCF: $XML" >&2
exit 1
fi
echo "[o6-right] urdf=$URDF"
echo "[o6-right] xml=$XML"
echo "[o6-right] cmd-topic=/cb_right_hand_control_cmd"
echo "[o6-right] sim-state=/sim/mujoco/o6/right/joint_state"
echo "[o6-right] SDK 6ch: pitch, yaw, index, middle, ring, pinky"
exec python3 -m linker_hand_mujoco_ros2.o6_plan_bridge \
--xml "$XML" \
--node-name o6_mujoco_plan_bridge_right \
--window-title "O6 right" \
--cmd-topic /cb_right_hand_control_cmd \
--sim-state-topic /sim/mujoco/o6/right/joint_state \
--nominal-topic /retarget/o6/right/joint_target_nominal \
--mapping g20_urdf \
"$@"
+69
View File
@@ -0,0 +1,69 @@
# 按手型启动 MuJoCo
这里是新的日常使用入口。根目录统一使用 `run.sh`;旧 `run_*.sh` 已归档到 `archive/launchers/`。URDF、网格不复制成第二份运行资产:各目录的 `assets` 是指向包内原资产的相对符号链接。
## 启动
在工程根目录执行:
```bash
./hands/G20/run_left.sh
./hands/G20/run_right.sh
./hands/O6/run_left.sh
./hands/O6/run_right.sh
./hands/L6/run_left.sh
./hands/L6/run_right.sh
./hands/O12/run_right.sh
./hands/O30/run_left.sh
./hands/O30/run_right.sh
```
常用双窗入口为各手型的 `compare_left.sh` / `compare_right.sh`。完整实现及关节监视脚本放在各手型 `scripts/` 中。G20/O6/L6 双窗所需原始对照 URDF 已快照到各自 `reference/`,来源和散列见 `reference_sources.json`;这些文件用于配合包内模板生成对照窗口,不是独立网格资产包。G20 的 L20 对照改用包内已有资产,O12 对照改用工程内 `assets/vendor/Linkerbot_URDF/012/right`。O12 当前只有右手入口。
`hands/L20_V10.1/assets` 是 G20 双窗对照所用的原版 L20 模型。
O30 V5~V9 旧入口放在 `hands/O30/legacy`;历史 URDF 保留,但历史主动分段映射不再执行。
`hands/legacy/run_sim.sh` 保留旧 ROS launch 接口;日常使用优先选择上方按手型的桥接器。
## 映射规则
- 主动关节不查 JSON 角度表,使用实际加载的 URDF 派生 MJCF 关节限位线性换算。通道顺序、正反方向属于 SDK 协议,继续保留。
- G20/O6/L6/O30 的 SDK 输入仍为 0~255;O12 `/o12/right/joint_cmd` 仍为 12 路 SDK 弧度,不要发送 0~255 数组。
- 被动关节默认读取对应 URDF 的 `mimic` 比例和偏移。
- G20 左手默认额外使用 `G20/calibration/left_passive.json`:文件只包含被动关节相对于主关节角度的曲线,没有主动命令查表。缺失时明确提示并回退到 URDF mimic。
- G20 右手保持默认 URDF 线性 mimic;如需现有右手被动曲线,可以显式指定 `--passive-json`。
- O30 没有被动关节,保持全主动线性;V10 几何和关节参数未修改。
- 运行时不再因为导入公共函数而读取 G20 JSON。其余手型不需要 G20 的标定文件。
G20 被动曲线开关示例:
```bash
# 左手全部采用 URDF 线性关系,包括被动关节
./hands/G20/run_left.sh --passive-json none
# 右手主动线性,被动使用已有标定联动曲线
./hands/G20/run_right.sh --passive-json "$PWD/hands/G20/calibration/right_passive.json"
```
旧 `--mapping g20_calibrated` 等名称作为兼容别名保留,含义已调整为“主动线性 + 可选被动曲线”,不再代表主动查表。切换后 G20 左手同一 SDK 指令的姿态可能不同于旧标定查表结果。
## 资产与配置
`assets` 中有历史版本;正常单窗使用所在手型当前 MJCF 和对应的标准 URDF 文件,不按修改时间猜测“最新版本”。O30 当前仍由其启动脚本从指定 V10 URDF 生成模型。
其他手型现有单窗读取已经生成的 MJCF;修改 URDF 后需同步生成 MJCF,不能只改 URDF 文本就期待已有 XML 自动变化。此次已核对常用 SDK 手型当前 URDF 与 MJCF 关节限位一致。
自定义/双窗模型通过 `--mapping-urdf PATH` 明确指定 passive mimic 来源;主动限位始终与实际仿真的模型一致。双窗脚本已传入各自 URDF,避免套用另一窗口的联动比例。O12 旧 `--o12-urdf` 参数继续兼容。
被动 JSON 的精简格式为 `schema_version: 1`、`passive_maps`;每个条目包含 `master`、`master_rad`、`passive_rad`。只允许描述 URDF 中的被动关节。旧 JSON 的显式导入仍可用于转换被动联动:其中主关节曲线只用作被动曲线横坐标,绝不用于主动命令映射。
## 备份与测试
本轮前的启动脚本、Python 驱动及完整包内 URDF/网格保存在:
`archive/backups/before_hand_layout_20260928_102428/`。
历史测试文件也已保存。备份用于恢复旧行为,不建议与新映射代码混用。
```bash
PYTHONDONTWRITEBYTECODE=1 venv/bin/python -B tests/test_hand_mapping_policy.py
source /opt/ros/jazzy/setup.bash
PYTHONPATH="$PWD/venv/lib/python3.12/site-packages:$PYTHONPATH" python3 tools/o30_right_calibration/test_calibration.py
```
本轮是驱动与目录整理,不是重新标定真机。没有向真机话题发指令。
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# 本机启动左手 O6 MuJoCo 仿真(ROS2 用系统 Python,依赖从 venv/user site 补齐)
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT"
# ROS2
# shellcheck disable=SC1091
source /opt/ros/jazzy/setup.bash
# shellcheck disable=SC1091
source "$ROOT/install/setup.bash"
# 多机通信
export ROS_DOMAIN_ID="${ROS_DOMAIN_ID:-30}"
export ROS_LOCALHOST_ONLY=0
# ros2 launch 走系统 python3;把 venv 的 site-packages 挂上(含 mujoco/PyQt5)
VENV_SITE="$ROOT/venv/lib/python3.12/site-packages"
if [[ -d "$VENV_SITE" ]]; then
export PYTHONPATH="$VENV_SITE${PYTHONPATH:+:$PYTHONPATH}"
fi
exec ros2 launch linker_hand_mujoco_ros2 linker_hand_mujoco_ros2.launch.py "$@"
+42
View File
@@ -0,0 +1,42 @@
{
"hands/O6/reference/left_uncalibrated.urdf": {
"source": "/home/lxqs/linkerhand-urdf/o6/left/linkerhand_o6_left.urdf",
"sha256": "0bc0162a9dc0c2d57f68477a67e5ba88d1baf0b67eb8ad2de8556ed7b62114de"
},
"hands/O6/reference/left_calibrated.urdf": {
"source": "/home/lxqs/Linkerbot_URDF/06/LEFT/linkerhand_o6_left.urdf",
"sha256": "35b6a350119fc4c8162f8a1c40aa515e6a9f0562e8349cfcc0214c7dd02a884e"
},
"hands/O6/reference/right_uncalibrated.urdf": {
"source": "/home/lxqs/linkerhand-urdf/o6/right/linkerhand_o6_right.urdf",
"sha256": "8f184faad699fbf771e388f109a4e8793b5cb190c33a87b2eba8491a3a37dd62"
},
"hands/O6/reference/right_calibrated.urdf": {
"source": "/home/lxqs/Linkerbot_URDF/06/RIGHT/linkerhand_o6_right.urdf",
"sha256": "aa07cb443d085dade54069cd487c3d1bfc4aac9da54f9880adf33c1923a43aaa"
},
"hands/L6/reference/left_uncalibrated.urdf": {
"source": "/home/lxqs/linkerhand-urdf/l6/left/linkerhand_l6v3.1_left.urdf",
"sha256": "b7d83f5423c64d62c50bf5ca73f96210e2032b9531c816a3955bab7a51d13c54"
},
"hands/L6/reference/left_calibrated.urdf": {
"source": "/home/lxqs/Linkerbot_URDF/L6/LEFT/linkerhand_l6_left.urdf",
"sha256": "23110486b970604ca6d88bb29a5e6543d3f66361ce528cf767cbe41e6751443c"
},
"hands/L6/reference/right_uncalibrated.urdf": {
"source": "/home/lxqs/linkerhand-urdf/l6/right/linkerhand_l6v3.1_right.urdf",
"sha256": "298c1fbf5189648911426f530b50bdbeea4830cab9c54e20f46c532485df4666"
},
"hands/L6/reference/right_calibrated.urdf": {
"source": "/home/lxqs/Linkerbot_URDF/L6/RIGHT/linkerhand_l6_right.urdf",
"sha256": "614fc9df0d44fa0cc66770b0361c5037fc56c4dd5e59176204f9b29dc99b387d"
},
"hands/G20/reference/left_calibrated.urdf": {
"source": "/home/lxqs/图片/linkerhand_g20_left_tag.urdf",
"sha256": "93ef571430a15a229f221b53c09747c1c94a5c8d228ababd979c717f1d5f1a5a"
},
"hands/G20/reference/right_calibrated.urdf": {
"source": "/home/lxqs/音乐/linkerhand_G20_RIGHT_tag.urdf",
"sha256": "d46d3ca2f51f8932854f5090d0bf7f813ffa41501f68e534fc4f760e959cb496"
}
}