换台电脑克隆后启动失败,是因为 site 和启动脚本写死了 /home/cc/ros2。仓库内路径改成相对仓库根,加载时再展开;缺 install 时先说明要编译,而不是 source 一个不存在的文件。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
arm_dev
2026-10-15 10:51:24 +08:00
parent ab73399426
commit 3c86441fb0
25 changed files with 254 additions and 91 deletions
@@ -137,7 +137,9 @@ class HandeyeCalibrate(TolerantNode):
self.declare_parameter('pin_hold_sec', 0.45) # 每拍多长
self.declare_parameter('skip_unstable_poses', True)
self.declare_parameter('output_dir', '/home/cc/ros2/handeye/tmp')
from arm_application.site_io import ros2_root
self.declare_parameter('output_dir', str(ros2_root() / 'handeye' / 'tmp'))
self.declare_parameter('result_file', '')
self.declare_parameter('cleanup_images', True)
# 眼在手上:AX=XB 的 gripper 用 Studio 工具 TCP,再折成 cam→法兰写 T_4x4。
@@ -108,10 +108,12 @@ class RdkBridge:
self.robot_sn = robot_sn
self._last_q: Optional[List[float]] = None
self._io = threading.Lock()
py = os.environ.get('RDK_PYTHON', '/home/cc/rdk_py310/bin/python')
bridge = Path.home() / 'ros2' / 'scripts' / 'rdk_drag_bridge.py'
if not bridge.is_file():
bridge = Path('/home/cc/ros2/scripts/rdk_drag_bridge.py')
from arm_application.site_io import ros2_root
py = os.environ.get('RDK_PYTHON', '').strip() or str(
Path.home() / 'rdk_py310' / 'bin' / 'python'
)
bridge = ros2_root() / 'scripts' / 'rdk_drag_bridge.py'
if not bridge.is_file():
raise FileNotFoundError(f'找不到 rdk_drag_bridge.py:{bridge}')
@@ -246,7 +248,9 @@ class RdkBridge:
class HandeyeTeach(TolerantNode):
def __init__(self) -> None:
super().__init__('handeye_teach')
self.declare_parameter('output_dir', '/home/cc/ros2/handeye/tmp')
from arm_application.site_io import ros2_root
self.declare_parameter('output_dir', str(ros2_root() / 'handeye' / 'tmp'))
self.declare_parameter('width', 1280)
self.declare_parameter('height', 720)
self.declare_parameter('fps', 15)
@@ -105,7 +105,7 @@ class PerceptionNode(TolerantNode):
self.declare_parameter("image_topic", "")
self.declare_parameter("camera_info_topic", "")
self.declare_parameter("depth_topic", "/d405/aligned_depth_to_color/image_raw")
self.declare_parameter("weights", "/home/cc/ros2/models/yolo/gongjian0929.onnx")
self.declare_parameter("weights", "models/yolo/gongjian0929.onnx")
self.declare_parameter("conf", 0.10)
self.declare_parameter("iou", 0.45)
self.declare_parameter("imgsz", 640)
@@ -336,7 +336,9 @@ class PerceptionNode(TolerantNode):
r_pos=float(self.get_parameter("kf_r_pos").value),
)
weights_raw = str(self.get_parameter("weights").value)
from arm_application.site_io import resolve_repo_path
weights_raw = resolve_repo_path(str(self.get_parameter("weights").value))
weights = str(resolve_weights(weights_raw, self.backend_name))
yolo_cfg = {
"weights": weights,
@@ -73,15 +73,9 @@ def _fill_camera_info(
def _site_yaml_path() -> Path:
env = str(os.environ.get("SITE_CONFIG") or "").strip()
if env:
p = Path(env).expanduser()
if p.is_file():
return p
home = Path.home() / "ros2" / "config" / "site.yaml"
if home.is_file():
return home
return Path("/home/cc/ros2/config/site.yaml")
from arm_application.site_io import site_path
return site_path()
class RealsenseColorPub(TolerantNode):
@@ -126,7 +126,7 @@ class ServoNode(TolerantNode):
self.declare_parameter("point_topic", "visual_servo/filtered_point")
# 默认值只作兜底:真机运行一定由 launch 传入 site.yaml 的 paths.handeye_file。
# 指向 /tmp 是历史遗留(标定临时产物),会让人误以为"标定结果在那里"。
self.declare_parameter("handeye_file", "/home/cc/ros2/handeye/results/89_eye_in_hand.yaml")
self.declare_parameter("handeye_file", "handeye/results/89_eye_in_hand.yaml")
# eye_in_hand = cam→法兰;eye_to_hand = cam→基座。写错会把固定相机当腕部用。
self.declare_parameter("handeye_mode", "eye_in_hand")
self.declare_parameter("use_static_handeye_tf", False)
@@ -374,9 +374,10 @@ class ServoNode(TolerantNode):
self.ws_min = np.array([float(v) for v in ws_min[:3]], dtype=float)
self.ws_max = np.array([float(v) for v in ws_max[:3]], dtype=float)
self.T_cam_to_gripper = load_handeye_T(
str(self.get_parameter("handeye_file").value)
)
from arm_application.site_io import resolve_repo_path
handeye_path = resolve_repo_path(str(self.get_parameter("handeye_file").value))
self.T_cam_to_gripper = load_handeye_T(handeye_path)
self.handeye_mode = str(
self.get_parameter("handeye_mode").value or "eye_in_hand"
).strip().lower()
@@ -399,7 +400,7 @@ class ServoNode(TolerantNode):
t_he = np.asarray(self.T_cam_to_gripper[:3, 3], dtype=float)
axis_z = float(np.clip(R_he[2, 2], -1.0, 1.0))
self._cam_tilt_deg = float(np.degrees(np.arccos(axis_z)))
he_path = str(self.get_parameter("handeye_file").value)
he_path = handeye_path
if self.handeye_mode == "eye_to_hand":
self.get_logger().warn(
f"HANDEYE yaml={he_path} mode=eye_to_hand "
+75 -1
View File
@@ -10,13 +10,83 @@ import yaml
def ros2_root() -> Path:
"""仓库根目录。先看环境变量,再从本文件向上找 ``config/site.yaml``。
不能默认 ``~/ros2``:别人把仓库克隆到别的目录时,写死的家目录路径是空的,
启动会报文件不存在。
"""
for key in ("SITE_ROS2_ROOT", "ROS2_WS"):
v = os.environ.get(key, "").strip()
if v:
return Path(v).expanduser()
here = Path(__file__).resolve()
for parent in here.parents:
if (parent / "config" / "site.yaml").is_file() and (parent / "scripts" / "env.sh").is_file():
return parent
return Path.home() / "ros2"
_PATH_KEYS = frozenset({
"named_poses_file",
"handeye_file",
"handeye_waypoints",
"handeye_workdir",
"handeye_calibrate",
"yolo_weights",
"hole_weights",
"weights",
"workpiece_weights",
"config_file",
"append_poses_file",
})
def resolve_repo_path(value: str, root: Path | None = None) -> str:
"""仓库内的相对路径展开成绝对路径。已经是绝对路径的原样保留。"""
text = str(value or "").strip()
if not text:
return ""
path = Path(text).expanduser()
if path.is_absolute():
return str(path)
base = root or ros2_root()
return str((base / path).resolve())
def _is_path_key(key: str) -> bool:
return (
key in _PATH_KEYS
or key.startswith("handeye_file")
or key.startswith("handeye_waypoints")
or key.endswith("_weights")
or key.endswith("_file")
or key.endswith("_workdir")
)
def absolutize_site_paths(data: Any, root: Path | None = None) -> Any:
"""把 site 里的仓库相对路径展开。只动路径字段,不动别的字符串。"""
base = root or ros2_root()
if isinstance(data, dict):
out: dict[Any, Any] = {}
for key, val in data.items():
name = str(key)
if isinstance(val, str) and _is_path_key(name):
out[key] = resolve_repo_path(val, base)
elif (
name == "config"
and isinstance(val, str)
and (val.endswith((".yaml", ".yml", ".json")) or "/" in val)
):
out[key] = resolve_repo_path(val, base)
else:
out[key] = absolutize_site_paths(val, base)
return out
if isinstance(data, list):
return [absolutize_site_paths(item, base) for item in data]
return data
def site_path() -> Path:
env = os.environ.get("SITE_CONFIG", "").strip()
if env:
@@ -29,7 +99,11 @@ def load_site_dict(path: str | Path | None = None) -> dict[str, Any]:
if not p.is_file():
return {}
data = yaml.safe_load(p.read_text(encoding="utf-8")) or {}
return data if isinstance(data, dict) else {}
if not isinstance(data, dict):
return {}
resolved = p.resolve()
root = resolved.parent.parent if resolved.parent.name == "config" else ros2_root()
return absolutize_site_paths(data, root)
def deep_merge(base: dict[str, Any], overlay: dict[str, Any]) -> dict[str, Any]:
+1 -1
View File
@@ -12,7 +12,7 @@ perception_node:
image_topic: ""
camera_info_topic: ""
depth_topic: "/d405/aligned_depth_to_color/image_raw"
weights: "/home/cc/ros2/models/yolo/gongjian0929.onnx"
weights: "models/yolo/gongjian0929.onnx"
conf: 0.10
iou: 0.45
imgsz: 640
+1 -1
View File
@@ -10,7 +10,7 @@ servo_node:
camera_frame: "d405_color_optical_frame"
traj_topic: "rizon_arm_controller/joint_trajectory"
point_topic: "visual_servo/filtered_point"
handeye_file: "/home/cc/ros2/src/arm_application/config/handeye_result.yaml"
handeye_file: "src/arm_application/config/handeye_result.yaml"
use_static_handeye_tf: false
handeye_child_frame: "d405_color_optical_frame"