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右手正式一键标定
固定三相机和19张Tag安装完成后,用户只运行:
```bash
ros2 run g20_thumb_apriltag_calibration calibrate_g20_right
ros2 run linkerhand_calibration calibrate_hand
```
旧 executable `calibrate_g20_right` 在本发行版内保留为同一入口的别名;
旧 ROS 包名前缀不再提供。新脚本和部署配置统一使用 `calibrate_hand`
完全独立地只标定大拇指4项任务时,使用:
```bash
ros2 run g20_thumb_apriltag_calibration calibrate_g20_right \
ros2 run linkerhand_calibration calibrate_hand \
--scope thumb
```
@@ -23,7 +26,7 @@ ros2 run g20_thumb_apriltag_calibration calibrate_g20_right \
如果确实需要把新的拇指结果合并到一份已经通过的完整整手标定,才额外使用:
```bash
ros2 run g20_thumb_apriltag_calibration calibrate_g20_right \
ros2 run linkerhand_calibration calibrate_hand \
--scope thumb \
--base-session calibration_output/G20_RIGHT_001/latest_passed
```
@@ -35,7 +38,7 @@ ros2 run g20_thumb_apriltag_calibration calibrate_g20_right \
只重新采集12项四指任务并合成完整整手URDF:
```bash
ros2 run g20_thumb_apriltag_calibration calibrate_g20_right \
ros2 run linkerhand_calibration calibrate_hand \
--scope fingers \
--base-session calibration_output/G20_RIGHT_001/<已通过的拇指会话时间戳>
```
@@ -114,7 +117,7 @@ schema v4 JSON,不再生成schema v5运行文件。
`tag_layout:=legacy_11`,两套配置和结果schema互不覆盖:
```bash
ros2 launch g20_thumb_apriltag_calibration three_camera_calibration.launch.py \
ros2 launch linkerhand_calibration three_camera_calibration.launch.py \
hand_type:=right \
tag_layout:=g20_right_19 \
serial_number:=G20_RIGHT_001 \
@@ -271,7 +274,7 @@ ID 9 必须在拇指横摆的完整行程中持续可见。
```bash
mkdir -p /home/lxp/projects/linkerhand_retarget_ros2/config
ros2 launch g20_thumb_apriltag_calibration \
ros2 launch linkerhand_calibration \
three_camera_extrinsics.launch.py \
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
@@ -312,7 +315,7 @@ ros2 service call /g20_camera_extrinsics/save std_srvs/srv/Trigger {}
先使用禁止运动模式检查三个机位、外参、内参和标签:
```bash
ros2 launch g20_thumb_apriltag_calibration \
ros2 launch linkerhand_calibration \
three_camera_calibration.launch.py \
hand_type:=left \
serial_number:=G20_LEFT_001 \
@@ -335,7 +338,7 @@ ros2 run image_view image_view --ros-args \
确认全行程安全、MVS客户端已关闭且没有其他命令发布者后,重启正式流程:
```bash
ros2 launch g20_thumb_apriltag_calibration \
ros2 launch linkerhand_calibration \
three_camera_calibration.launch.py \
hand_type:=left \
serial_number:=G20_LEFT_001 \
@@ -347,7 +350,7 @@ ros2 launch g20_thumb_apriltag_calibration \
右手使用同一入口;默认自动选择右手SDK话题和原始URDF:
```bash
ros2 launch g20_thumb_apriltag_calibration \
ros2 launch linkerhand_calibration \
three_camera_calibration.launch.py \
hand_type:=right \
serial_number:=G20_RIGHT_001 \
@@ -526,7 +529,7 @@ calibration_output/G20_RIGHT_001/<时间戳>/
命令。`--output-tag` 为新产物增加安全后缀,已有JSON、URDF和验证报告不会被覆盖:
```bash
python3 -m g20_thumb_apriltag_calibration.offline_replay \
python3 -m linkerhand_calibration.offline_replay \
calibration_output/G20_RIGHT_001/20260811_120146 \
--output-tag AXIS_FRAME_V3 \
--write
@@ -543,7 +546,7 @@ python3 -m g20_thumb_apriltag_calibration.offline_replay \
`JointState`(包括5个被动关节):
```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 \
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
source /opt/ros/jazzy/setup.bash
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
```
@@ -642,7 +645,7 @@ source install/setup.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 \
-p serial_number:=DB2163742 \
-p camera_info_url:=$HOME/.ros/camera_info/hikrobot_DB2163742.yaml
@@ -680,7 +683,7 @@ ros2 run camera_calibration cameracalibrator \
但标定节点不会发送位置运动命令,也不会允许解锁全行程扫描:
```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 \
camera_serial_number:=DB2163742 \
commands_enabled:=false
@@ -691,7 +694,7 @@ ros2 launch g20_thumb_apriltag_calibration front_thumb_calibration.launch.py \
速度,并使用单终点连续运动:
```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 \
camera_serial_number:=DB2163742 \
can_interface:=can0 \
@@ -804,7 +807,7 @@ calibration_output/<序列号>/<时间戳>/
恢复时必须显式复用原目录,否则会创建新会话:
```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 \
session_dir:=/绝对路径/calibration_output/G20_LEFT_001/20260727_120000
```
@@ -884,7 +887,7 @@ for name, joint in data["joints"].items():
URDF
```bash
ros2 launch g20_thumb_apriltag_calibration \
ros2 launch linkerhand_calibration \
front_cmc_pitch_zero.launch.py \
serial_number:=G20_LEFT_001
```
@@ -961,7 +964,7 @@ zero_angles.table_projected_zero_rad
Roll同样固定使用“T3中心→拟合圆心”的内向径向矢量,不读取T3标签朝向。
```bash
ros2 launch g20_thumb_apriltag_calibration \
ros2 launch linkerhand_calibration \
front_cmc_roll_calibration.launch.py \
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("output_topic", default_value=""),
Node(
package="g20_thumb_apriltag_calibration",
package="linkerhand_calibration",
executable="calibrated_joint_state_bridge",
name=[
"g20_calibrated_joint_state_bridge_",
@@ -48,7 +48,7 @@ def _launch_stack(context):
tag_config = LaunchConfiguration("tag_config").perform(context)
zero_config = LaunchConfiguration("zero_config").perform(context)
camera = Node(
package="g20_thumb_apriltag_calibration",
package="linkerhand_calibration",
executable="hikrobot_camera_node",
name="hikrobot_camera",
namespace="/camera/camera/color",
@@ -158,7 +158,7 @@ def _launch_stack(context):
)
zero_node = Node(
package="g20_thumb_apriltag_calibration",
package="linkerhand_calibration",
executable="cmc_pitch_zero_node",
name="g20_thumb_cmc_pitch_zero",
output="screen",
@@ -203,7 +203,7 @@ def _launch_stack(context):
def generate_launch_description() -> LaunchDescription:
package_share = Path(
get_package_share_directory("g20_thumb_apriltag_calibration")
get_package_share_directory("linkerhand_calibration")
)
return LaunchDescription(
[
@@ -50,7 +50,7 @@ def _launch_stack(context):
"calibration_config"
).perform(context)
camera = Node(
package="g20_thumb_apriltag_calibration",
package="linkerhand_calibration",
executable="hikrobot_camera_node",
name="hikrobot_camera",
namespace="/camera/camera/color",
@@ -160,7 +160,7 @@ def _launch_stack(context):
)
calibration_node = Node(
package="g20_thumb_apriltag_calibration",
package="linkerhand_calibration",
executable="cmc_roll_calibration_node",
name="g20_thumb_cmc_roll_calibration",
output="screen",
@@ -205,7 +205,7 @@ def _launch_stack(context):
def generate_launch_description() -> LaunchDescription:
package_share = Path(
get_package_share_directory("g20_thumb_apriltag_calibration")
get_package_share_directory("linkerhand_calibration")
)
return LaunchDescription(
[
@@ -82,7 +82,7 @@ def _launch_stack(context):
)
camera = Node(
package="g20_thumb_apriltag_calibration",
package="linkerhand_calibration",
executable="hikrobot_camera_node",
name="hikrobot_camera",
namespace="/camera/camera/color",
@@ -234,7 +234,7 @@ def _launch_stack(context):
)
calibration = Node(
package="g20_thumb_apriltag_calibration",
package="linkerhand_calibration",
executable="calibration_node",
name="g20_thumb_calibration",
output="screen",
@@ -315,7 +315,7 @@ def _launch_stack(context):
def generate_launch_description() -> LaunchDescription:
package_share = Path(
get_package_share_directory("g20_thumb_apriltag_calibration")
get_package_share_directory("linkerhand_calibration")
)
default_output = str(Path.cwd() / "calibration_output")
return LaunchDescription(
@@ -39,7 +39,7 @@ def _default_source_urdf(hand_type: str) -> Path:
def _launch_stack(context):
from g20_thumb_apriltag_calibration.product import (
from linkerhand_calibration.product import (
get_product_calibration_contract,
)
@@ -56,7 +56,7 @@ def _launch_stack(context):
raise RuntimeError(str(error)) from error
requested_tag_config = LaunchConfiguration("tag_config").perform(context)
package_share = Path(
get_package_share_directory("g20_thumb_apriltag_calibration")
get_package_share_directory("linkerhand_calibration")
)
tag_config = (
Path(requested_tag_config).expanduser().resolve()
@@ -149,7 +149,7 @@ def _launch_stack(context):
detection_topics.append(detection_topic)
cameras.append(
Node(
package="g20_thumb_apriltag_calibration",
package="linkerhand_calibration",
executable="hikrobot_camera_node",
name="hikrobot_camera",
namespace=namespace,
@@ -271,7 +271,7 @@ def _launch_stack(context):
],
)
calibration = Node(
package="g20_thumb_apriltag_calibration",
package="linkerhand_calibration",
executable="three_camera_calibration_node",
name="g20_calibration",
output="screen",
@@ -386,7 +386,7 @@ def _launch_stack(context):
def generate_launch_description() -> LaunchDescription:
package_share = Path(
get_package_share_directory("g20_thumb_apriltag_calibration")
get_package_share_directory("linkerhand_calibration")
)
info_root = Path.home() / ".ros" / "camera_info"
return LaunchDescription(
@@ -26,7 +26,7 @@ def _launch(context):
namespace = f"/g20_extrinsics/{view}/camera"
cameras.append(
Node(
package="g20_thumb_apriltag_calibration",
package="linkerhand_calibration",
executable="hikrobot_camera_node",
name="hikrobot_camera",
namespace=namespace,
@@ -85,7 +85,7 @@ def _launch(context):
output="screen",
)
solver = Node(
package="g20_thumb_apriltag_calibration",
package="linkerhand_calibration",
executable="three_camera_extrinsics_node",
name="g20_camera_extrinsics",
output="screen",
@@ -138,7 +138,7 @@ def _launch(context):
def generate_launch_description() -> LaunchDescription:
package_share = Path(
get_package_share_directory("g20_thumb_apriltag_calibration")
get_package_share_directory("linkerhand_calibration")
)
camera_info = Path.home() / ".ros" / "camera_info"
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
profile = (
Path(get_package_share_directory("g20_thumb_apriltag_calibration"))
Path(get_package_share_directory("linkerhand_calibration"))
/ "config"
/ "fastdds_large_images.xml"
)
@@ -137,7 +137,7 @@ class ProgressConsole:
def _default_product_config() -> Path:
try:
installed = Path(
get_package_share_directory("g20_thumb_apriltag_calibration")
get_package_share_directory("linkerhand_calibration")
) / "config" / "g20_right_product.yaml"
if installed.is_file():
return installed
@@ -145,7 +145,7 @@ def _default_product_config() -> Path:
pass
return (
Path.cwd()
/ "src/g20_thumb_apriltag_calibration/config/g20_right_product.yaml"
/ "src/linkerhand_calibration/config/g20_right_product.yaml"
).resolve()
@@ -188,7 +188,7 @@ def _launch_command(
return [
"ros2",
"launch",
"g20_thumb_apriltag_calibration",
"linkerhand_calibration",
"three_camera_calibration.launch.py",
*(f"{name}:={value}" for name, value in values.items()),
]
@@ -16,6 +16,7 @@ from typing import Any, Mapping
import yaml
from .compat import resolve_renamed_package_path
from .extrinsics import camera_info_fingerprint, load_three_camera_extrinsics
from .full_hand import (
G20_RIGHT_19_LAYOUT,
@@ -142,10 +143,7 @@ def _resolve_path(value: Any, *, workspace: Path, name: str) -> Path:
text = str(value).strip()
if not text:
raise ValueError(f"{name} is required")
candidate = Path(text).expanduser()
if not candidate.is_absolute():
candidate = workspace / candidate
return candidate.resolve()
return resolve_renamed_package_path(text, workspace)
def _camera_info_fingerprint(path: Path) -> str:
@@ -1,9 +1,9 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>g20_thumb_apriltag_calibration</name>
<name>linkerhand_calibration</name>
<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>
<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
package_name = "g20_thumb_apriltag_calibration"
package_name = "linkerhand_calibration"
setup(
name=package_name,
@@ -32,45 +32,45 @@ setup(
"console_scripts": [
(
"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 = "
"g20_thumb_apriltag_calibration.zero_node:main"
"linkerhand_calibration.zero_node:main"
),
(
"cmc_roll_calibration_node = "
"g20_thumb_apriltag_calibration.zero_node:main"
"linkerhand_calibration.zero_node:main"
),
(
"three_camera_calibration_node = "
"g20_thumb_apriltag_calibration.three_camera_node:main"
"linkerhand_calibration.three_camera_node:main"
),
(
"three_camera_extrinsics_node = "
"g20_thumb_apriltag_calibration.extrinsics_node:main"
"linkerhand_calibration.extrinsics_node:main"
),
(
"offline_replay = "
"g20_thumb_apriltag_calibration.offline_replay:main"
"linkerhand_calibration.offline_replay:main"
),
(
"camera_alignment_view = "
"g20_thumb_apriltag_calibration.alignment_view:main"
"linkerhand_calibration.alignment_view:main"
),
(
"calibrated_joint_state_bridge = "
"g20_thumb_apriltag_calibration."
"linkerhand_calibration."
"calibrated_joint_state_bridge:main"
),
(
"calibrate_g20_right = "
"g20_thumb_apriltag_calibration.one_command:main"
"linkerhand_calibration.one_command:main"
),
(
"calibrate_hand = "
"g20_thumb_apriltag_calibration.one_command:main"
"linkerhand_calibration.one_command:main"
),
],
},
@@ -6,7 +6,7 @@ import numpy as np
import pytest
from scipy.spatial.transform import Rotation
from g20_thumb_apriltag_calibration.acquisition import (
from linkerhand_calibration.acquisition import (
ContinuousSweepCollector,
Observation,
PointCollector,
@@ -19,7 +19,7 @@ from g20_thumb_apriltag_calibration.acquisition import (
tag_quality_is_valid,
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:
@@ -2,7 +2,7 @@
import pytest
from g20_thumb_apriltag_calibration.alignment_view import (
from linkerhand_calibration.alignment_view import (
summarize_alignment_measurements,
)
@@ -2,18 +2,18 @@ import copy
import pytest
from g20_thumb_apriltag_calibration.calibrated_joint_state_bridge import (
from linkerhand_calibration.calibrated_joint_state_bridge import (
G20_COMMAND_NAMES,
G20_URDF_JOINT_NAMES,
CalibratedCommandMapper,
default_input_topic,
)
from g20_thumb_apriltag_calibration.full_hand import (
from linkerhand_calibration.full_hand import (
JointCurveFit,
build_compact_payload,
get_hand_calibration_profile,
)
from g20_thumb_apriltag_calibration.urdf_zero import (
from linkerhand_calibration.urdf_zero import (
get_zero_calibration_profile,
)
@@ -6,7 +6,7 @@ import numpy as np
import pytest
from scipy.spatial.transform import Rotation
from g20_thumb_apriltag_calibration.core import (
from linkerhand_calibration.core import (
BASELINE_COMMAND,
DIRECTION_DECREASING,
DIRECTION_INCREASING,
@@ -1,5 +1,5 @@
from g20_thumb_apriltag_calibration.acquisition import TagQuality
from g20_thumb_apriltag_calibration.diagnostics import (
from linkerhand_calibration.acquisition import TagQuality
from linkerhand_calibration.diagnostics import (
build_tag_quality_diagnostics,
render_status_text_zh,
status_guidance_zh,
@@ -4,8 +4,8 @@ import cv2
import numpy as np
from scipy.spatial.transform import Rotation
import g20_thumb_apriltag_calibration.extrinsics_node as extrinsics_node
from g20_thumb_apriltag_calibration.extrinsics_node import (
import linkerhand_calibration.extrinsics_node as extrinsics_node
from linkerhand_calibration.extrinsics_node import (
BoardPose,
StereoCapture,
_fit_stereo_robust,
@@ -5,7 +5,7 @@ from pathlib import Path
import numpy as np
import pytest
from g20_thumb_apriltag_calibration.full_hand import (
from linkerhand_calibration.full_hand import (
ACTIVE_JOINTS,
IMAGE_TRAJECTORY_JOINTS,
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:
from g20_thumb_apriltag_calibration.urdf_zero import (
from linkerhand_calibration.urdf_zero import (
get_zero_calibration_profile,
)
@@ -10,11 +10,11 @@ import numpy as np
import pytest
from scipy.spatial.transform import Rotation
from g20_thumb_apriltag_calibration.core import (
from linkerhand_calibration.core import (
DIRECTION_DECREASING,
DIRECTION_INCREASING,
)
from g20_thumb_apriltag_calibration.full_hand import (
from linkerhand_calibration.full_hand import (
G20_COMBINATION_REQUIRED_TARGET_KEYS,
G20_REFERENCE_THUMB_CMC_JOINTS,
G20_RIGHT_19_LAYOUT,
@@ -22,21 +22,21 @@ from g20_thumb_apriltag_calibration.full_hand import (
build_compact_payload,
get_hand_calibration_profile,
)
from g20_thumb_apriltag_calibration.operator_report import (
from linkerhand_calibration.operator_report import (
ProgressEstimator,
build_failure_report,
classify_error,
render_progress_zh,
)
from g20_thumb_apriltag_calibration.one_command import (
from linkerhand_calibration.one_command import (
_automatic_resume_candidate,
_calibration_node_exited_before_status,
_launch_command,
_resolve_partial_base_session,
_status_timeout_seconds,
)
from g20_thumb_apriltag_calibration.product import load_product_config
from g20_thumb_apriltag_calibration.publication import (
from linkerhand_calibration.product import load_product_config
from linkerhand_calibration.publication import (
ACTIVE_ZERO_JOINTS,
PASSIVE_JOINTS,
atomic_session_pointer,
@@ -49,8 +49,8 @@ from g20_thumb_apriltag_calibration.publication import (
verify_partial_scope_preserves_certified_zeros,
verify_urdf_mesh_resources,
)
from g20_thumb_apriltag_calibration.storage import atomic_write_json
from g20_thumb_apriltag_calibration.three_camera_node import (
from linkerhand_calibration.storage import atomic_write_json
from linkerhand_calibration.three_camera_node import (
G20ThreeCameraCalibrationNode,
STEADY_COMMAND_CHECKPOINTS,
SweepItem,
@@ -70,7 +70,7 @@ from g20_thumb_apriltag_calibration.three_camera_node import (
resume_tasks_invalidated_by_tag_size_changes,
resumable_completed_task_prefix,
)
from g20_thumb_apriltag_calibration.urdf_zero import (
from linkerhand_calibration.urdf_zero import (
RIGHT_19_MECHANICAL_ENDPOINT_JOINTS,
UrdfKinematicModel,
get_zero_calibration_profile,
@@ -79,7 +79,7 @@ from g20_thumb_apriltag_calibration.urdf_zero import (
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):
@@ -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 config.source_urdf.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:
@@ -3,7 +3,7 @@ from pathlib import Path
import pytest
import yaml
from g20_thumb_apriltag_calibration.hikrobot_camera import (
from linkerhand_calibration.hikrobot_camera import (
DeviceDescriptor,
decode_c_string,
load_camera_calibration,
@@ -1,6 +1,6 @@
import pytest
from g20_thumb_apriltag_calibration.offline_replay import (
from linkerhand_calibration.offline_replay import (
_latest_attempt_records,
_latest_palm_axis_records,
_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
from scipy.spatial.transform import Rotation
from g20_thumb_apriltag_calibration.pnp import (
from linkerhand_calibration.pnp import (
SquareTagGroupPoseTracker,
SquareTagPose,
SquareTagPoseTracker,
@@ -1,6 +1,6 @@
"""Tests for the explicit calibration sample command domains."""
from g20_thumb_apriltag_calibration.sample_schema import (
from linkerhand_calibration.sample_schema import (
SampleDataContractError,
canonical_sample_record,
fitting_sample_record,
@@ -1,4 +1,4 @@
from g20_thumb_apriltag_calibration.storage import (
from linkerhand_calibration.storage import (
append_jsonl,
atomic_write_json,
completed_scan_keys,
@@ -2,7 +2,7 @@ import json
import numpy as np
from g20_thumb_apriltag_calibration.three_camera_diagnostics import (
from linkerhand_calibration.three_camera_diagnostics import (
_task_text,
render_three_camera_status_text_zh,
three_camera_reason_zh,
@@ -8,13 +8,13 @@ import numpy as np
import pytest
from scipy.spatial.transform import Rotation
import g20_thumb_apriltag_calibration.three_camera_node as three_camera_node
from g20_thumb_apriltag_calibration.core import (
import linkerhand_calibration.three_camera_node as three_camera_node
from linkerhand_calibration.core import (
DIRECTION_DECREASING,
DIRECTION_INCREASING,
)
from g20_thumb_apriltag_calibration.acquisition import TagQuality
from g20_thumb_apriltag_calibration.full_hand import (
from linkerhand_calibration.acquisition import TagQuality
from linkerhand_calibration.full_hand import (
JointCurveFit,
PalmAxisObserver,
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_return_waypoints,
)
from g20_thumb_apriltag_calibration.three_camera_node import (
from linkerhand_calibration.three_camera_node import (
FrameObservation,
G20ThreeCameraCalibrationNode,
STATE_COMPLETE,
@@ -56,8 +56,8 @@ from g20_thumb_apriltag_calibration.three_camera_node import (
_sweep_views,
_thumb_yaw_zero_repeatability_failures,
)
from g20_thumb_apriltag_calibration.pnp import SquareTagPose
from g20_thumb_apriltag_calibration.urdf_zero import (
from linkerhand_calibration.pnp import SquareTagPose
from linkerhand_calibration.urdf_zero import (
RIGHT_19_MECHANICAL_ENDPOINT_JOINTS,
JointAxisMeasurement,
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)
gaps_deg = [0.10, 0.81, 0.10] # range 0.71 deg, maximum below 2 deg
monkeypatch.setattr(
"g20_thumb_apriltag_calibration.three_camera_node"
"linkerhand_calibration.three_camera_node"
".baseline_hysteresis_by_cycle_rad",
lambda records, zero_command_u8, axis_xyz: [
math.radians(value) for value in gaps_deg
@@ -4,7 +4,7 @@ import numpy as np
import pytest
from scipy.spatial.transform import Rotation
from g20_thumb_apriltag_calibration.core import (
from linkerhand_calibration.core import (
DIRECTION_DECREASING,
DIRECTION_INCREASING,
PAIR_IP,
@@ -14,7 +14,7 @@ from g20_thumb_apriltag_calibration.core import (
PHASE_TIP,
create_final_payload,
)
from g20_thumb_apriltag_calibration.trajectory import (
from linkerhand_calibration.trajectory import (
_regularize_coupled_zero_tail,
fit_center_trajectory_curves,
maximum_center_non_target_drift_rad,
@@ -6,12 +6,12 @@ import numpy as np
import pytest
from scipy.spatial.transform import Rotation
from g20_thumb_apriltag_calibration.extrinsics import (
from linkerhand_calibration.extrinsics import (
camera_info_fingerprint,
dump_three_camera_extrinsics,
load_three_camera_extrinsics,
)
from g20_thumb_apriltag_calibration.urdf_zero import (
from linkerhand_calibration.urdf_zero import (
AXIS_JOINTS,
DIRECT_ZERO_JOINTS,
INHERITED_ZERO_JOINTS,
@@ -35,7 +35,7 @@ from g20_thumb_apriltag_calibration.urdf_zero import (
merge_right_19_thumb_zero_result,
write_zero_corrected_urdf,
)
from g20_thumb_apriltag_calibration.full_hand import (
from linkerhand_calibration.full_hand import (
ACTIVE_JOINTS,
JOINT_SPECS,
MEASURED_JOINTS,
@@ -3,7 +3,7 @@ from __future__ import annotations
import math
import cv2
from g20_thumb_apriltag_calibration.zero_calibration import (
from linkerhand_calibration.zero_calibration import (
build_trajectory_zero_angle_payload,
build_trajectory_zero_travel_payload,
circular_median_rad,