refactor: rename calibration package

This commit is contained in:
lxp
2026-08-31 18:06:15 +08:00
parent 06c050e446
commit 0d606c2ba2
74 changed files with 225 additions and 106 deletions
@@ -1,4 +0,0 @@
[develop]
script_dir=$base/lib/g20_thumb_apriltag_calibration
[install]
install_scripts=$base/lib/g20_thumb_apriltag_calibration
@@ -1,17 +1,20 @@
# G20 左右手 AprilTag 标定 # LinkerHand 专业标定
## G20右手正式一键标定 ## G20右手正式一键标定
固定三相机和19张Tag安装完成后,用户只运行: 固定三相机和19张Tag安装完成后,用户只运行:
```bash ```bash
ros2 run g20_thumb_apriltag_calibration calibrate_g20_right ros2 run linkerhand_calibration calibrate_hand
``` ```
旧 executable `calibrate_g20_right` 在本发行版内保留为同一入口的别名;
旧 ROS 包名前缀不再提供。新脚本和部署配置统一使用 `calibrate_hand`
完全独立地只标定大拇指4项任务时,使用: 完全独立地只标定大拇指4项任务时,使用:
```bash ```bash
ros2 run g20_thumb_apriltag_calibration calibrate_g20_right \ ros2 run linkerhand_calibration calibrate_hand \
--scope thumb --scope thumb
``` ```
@@ -23,7 +26,7 @@ ros2 run g20_thumb_apriltag_calibration calibrate_g20_right \
如果确实需要把新的拇指结果合并到一份已经通过的完整整手标定,才额外使用: 如果确实需要把新的拇指结果合并到一份已经通过的完整整手标定,才额外使用:
```bash ```bash
ros2 run g20_thumb_apriltag_calibration calibrate_g20_right \ ros2 run linkerhand_calibration calibrate_hand \
--scope thumb \ --scope thumb \
--base-session calibration_output/G20_RIGHT_001/latest_passed --base-session calibration_output/G20_RIGHT_001/latest_passed
``` ```
@@ -35,7 +38,7 @@ ros2 run g20_thumb_apriltag_calibration calibrate_g20_right \
只重新采集12项四指任务并合成完整整手URDF: 只重新采集12项四指任务并合成完整整手URDF:
```bash ```bash
ros2 run g20_thumb_apriltag_calibration calibrate_g20_right \ ros2 run linkerhand_calibration calibrate_hand \
--scope fingers \ --scope fingers \
--base-session calibration_output/G20_RIGHT_001/<已通过的拇指会话时间戳> --base-session calibration_output/G20_RIGHT_001/<已通过的拇指会话时间戳>
``` ```
@@ -114,7 +117,7 @@ schema v4 JSON,不再生成schema v5运行文件。
`tag_layout:=legacy_11`,两套配置和结果schema互不覆盖: `tag_layout:=legacy_11`,两套配置和结果schema互不覆盖:
```bash ```bash
ros2 launch g20_thumb_apriltag_calibration three_camera_calibration.launch.py \ ros2 launch linkerhand_calibration three_camera_calibration.launch.py \
hand_type:=right \ hand_type:=right \
tag_layout:=g20_right_19 \ tag_layout:=g20_right_19 \
serial_number:=G20_RIGHT_001 \ serial_number:=G20_RIGHT_001 \
@@ -271,7 +274,7 @@ ID 9 必须在拇指横摆的完整行程中持续可见。
```bash ```bash
mkdir -p /home/lxp/projects/linkerhand_retarget_ros2/config mkdir -p /home/lxp/projects/linkerhand_retarget_ros2/config
ros2 launch g20_thumb_apriltag_calibration \ ros2 launch linkerhand_calibration \
three_camera_extrinsics.launch.py \ three_camera_extrinsics.launch.py \
output_file:=/home/lxp/projects/linkerhand_retarget_ros2/config/g20_three_camera_extrinsics.yaml \ output_file:=/home/lxp/projects/linkerhand_retarget_ros2/config/g20_three_camera_extrinsics.yaml \
checkerboard_columns:=8 checkerboard_rows:=5 square_size_m:=0.027 checkerboard_columns:=8 checkerboard_rows:=5 square_size_m:=0.027
@@ -312,7 +315,7 @@ ros2 service call /g20_camera_extrinsics/save std_srvs/srv/Trigger {}
先使用禁止运动模式检查三个机位、外参、内参和标签: 先使用禁止运动模式检查三个机位、外参、内参和标签:
```bash ```bash
ros2 launch g20_thumb_apriltag_calibration \ ros2 launch linkerhand_calibration \
three_camera_calibration.launch.py \ three_camera_calibration.launch.py \
hand_type:=left \ hand_type:=left \
serial_number:=G20_LEFT_001 \ serial_number:=G20_LEFT_001 \
@@ -335,7 +338,7 @@ ros2 run image_view image_view --ros-args \
确认全行程安全、MVS客户端已关闭且没有其他命令发布者后,重启正式流程: 确认全行程安全、MVS客户端已关闭且没有其他命令发布者后,重启正式流程:
```bash ```bash
ros2 launch g20_thumb_apriltag_calibration \ ros2 launch linkerhand_calibration \
three_camera_calibration.launch.py \ three_camera_calibration.launch.py \
hand_type:=left \ hand_type:=left \
serial_number:=G20_LEFT_001 \ serial_number:=G20_LEFT_001 \
@@ -347,7 +350,7 @@ ros2 launch g20_thumb_apriltag_calibration \
右手使用同一入口;默认自动选择右手SDK话题和原始URDF: 右手使用同一入口;默认自动选择右手SDK话题和原始URDF:
```bash ```bash
ros2 launch g20_thumb_apriltag_calibration \ ros2 launch linkerhand_calibration \
three_camera_calibration.launch.py \ three_camera_calibration.launch.py \
hand_type:=right \ hand_type:=right \
serial_number:=G20_RIGHT_001 \ serial_number:=G20_RIGHT_001 \
@@ -526,7 +529,7 @@ calibration_output/G20_RIGHT_001/<时间戳>/
命令。`--output-tag` 为新产物增加安全后缀,已有JSON、URDF和验证报告不会被覆盖: 命令。`--output-tag` 为新产物增加安全后缀,已有JSON、URDF和验证报告不会被覆盖:
```bash ```bash
python3 -m g20_thumb_apriltag_calibration.offline_replay \ python3 -m linkerhand_calibration.offline_replay \
calibration_output/G20_RIGHT_001/20260811_120146 \ calibration_output/G20_RIGHT_001/20260811_120146 \
--output-tag AXIS_FRAME_V3 \ --output-tag AXIS_FRAME_V3 \
--write --write
@@ -543,7 +546,7 @@ python3 -m g20_thumb_apriltag_calibration.offline_replay \
`JointState`(包括5个被动关节): `JointState`(包括5个被动关节):
```bash ```bash
ros2 launch g20_thumb_apriltag_calibration calibrated_joint_state_bridge.launch.py \ ros2 launch linkerhand_calibration calibrated_joint_state_bridge.launch.py \
hand_type:=right \ hand_type:=right \
calibration_file:=$PWD/calibration_output/G20_RIGHT_001/20260811_120146/g20_right_G20_RIGHT_001_calibration.json calibration_file:=$PWD/calibration_output/G20_RIGHT_001/20260811_120146/g20_right_G20_RIGHT_001_calibration.json
``` ```
@@ -615,7 +618,7 @@ sudo apt-get install -y \
cd /home/lxp/projects/linkerhand_retarget_ros2 cd /home/lxp/projects/linkerhand_retarget_ros2
source /opt/ros/jazzy/setup.bash source /opt/ros/jazzy/setup.bash
colcon build --symlink-install \ colcon build --symlink-install \
--packages-select linker_hand_ros2_sdk g20_thumb_apriltag_calibration --packages-select linker_hand_ros2_sdk linkerhand_calibration
source install/setup.bash source install/setup.bash
``` ```
@@ -642,7 +645,7 @@ source install/setup.bash
先单独启动相机(不会连接机械手,也不会发送关节命令): 先单独启动相机(不会连接机械手,也不会发送关节命令):
```bash ```bash
ros2 run g20_thumb_apriltag_calibration hikrobot_camera_node --ros-args \ ros2 run linkerhand_calibration hikrobot_camera_node --ros-args \
--remap __ns:=/camera/camera/color \ --remap __ns:=/camera/camera/color \
-p serial_number:=DB2163742 \ -p serial_number:=DB2163742 \
-p camera_info_url:=$HOME/.ros/camera_info/hikrobot_DB2163742.yaml -p camera_info_url:=$HOME/.ros/camera_info/hikrobot_DB2163742.yaml
@@ -680,7 +683,7 @@ ros2 run camera_calibration cameracalibrator \
但标定节点不会发送位置运动命令,也不会允许解锁全行程扫描: 但标定节点不会发送位置运动命令,也不会允许解锁全行程扫描:
```bash ```bash
ros2 launch g20_thumb_apriltag_calibration front_thumb_calibration.launch.py \ ros2 launch linkerhand_calibration front_thumb_calibration.launch.py \
serial_number:=G20_LEFT_001 \ serial_number:=G20_LEFT_001 \
camera_serial_number:=DB2163742 \ camera_serial_number:=DB2163742 \
commands_enabled:=false commands_enabled:=false
@@ -691,7 +694,7 @@ ros2 launch g20_thumb_apriltag_calibration front_thumb_calibration.launch.py \
速度,并使用单终点连续运动: 速度,并使用单终点连续运动:
```bash ```bash
ros2 launch g20_thumb_apriltag_calibration front_thumb_calibration.launch.py \ ros2 launch linkerhand_calibration front_thumb_calibration.launch.py \
serial_number:=G20_LEFT_001 \ serial_number:=G20_LEFT_001 \
camera_serial_number:=DB2163742 \ camera_serial_number:=DB2163742 \
can_interface:=can0 \ can_interface:=can0 \
@@ -804,7 +807,7 @@ calibration_output/<序列号>/<时间戳>/
恢复时必须显式复用原目录,否则会创建新会话: 恢复时必须显式复用原目录,否则会创建新会话:
```bash ```bash
ros2 launch g20_thumb_apriltag_calibration front_thumb_calibration.launch.py \ ros2 launch linkerhand_calibration front_thumb_calibration.launch.py \
serial_number:=G20_LEFT_001 \ serial_number:=G20_LEFT_001 \
session_dir:=/绝对路径/calibration_output/G20_LEFT_001/20260727_120000 session_dir:=/绝对路径/calibration_output/G20_LEFT_001/20260727_120000
``` ```
@@ -884,7 +887,7 @@ for name, joint in data["joints"].items():
URDF URDF
```bash ```bash
ros2 launch g20_thumb_apriltag_calibration \ ros2 launch linkerhand_calibration \
front_cmc_pitch_zero.launch.py \ front_cmc_pitch_zero.launch.py \
serial_number:=G20_LEFT_001 serial_number:=G20_LEFT_001
``` ```
@@ -961,7 +964,7 @@ zero_angles.table_projected_zero_rad
Roll同样固定使用“T3中心→拟合圆心”的内向径向矢量,不读取T3标签朝向。 Roll同样固定使用“T3中心→拟合圆心”的内向径向矢量,不读取T3标签朝向。
```bash ```bash
ros2 launch g20_thumb_apriltag_calibration \ ros2 launch linkerhand_calibration \
front_cmc_roll_calibration.launch.py \ front_cmc_roll_calibration.launch.py \
serial_number:=G20_LEFT_001 serial_number:=G20_LEFT_001
``` ```
@@ -0,0 +1,21 @@
"""One-release compatibility surface for the former Python package name.
New code must import :mod:`linkerhand_calibration`. Only the documented
configuration loader is re-exported here; calibration algorithms continue to
have a single implementation in the renamed package.
"""
from __future__ import annotations
import warnings
warnings.warn(
"g20_thumb_apriltag_calibration is deprecated; "
"import linkerhand_calibration instead",
DeprecationWarning,
stacklevel=2,
)
from linkerhand_calibration.product import ProductConfig, load_product_config
__all__ = ["ProductConfig", "load_product_config"]
@@ -0,0 +1,9 @@
"""Deprecated forwarding entry point for the runtime joint-state bridge."""
from linkerhand_calibration.calibrated_joint_state_bridge import main
__all__ = ["main"]
if __name__ == "__main__":
main()
@@ -0,0 +1,9 @@
"""Deprecated forwarding entry point for offline replay."""
from linkerhand_calibration.offline_replay import main
__all__ = ["main"]
if __name__ == "__main__":
main()
@@ -0,0 +1,9 @@
"""Deprecated forwarding entry point for the former Python package."""
from linkerhand_calibration.one_command import main
__all__ = ["main"]
if __name__ == "__main__":
main()
@@ -14,7 +14,7 @@ def generate_launch_description() -> LaunchDescription:
DeclareLaunchArgument("input_topic", default_value=""), DeclareLaunchArgument("input_topic", default_value=""),
DeclareLaunchArgument("output_topic", default_value=""), DeclareLaunchArgument("output_topic", default_value=""),
Node( Node(
package="g20_thumb_apriltag_calibration", package="linkerhand_calibration",
executable="calibrated_joint_state_bridge", executable="calibrated_joint_state_bridge",
name=[ name=[
"g20_calibrated_joint_state_bridge_", "g20_calibrated_joint_state_bridge_",
@@ -48,7 +48,7 @@ def _launch_stack(context):
tag_config = LaunchConfiguration("tag_config").perform(context) tag_config = LaunchConfiguration("tag_config").perform(context)
zero_config = LaunchConfiguration("zero_config").perform(context) zero_config = LaunchConfiguration("zero_config").perform(context)
camera = Node( camera = Node(
package="g20_thumb_apriltag_calibration", package="linkerhand_calibration",
executable="hikrobot_camera_node", executable="hikrobot_camera_node",
name="hikrobot_camera", name="hikrobot_camera",
namespace="/camera/camera/color", namespace="/camera/camera/color",
@@ -158,7 +158,7 @@ def _launch_stack(context):
) )
zero_node = Node( zero_node = Node(
package="g20_thumb_apriltag_calibration", package="linkerhand_calibration",
executable="cmc_pitch_zero_node", executable="cmc_pitch_zero_node",
name="g20_thumb_cmc_pitch_zero", name="g20_thumb_cmc_pitch_zero",
output="screen", output="screen",
@@ -203,7 +203,7 @@ def _launch_stack(context):
def generate_launch_description() -> LaunchDescription: def generate_launch_description() -> LaunchDescription:
package_share = Path( package_share = Path(
get_package_share_directory("g20_thumb_apriltag_calibration") get_package_share_directory("linkerhand_calibration")
) )
return LaunchDescription( return LaunchDescription(
[ [
@@ -50,7 +50,7 @@ def _launch_stack(context):
"calibration_config" "calibration_config"
).perform(context) ).perform(context)
camera = Node( camera = Node(
package="g20_thumb_apriltag_calibration", package="linkerhand_calibration",
executable="hikrobot_camera_node", executable="hikrobot_camera_node",
name="hikrobot_camera", name="hikrobot_camera",
namespace="/camera/camera/color", namespace="/camera/camera/color",
@@ -160,7 +160,7 @@ def _launch_stack(context):
) )
calibration_node = Node( calibration_node = Node(
package="g20_thumb_apriltag_calibration", package="linkerhand_calibration",
executable="cmc_roll_calibration_node", executable="cmc_roll_calibration_node",
name="g20_thumb_cmc_roll_calibration", name="g20_thumb_cmc_roll_calibration",
output="screen", output="screen",
@@ -205,7 +205,7 @@ def _launch_stack(context):
def generate_launch_description() -> LaunchDescription: def generate_launch_description() -> LaunchDescription:
package_share = Path( package_share = Path(
get_package_share_directory("g20_thumb_apriltag_calibration") get_package_share_directory("linkerhand_calibration")
) )
return LaunchDescription( return LaunchDescription(
[ [
@@ -82,7 +82,7 @@ def _launch_stack(context):
) )
camera = Node( camera = Node(
package="g20_thumb_apriltag_calibration", package="linkerhand_calibration",
executable="hikrobot_camera_node", executable="hikrobot_camera_node",
name="hikrobot_camera", name="hikrobot_camera",
namespace="/camera/camera/color", namespace="/camera/camera/color",
@@ -234,7 +234,7 @@ def _launch_stack(context):
) )
calibration = Node( calibration = Node(
package="g20_thumb_apriltag_calibration", package="linkerhand_calibration",
executable="calibration_node", executable="calibration_node",
name="g20_thumb_calibration", name="g20_thumb_calibration",
output="screen", output="screen",
@@ -315,7 +315,7 @@ def _launch_stack(context):
def generate_launch_description() -> LaunchDescription: def generate_launch_description() -> LaunchDescription:
package_share = Path( package_share = Path(
get_package_share_directory("g20_thumb_apriltag_calibration") get_package_share_directory("linkerhand_calibration")
) )
default_output = str(Path.cwd() / "calibration_output") default_output = str(Path.cwd() / "calibration_output")
return LaunchDescription( return LaunchDescription(
@@ -39,7 +39,7 @@ def _default_source_urdf(hand_type: str) -> Path:
def _launch_stack(context): def _launch_stack(context):
from g20_thumb_apriltag_calibration.product import ( from linkerhand_calibration.product import (
get_product_calibration_contract, get_product_calibration_contract,
) )
@@ -56,7 +56,7 @@ def _launch_stack(context):
raise RuntimeError(str(error)) from error raise RuntimeError(str(error)) from error
requested_tag_config = LaunchConfiguration("tag_config").perform(context) requested_tag_config = LaunchConfiguration("tag_config").perform(context)
package_share = Path( package_share = Path(
get_package_share_directory("g20_thumb_apriltag_calibration") get_package_share_directory("linkerhand_calibration")
) )
tag_config = ( tag_config = (
Path(requested_tag_config).expanduser().resolve() Path(requested_tag_config).expanduser().resolve()
@@ -149,7 +149,7 @@ def _launch_stack(context):
detection_topics.append(detection_topic) detection_topics.append(detection_topic)
cameras.append( cameras.append(
Node( Node(
package="g20_thumb_apriltag_calibration", package="linkerhand_calibration",
executable="hikrobot_camera_node", executable="hikrobot_camera_node",
name="hikrobot_camera", name="hikrobot_camera",
namespace=namespace, namespace=namespace,
@@ -271,7 +271,7 @@ def _launch_stack(context):
], ],
) )
calibration = Node( calibration = Node(
package="g20_thumb_apriltag_calibration", package="linkerhand_calibration",
executable="three_camera_calibration_node", executable="three_camera_calibration_node",
name="g20_calibration", name="g20_calibration",
output="screen", output="screen",
@@ -386,7 +386,7 @@ def _launch_stack(context):
def generate_launch_description() -> LaunchDescription: def generate_launch_description() -> LaunchDescription:
package_share = Path( package_share = Path(
get_package_share_directory("g20_thumb_apriltag_calibration") get_package_share_directory("linkerhand_calibration")
) )
info_root = Path.home() / ".ros" / "camera_info" info_root = Path.home() / ".ros" / "camera_info"
return LaunchDescription( return LaunchDescription(
@@ -26,7 +26,7 @@ def _launch(context):
namespace = f"/g20_extrinsics/{view}/camera" namespace = f"/g20_extrinsics/{view}/camera"
cameras.append( cameras.append(
Node( Node(
package="g20_thumb_apriltag_calibration", package="linkerhand_calibration",
executable="hikrobot_camera_node", executable="hikrobot_camera_node",
name="hikrobot_camera", name="hikrobot_camera",
namespace=namespace, namespace=namespace,
@@ -85,7 +85,7 @@ def _launch(context):
output="screen", output="screen",
) )
solver = Node( solver = Node(
package="g20_thumb_apriltag_calibration", package="linkerhand_calibration",
executable="three_camera_extrinsics_node", executable="three_camera_extrinsics_node",
name="g20_camera_extrinsics", name="g20_camera_extrinsics",
output="screen", output="screen",
@@ -138,7 +138,7 @@ def _launch(context):
def generate_launch_description() -> LaunchDescription: def generate_launch_description() -> LaunchDescription:
package_share = Path( package_share = Path(
get_package_share_directory("g20_thumb_apriltag_calibration") get_package_share_directory("linkerhand_calibration")
) )
camera_info = Path.home() / ".ros" / "camera_info" camera_info = Path.home() / ".ros" / "camera_info"
return LaunchDescription( return LaunchDescription(
@@ -0,0 +1,5 @@
"""Compatibility adapters for one-release calibration migrations."""
from .paths import resolve_renamed_package_path
__all__ = ["resolve_renamed_package_path"]
@@ -0,0 +1,29 @@
"""Path compatibility for immutable v1 product configurations."""
from __future__ import annotations
from pathlib import Path
_LEGACY_SOURCE_PREFIX = Path("src/g20_thumb_apriltag_calibration")
_CURRENT_SOURCE_PREFIX = Path("src/linkerhand_calibration")
def resolve_renamed_package_path(value: str | Path, workspace: Path) -> Path:
"""Resolve a path and remap only the former source-package prefix.
Deployed v1 product YAML files are kept byte-for-byte stable because the
artifact paths participate in operational review. Existing paths always
win; the rename mapping is used only when the literal legacy path no
longer exists.
"""
raw = Path(value).expanduser()
candidate = raw if raw.is_absolute() else workspace / raw
candidate = candidate.resolve()
if candidate.exists() or raw.is_absolute():
return candidate
try:
suffix = raw.relative_to(_LEGACY_SOURCE_PREFIX)
except ValueError:
return candidate
return (workspace / _CURRENT_SOURCE_PREFIX / suffix).resolve()
@@ -203,7 +203,7 @@ def configure_fastdds_large_image_transport() -> Path:
from ament_index_python.packages import get_package_share_directory from ament_index_python.packages import get_package_share_directory
profile = ( profile = (
Path(get_package_share_directory("g20_thumb_apriltag_calibration")) Path(get_package_share_directory("linkerhand_calibration"))
/ "config" / "config"
/ "fastdds_large_images.xml" / "fastdds_large_images.xml"
) )
@@ -137,7 +137,7 @@ class ProgressConsole:
def _default_product_config() -> Path: def _default_product_config() -> Path:
try: try:
installed = Path( installed = Path(
get_package_share_directory("g20_thumb_apriltag_calibration") get_package_share_directory("linkerhand_calibration")
) / "config" / "g20_right_product.yaml" ) / "config" / "g20_right_product.yaml"
if installed.is_file(): if installed.is_file():
return installed return installed
@@ -145,7 +145,7 @@ def _default_product_config() -> Path:
pass pass
return ( return (
Path.cwd() Path.cwd()
/ "src/g20_thumb_apriltag_calibration/config/g20_right_product.yaml" / "src/linkerhand_calibration/config/g20_right_product.yaml"
).resolve() ).resolve()
@@ -188,7 +188,7 @@ def _launch_command(
return [ return [
"ros2", "ros2",
"launch", "launch",
"g20_thumb_apriltag_calibration", "linkerhand_calibration",
"three_camera_calibration.launch.py", "three_camera_calibration.launch.py",
*(f"{name}:={value}" for name, value in values.items()), *(f"{name}:={value}" for name, value in values.items()),
] ]
@@ -16,6 +16,7 @@ from typing import Any, Mapping
import yaml import yaml
from .compat import resolve_renamed_package_path
from .extrinsics import camera_info_fingerprint, load_three_camera_extrinsics from .extrinsics import camera_info_fingerprint, load_three_camera_extrinsics
from .full_hand import ( from .full_hand import (
G20_RIGHT_19_LAYOUT, G20_RIGHT_19_LAYOUT,
@@ -142,10 +143,7 @@ def _resolve_path(value: Any, *, workspace: Path, name: str) -> Path:
text = str(value).strip() text = str(value).strip()
if not text: if not text:
raise ValueError(f"{name} is required") raise ValueError(f"{name} is required")
candidate = Path(text).expanduser() return resolve_renamed_package_path(text, workspace)
if not candidate.is_absolute():
candidate = workspace / candidate
return candidate.resolve()
def _camera_info_fingerprint(path: Path) -> str: def _camera_info_fingerprint(path: Path) -> str:
@@ -1,9 +1,9 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?> <?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3"> <package format="3">
<name>g20_thumb_apriltag_calibration</name> <name>linkerhand_calibration</name>
<version>0.1.0</version> <version>0.1.0</version>
<description>One-command three-camera AprilTag calibration and zero-URDF correction for the G20 right hand.</description> <description>Profile-driven hand calibration and validated URDF correction.</description>
<maintainer email="support@linker-robotics.com">lxp</maintainer> <maintainer email="support@linker-robotics.com">lxp</maintainer>
<license>MIT</license> <license>MIT</license>
+4
View File
@@ -0,0 +1,4 @@
[develop]
script_dir=$base/lib/linkerhand_calibration
[install]
install_scripts=$base/lib/linkerhand_calibration
@@ -3,7 +3,7 @@ from glob import glob
from setuptools import find_packages, setup from setuptools import find_packages, setup
package_name = "g20_thumb_apriltag_calibration" package_name = "linkerhand_calibration"
setup( setup(
name=package_name, name=package_name,
@@ -32,45 +32,45 @@ setup(
"console_scripts": [ "console_scripts": [
( (
"hikrobot_camera_node = " "hikrobot_camera_node = "
"g20_thumb_apriltag_calibration.hikrobot_camera:main" "linkerhand_calibration.hikrobot_camera:main"
), ),
"calibration_node = g20_thumb_apriltag_calibration.node:main", "calibration_node = linkerhand_calibration.node:main",
( (
"cmc_pitch_zero_node = " "cmc_pitch_zero_node = "
"g20_thumb_apriltag_calibration.zero_node:main" "linkerhand_calibration.zero_node:main"
), ),
( (
"cmc_roll_calibration_node = " "cmc_roll_calibration_node = "
"g20_thumb_apriltag_calibration.zero_node:main" "linkerhand_calibration.zero_node:main"
), ),
( (
"three_camera_calibration_node = " "three_camera_calibration_node = "
"g20_thumb_apriltag_calibration.three_camera_node:main" "linkerhand_calibration.three_camera_node:main"
), ),
( (
"three_camera_extrinsics_node = " "three_camera_extrinsics_node = "
"g20_thumb_apriltag_calibration.extrinsics_node:main" "linkerhand_calibration.extrinsics_node:main"
), ),
( (
"offline_replay = " "offline_replay = "
"g20_thumb_apriltag_calibration.offline_replay:main" "linkerhand_calibration.offline_replay:main"
), ),
( (
"camera_alignment_view = " "camera_alignment_view = "
"g20_thumb_apriltag_calibration.alignment_view:main" "linkerhand_calibration.alignment_view:main"
), ),
( (
"calibrated_joint_state_bridge = " "calibrated_joint_state_bridge = "
"g20_thumb_apriltag_calibration." "linkerhand_calibration."
"calibrated_joint_state_bridge:main" "calibrated_joint_state_bridge:main"
), ),
( (
"calibrate_g20_right = " "calibrate_g20_right = "
"g20_thumb_apriltag_calibration.one_command:main" "linkerhand_calibration.one_command:main"
), ),
( (
"calibrate_hand = " "calibrate_hand = "
"g20_thumb_apriltag_calibration.one_command:main" "linkerhand_calibration.one_command:main"
), ),
], ],
}, },
@@ -6,7 +6,7 @@ import numpy as np
import pytest import pytest
from scipy.spatial.transform import Rotation from scipy.spatial.transform import Rotation
from g20_thumb_apriltag_calibration.acquisition import ( from linkerhand_calibration.acquisition import (
ContinuousSweepCollector, ContinuousSweepCollector,
Observation, Observation,
PointCollector, PointCollector,
@@ -19,7 +19,7 @@ from g20_thumb_apriltag_calibration.acquisition import (
tag_quality_is_valid, tag_quality_is_valid,
update_pnp_reset_watchdog, update_pnp_reset_watchdog,
) )
from g20_thumb_apriltag_calibration.core import PAIR_NAMES from linkerhand_calibration.core import PAIR_NAMES
def test_pnp_watchdog_resets_after_one_continuous_invalid_second() -> None: def test_pnp_watchdog_resets_after_one_continuous_invalid_second() -> None:
@@ -2,7 +2,7 @@
import pytest import pytest
from g20_thumb_apriltag_calibration.alignment_view import ( from linkerhand_calibration.alignment_view import (
summarize_alignment_measurements, summarize_alignment_measurements,
) )
@@ -2,18 +2,18 @@ import copy
import pytest import pytest
from g20_thumb_apriltag_calibration.calibrated_joint_state_bridge import ( from linkerhand_calibration.calibrated_joint_state_bridge import (
G20_COMMAND_NAMES, G20_COMMAND_NAMES,
G20_URDF_JOINT_NAMES, G20_URDF_JOINT_NAMES,
CalibratedCommandMapper, CalibratedCommandMapper,
default_input_topic, default_input_topic,
) )
from g20_thumb_apriltag_calibration.full_hand import ( from linkerhand_calibration.full_hand import (
JointCurveFit, JointCurveFit,
build_compact_payload, build_compact_payload,
get_hand_calibration_profile, get_hand_calibration_profile,
) )
from g20_thumb_apriltag_calibration.urdf_zero import ( from linkerhand_calibration.urdf_zero import (
get_zero_calibration_profile, get_zero_calibration_profile,
) )
@@ -6,7 +6,7 @@ import numpy as np
import pytest import pytest
from scipy.spatial.transform import Rotation from scipy.spatial.transform import Rotation
from g20_thumb_apriltag_calibration.core import ( from linkerhand_calibration.core import (
BASELINE_COMMAND, BASELINE_COMMAND,
DIRECTION_DECREASING, DIRECTION_DECREASING,
DIRECTION_INCREASING, DIRECTION_INCREASING,
@@ -1,5 +1,5 @@
from g20_thumb_apriltag_calibration.acquisition import TagQuality from linkerhand_calibration.acquisition import TagQuality
from g20_thumb_apriltag_calibration.diagnostics import ( from linkerhand_calibration.diagnostics import (
build_tag_quality_diagnostics, build_tag_quality_diagnostics,
render_status_text_zh, render_status_text_zh,
status_guidance_zh, status_guidance_zh,
@@ -4,8 +4,8 @@ import cv2
import numpy as np import numpy as np
from scipy.spatial.transform import Rotation from scipy.spatial.transform import Rotation
import g20_thumb_apriltag_calibration.extrinsics_node as extrinsics_node import linkerhand_calibration.extrinsics_node as extrinsics_node
from g20_thumb_apriltag_calibration.extrinsics_node import ( from linkerhand_calibration.extrinsics_node import (
BoardPose, BoardPose,
StereoCapture, StereoCapture,
_fit_stereo_robust, _fit_stereo_robust,
@@ -5,7 +5,7 @@ from pathlib import Path
import numpy as np import numpy as np
import pytest import pytest
from g20_thumb_apriltag_calibration.full_hand import ( from linkerhand_calibration.full_hand import (
ACTIVE_JOINTS, ACTIVE_JOINTS,
IMAGE_TRAJECTORY_JOINTS, IMAGE_TRAJECTORY_JOINTS,
JOINT_SPECS, JOINT_SPECS,
@@ -1005,7 +1005,7 @@ def test_right_compact_payload_keeps_v4_shape_and_uses_pinky_sources() -> None:
def test_right_19_schema_v4_records_visual_dips_and_16_static_zeros() -> None: def test_right_19_schema_v4_records_visual_dips_and_16_static_zeros() -> None:
from g20_thumb_apriltag_calibration.urdf_zero import ( from linkerhand_calibration.urdf_zero import (
get_zero_calibration_profile, get_zero_calibration_profile,
) )
@@ -10,11 +10,11 @@ import numpy as np
import pytest import pytest
from scipy.spatial.transform import Rotation from scipy.spatial.transform import Rotation
from g20_thumb_apriltag_calibration.core import ( from linkerhand_calibration.core import (
DIRECTION_DECREASING, DIRECTION_DECREASING,
DIRECTION_INCREASING, DIRECTION_INCREASING,
) )
from g20_thumb_apriltag_calibration.full_hand import ( from linkerhand_calibration.full_hand import (
G20_COMBINATION_REQUIRED_TARGET_KEYS, G20_COMBINATION_REQUIRED_TARGET_KEYS,
G20_REFERENCE_THUMB_CMC_JOINTS, G20_REFERENCE_THUMB_CMC_JOINTS,
G20_RIGHT_19_LAYOUT, G20_RIGHT_19_LAYOUT,
@@ -22,21 +22,21 @@ from g20_thumb_apriltag_calibration.full_hand import (
build_compact_payload, build_compact_payload,
get_hand_calibration_profile, get_hand_calibration_profile,
) )
from g20_thumb_apriltag_calibration.operator_report import ( from linkerhand_calibration.operator_report import (
ProgressEstimator, ProgressEstimator,
build_failure_report, build_failure_report,
classify_error, classify_error,
render_progress_zh, render_progress_zh,
) )
from g20_thumb_apriltag_calibration.one_command import ( from linkerhand_calibration.one_command import (
_automatic_resume_candidate, _automatic_resume_candidate,
_calibration_node_exited_before_status, _calibration_node_exited_before_status,
_launch_command, _launch_command,
_resolve_partial_base_session, _resolve_partial_base_session,
_status_timeout_seconds, _status_timeout_seconds,
) )
from g20_thumb_apriltag_calibration.product import load_product_config from linkerhand_calibration.product import load_product_config
from g20_thumb_apriltag_calibration.publication import ( from linkerhand_calibration.publication import (
ACTIVE_ZERO_JOINTS, ACTIVE_ZERO_JOINTS,
PASSIVE_JOINTS, PASSIVE_JOINTS,
atomic_session_pointer, atomic_session_pointer,
@@ -49,8 +49,8 @@ from g20_thumb_apriltag_calibration.publication import (
verify_partial_scope_preserves_certified_zeros, verify_partial_scope_preserves_certified_zeros,
verify_urdf_mesh_resources, verify_urdf_mesh_resources,
) )
from g20_thumb_apriltag_calibration.storage import atomic_write_json from linkerhand_calibration.storage import atomic_write_json
from g20_thumb_apriltag_calibration.three_camera_node import ( from linkerhand_calibration.three_camera_node import (
G20ThreeCameraCalibrationNode, G20ThreeCameraCalibrationNode,
STEADY_COMMAND_CHECKPOINTS, STEADY_COMMAND_CHECKPOINTS,
SweepItem, SweepItem,
@@ -70,7 +70,7 @@ from g20_thumb_apriltag_calibration.three_camera_node import (
resume_tasks_invalidated_by_tag_size_changes, resume_tasks_invalidated_by_tag_size_changes,
resumable_completed_task_prefix, resumable_completed_task_prefix,
) )
from g20_thumb_apriltag_calibration.urdf_zero import ( from linkerhand_calibration.urdf_zero import (
RIGHT_19_MECHANICAL_ENDPOINT_JOINTS, RIGHT_19_MECHANICAL_ENDPOINT_JOINTS,
UrdfKinematicModel, UrdfKinematicModel,
get_zero_calibration_profile, get_zero_calibration_profile,
@@ -79,7 +79,7 @@ from g20_thumb_apriltag_calibration.urdf_zero import (
REPO = Path(__file__).resolve().parents[3] REPO = Path(__file__).resolve().parents[3]
PRODUCT = REPO / "src/g20_thumb_apriltag_calibration/config/g20_right_product.yaml" PRODUCT = REPO / "src/linkerhand_calibration/config/g20_right_product.yaml"
def _config(tmp_path: Path, *, passes: int = 2): def _config(tmp_path: Path, *, passes: int = 2):
@@ -202,6 +202,16 @@ def test_product_config_locks_three_cameras_tags_and_artifact_hashes() -> None:
assert len({camera["serial_number"] for camera in config.cameras.values()}) == 3 assert len({camera["serial_number"] for camera in config.cameras.values()}) == 3
assert config.source_urdf.is_file() assert config.source_urdf.is_file()
assert config.camera_extrinsics.is_file() assert config.camera_extrinsics.is_file()
assert config.calibration_config == (
REPO
/ "src/linkerhand_calibration/config/three_camera_calibration.yaml"
)
assert config.tag_config == (
REPO
/ "src/linkerhand_calibration/config/three_camera_tags_g20_right_19.yaml"
)
product_text = PRODUCT.read_text(encoding="utf-8")
assert "src/g20_thumb_apriltag_calibration/config/" in product_text
def test_first_round_uses_nine_bidirectional_steady_commands() -> None: def test_first_round_uses_nine_bidirectional_steady_commands() -> None:
@@ -3,7 +3,7 @@ from pathlib import Path
import pytest import pytest
import yaml import yaml
from g20_thumb_apriltag_calibration.hikrobot_camera import ( from linkerhand_calibration.hikrobot_camera import (
DeviceDescriptor, DeviceDescriptor,
decode_c_string, decode_c_string,
load_camera_calibration, load_camera_calibration,
@@ -1,6 +1,6 @@
import pytest import pytest
from g20_thumb_apriltag_calibration.offline_replay import ( from linkerhand_calibration.offline_replay import (
_latest_attempt_records, _latest_attempt_records,
_latest_palm_axis_records, _latest_palm_axis_records,
_output_suffix, _output_suffix,
@@ -0,0 +1,26 @@
import importlib
from pathlib import Path
import re
import pytest
def test_legacy_python_package_forwards_only_public_config_api() -> None:
with pytest.warns(DeprecationWarning, match="linkerhand_calibration"):
legacy = importlib.import_module("g20_thumb_apriltag_calibration")
current = importlib.import_module("linkerhand_calibration.product")
assert legacy.ProductConfig is current.ProductConfig
assert legacy.load_product_config is current.load_product_config
def test_new_and_legacy_executable_names_share_one_implementation() -> None:
setup_text = (Path(__file__).resolve().parents[1] / "setup.py").read_text(
encoding="utf-8"
)
for executable in ("calibrate_hand", "calibrate_g20_right"):
assert re.search(
rf'"{executable} = "\s*'
r'"linkerhand_calibration\.one_command:main"',
setup_text,
)
@@ -5,7 +5,7 @@ import numpy as np
import pytest import pytest
from scipy.spatial.transform import Rotation from scipy.spatial.transform import Rotation
from g20_thumb_apriltag_calibration.pnp import ( from linkerhand_calibration.pnp import (
SquareTagGroupPoseTracker, SquareTagGroupPoseTracker,
SquareTagPose, SquareTagPose,
SquareTagPoseTracker, SquareTagPoseTracker,
@@ -1,6 +1,6 @@
"""Tests for the explicit calibration sample command domains.""" """Tests for the explicit calibration sample command domains."""
from g20_thumb_apriltag_calibration.sample_schema import ( from linkerhand_calibration.sample_schema import (
SampleDataContractError, SampleDataContractError,
canonical_sample_record, canonical_sample_record,
fitting_sample_record, fitting_sample_record,
@@ -1,4 +1,4 @@
from g20_thumb_apriltag_calibration.storage import ( from linkerhand_calibration.storage import (
append_jsonl, append_jsonl,
atomic_write_json, atomic_write_json,
completed_scan_keys, completed_scan_keys,
@@ -2,7 +2,7 @@ import json
import numpy as np import numpy as np
from g20_thumb_apriltag_calibration.three_camera_diagnostics import ( from linkerhand_calibration.three_camera_diagnostics import (
_task_text, _task_text,
render_three_camera_status_text_zh, render_three_camera_status_text_zh,
three_camera_reason_zh, three_camera_reason_zh,
@@ -8,13 +8,13 @@ import numpy as np
import pytest import pytest
from scipy.spatial.transform import Rotation from scipy.spatial.transform import Rotation
import g20_thumb_apriltag_calibration.three_camera_node as three_camera_node import linkerhand_calibration.three_camera_node as three_camera_node
from g20_thumb_apriltag_calibration.core import ( from linkerhand_calibration.core import (
DIRECTION_DECREASING, DIRECTION_DECREASING,
DIRECTION_INCREASING, DIRECTION_INCREASING,
) )
from g20_thumb_apriltag_calibration.acquisition import TagQuality from linkerhand_calibration.acquisition import TagQuality
from g20_thumb_apriltag_calibration.full_hand import ( from linkerhand_calibration.full_hand import (
JointCurveFit, JointCurveFit,
PalmAxisObserver, PalmAxisObserver,
RIGHT_19_END_ON_IMAGE_CURVE_JOINTS, RIGHT_19_END_ON_IMAGE_CURVE_JOINTS,
@@ -27,7 +27,7 @@ from g20_thumb_apriltag_calibration.full_hand import (
build_calibration_preparation_waypoints, build_calibration_preparation_waypoints,
build_calibration_return_waypoints, build_calibration_return_waypoints,
) )
from g20_thumb_apriltag_calibration.three_camera_node import ( from linkerhand_calibration.three_camera_node import (
FrameObservation, FrameObservation,
G20ThreeCameraCalibrationNode, G20ThreeCameraCalibrationNode,
STATE_COMPLETE, STATE_COMPLETE,
@@ -56,8 +56,8 @@ from g20_thumb_apriltag_calibration.three_camera_node import (
_sweep_views, _sweep_views,
_thumb_yaw_zero_repeatability_failures, _thumb_yaw_zero_repeatability_failures,
) )
from g20_thumb_apriltag_calibration.pnp import SquareTagPose from linkerhand_calibration.pnp import SquareTagPose
from g20_thumb_apriltag_calibration.urdf_zero import ( from linkerhand_calibration.urdf_zero import (
RIGHT_19_MECHANICAL_ENDPOINT_JOINTS, RIGHT_19_MECHANICAL_ENDPOINT_JOINTS,
JointAxisMeasurement, JointAxisMeasurement,
axis_line_uses_depth_free_interpretation_plane, axis_line_uses_depth_free_interpretation_plane,
@@ -4450,7 +4450,7 @@ def test_side_alias_branch_gap_range_is_diagnostic_only(
records = _image_cycle_records([math.radians(47.0)] * 3) records = _image_cycle_records([math.radians(47.0)] * 3)
gaps_deg = [0.10, 0.81, 0.10] # range 0.71 deg, maximum below 2 deg gaps_deg = [0.10, 0.81, 0.10] # range 0.71 deg, maximum below 2 deg
monkeypatch.setattr( monkeypatch.setattr(
"g20_thumb_apriltag_calibration.three_camera_node" "linkerhand_calibration.three_camera_node"
".baseline_hysteresis_by_cycle_rad", ".baseline_hysteresis_by_cycle_rad",
lambda records, zero_command_u8, axis_xyz: [ lambda records, zero_command_u8, axis_xyz: [
math.radians(value) for value in gaps_deg math.radians(value) for value in gaps_deg
@@ -4,7 +4,7 @@ import numpy as np
import pytest import pytest
from scipy.spatial.transform import Rotation from scipy.spatial.transform import Rotation
from g20_thumb_apriltag_calibration.core import ( from linkerhand_calibration.core import (
DIRECTION_DECREASING, DIRECTION_DECREASING,
DIRECTION_INCREASING, DIRECTION_INCREASING,
PAIR_IP, PAIR_IP,
@@ -14,7 +14,7 @@ from g20_thumb_apriltag_calibration.core import (
PHASE_TIP, PHASE_TIP,
create_final_payload, create_final_payload,
) )
from g20_thumb_apriltag_calibration.trajectory import ( from linkerhand_calibration.trajectory import (
_regularize_coupled_zero_tail, _regularize_coupled_zero_tail,
fit_center_trajectory_curves, fit_center_trajectory_curves,
maximum_center_non_target_drift_rad, maximum_center_non_target_drift_rad,
@@ -6,12 +6,12 @@ import numpy as np
import pytest import pytest
from scipy.spatial.transform import Rotation from scipy.spatial.transform import Rotation
from g20_thumb_apriltag_calibration.extrinsics import ( from linkerhand_calibration.extrinsics import (
camera_info_fingerprint, camera_info_fingerprint,
dump_three_camera_extrinsics, dump_three_camera_extrinsics,
load_three_camera_extrinsics, load_three_camera_extrinsics,
) )
from g20_thumb_apriltag_calibration.urdf_zero import ( from linkerhand_calibration.urdf_zero import (
AXIS_JOINTS, AXIS_JOINTS,
DIRECT_ZERO_JOINTS, DIRECT_ZERO_JOINTS,
INHERITED_ZERO_JOINTS, INHERITED_ZERO_JOINTS,
@@ -35,7 +35,7 @@ from g20_thumb_apriltag_calibration.urdf_zero import (
merge_right_19_thumb_zero_result, merge_right_19_thumb_zero_result,
write_zero_corrected_urdf, write_zero_corrected_urdf,
) )
from g20_thumb_apriltag_calibration.full_hand import ( from linkerhand_calibration.full_hand import (
ACTIVE_JOINTS, ACTIVE_JOINTS,
JOINT_SPECS, JOINT_SPECS,
MEASURED_JOINTS, MEASURED_JOINTS,
@@ -3,7 +3,7 @@ from __future__ import annotations
import math import math
import cv2 import cv2
from g20_thumb_apriltag_calibration.zero_calibration import ( from linkerhand_calibration.zero_calibration import (
build_trajectory_zero_angle_payload, build_trajectory_zero_angle_payload,
build_trajectory_zero_travel_payload, build_trajectory_zero_travel_payload,
circular_median_rad, circular_median_rad,