feat: 接入番茄采摘ACT采集启动项
This commit is contained in:
@@ -0,0 +1,33 @@
|
|||||||
|
act_episode_recorder:
|
||||||
|
ros__parameters:
|
||||||
|
output_root: /home/robot/ACT_Data
|
||||||
|
task_name: tomato_pick
|
||||||
|
control_sample_topic: /xr_rm/right_rm75/act_control_sample
|
||||||
|
right_controller_topic: /xr/right_controller
|
||||||
|
left_controller_topic: /xr/left_controller
|
||||||
|
status_topic: /act/recording_status
|
||||||
|
|
||||||
|
cam_high_serial: "234222303366"
|
||||||
|
cam_high_model: D455
|
||||||
|
cam_right_wrist_serial: "412622272532"
|
||||||
|
cam_right_wrist_model: D405
|
||||||
|
image_width: 640
|
||||||
|
image_height: 480
|
||||||
|
camera_fps: 30
|
||||||
|
camera_warmup_sec: 5.0
|
||||||
|
|
||||||
|
control_rate_hz: 90.0
|
||||||
|
sample_rate_hz: 30.0
|
||||||
|
min_samples: 60
|
||||||
|
max_samples: 1800
|
||||||
|
min_control_hz: 27.0
|
||||||
|
max_control_gap_ms: 100.0
|
||||||
|
min_camera_fps: 27.0
|
||||||
|
max_drop_ratio: 0.01
|
||||||
|
max_feedback_age_ms: 50.0
|
||||||
|
max_camera_age_ms: 50.0
|
||||||
|
max_camera_skew_ms: 50.0
|
||||||
|
min_free_space_gib: 4.0
|
||||||
|
y_hold_sec: 1.0
|
||||||
|
gripper_completion_timeout_sec: 3.0
|
||||||
|
writer_queue_size: 8
|
||||||
@@ -80,6 +80,29 @@ def _validate_mujoco_mode(arm: str, use_mujoco: bool) -> None:
|
|||||||
raise ValueError("use_mujoco:=true requires arm:=both")
|
raise ValueError("use_mujoco:=true requires arm:=both")
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_act_mode(
|
||||||
|
arm: str,
|
||||||
|
use_mock: bool,
|
||||||
|
record_act: bool,
|
||||||
|
) -> None:
|
||||||
|
if record_act and (arm != "right" or use_mock):
|
||||||
|
raise ValueError(
|
||||||
|
"record_act:=true requires arm:=right use_mock:=false"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _act_recorder_node() -> Node:
|
||||||
|
"""创建独立 ACT 数据采集节点;退出时不终止遥操作。"""
|
||||||
|
return Node(
|
||||||
|
package="xr_rm_teleop",
|
||||||
|
executable="act_episode_recorder",
|
||||||
|
name="act_episode_recorder",
|
||||||
|
output="screen",
|
||||||
|
prefix=[XR_PYTHON],
|
||||||
|
parameters=[_config_file("act_tomato_pick.yaml")],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _single_arm_node(
|
def _single_arm_node(
|
||||||
arm: str,
|
arm: str,
|
||||||
use_mock: bool,
|
use_mock: bool,
|
||||||
@@ -164,10 +187,14 @@ def _launch_setup(context, *args, **kwargs):
|
|||||||
use_mujoco = _as_bool(
|
use_mujoco = _as_bool(
|
||||||
LaunchConfiguration("use_mujoco").perform(context)
|
LaunchConfiguration("use_mujoco").perform(context)
|
||||||
)
|
)
|
||||||
|
record_act = _as_bool(
|
||||||
|
LaunchConfiguration("record_act").perform(context)
|
||||||
|
)
|
||||||
|
|
||||||
if arm not in ("left", "right", "both"):
|
if arm not in ("left", "right", "both"):
|
||||||
raise ValueError("arm must be one of: left, right, both")
|
raise ValueError("arm must be one of: left, right, both")
|
||||||
_validate_mujoco_mode(arm, use_mujoco)
|
_validate_mujoco_mode(arm, use_mujoco)
|
||||||
|
_validate_act_mode(arm, use_mock, record_act)
|
||||||
|
|
||||||
nodes = [_udp_receiver_node()]
|
nodes = [_udp_receiver_node()]
|
||||||
if arm == "both":
|
if arm == "both":
|
||||||
@@ -176,6 +203,8 @@ def _launch_setup(context, *args, **kwargs):
|
|||||||
nodes.append(_single_arm_node(arm, use_mock))
|
nodes.append(_single_arm_node(arm, use_mock))
|
||||||
if use_mujoco:
|
if use_mujoco:
|
||||||
nodes.append(_mujoco_node())
|
nodes.append(_mujoco_node())
|
||||||
|
if record_act:
|
||||||
|
nodes.append(_act_recorder_node())
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
|
|
||||||
@@ -187,6 +216,8 @@ def generate_launch_description() -> LaunchDescription:
|
|||||||
DeclareLaunchArgument("use_mock", default_value="true"),
|
DeclareLaunchArgument("use_mock", default_value="true"),
|
||||||
# true 时额外启动只读 MuJoCo 双臂显示,默认不改变现有启动行为。
|
# true 时额外启动只读 MuJoCo 双臂显示,默认不改变现有启动行为。
|
||||||
DeclareLaunchArgument("use_mujoco", default_value="false"),
|
DeclareLaunchArgument("use_mujoco", default_value="false"),
|
||||||
|
# true 时只允许右臂真机,并启动独立 ACT 数据采集节点。
|
||||||
|
DeclareLaunchArgument("record_act", default_value="false"),
|
||||||
# UDP 监听参数,需要与 PICO 端或 sample_udp_sender 保持一致。
|
# UDP 监听参数,需要与 PICO 端或 sample_udp_sender 保持一致。
|
||||||
DeclareLaunchArgument("udp_host", default_value="0.0.0.0"),
|
DeclareLaunchArgument("udp_host", default_value="0.0.0.0"),
|
||||||
DeclareLaunchArgument("udp_port", default_value="15000"),
|
DeclareLaunchArgument("udp_port", default_value="15000"),
|
||||||
|
|||||||
@@ -41,3 +41,37 @@ def test_udp_receiver_exit_shuts_down_launch() -> None:
|
|||||||
receiver = arm_debug_launch._udp_receiver_node()
|
receiver = arm_debug_launch._udp_receiver_node()
|
||||||
|
|
||||||
assert isinstance(receiver._ExecuteLocal__on_exit, Shutdown)
|
assert isinstance(receiver._ExecuteLocal__on_exit, Shutdown)
|
||||||
|
|
||||||
|
|
||||||
|
def test_act_recording_is_disabled_by_default() -> None:
|
||||||
|
description = arm_debug_launch.generate_launch_description()
|
||||||
|
arguments = {
|
||||||
|
entity.name: entity
|
||||||
|
for entity in description.entities
|
||||||
|
if isinstance(entity, DeclareLaunchArgument)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert perform_substitutions(
|
||||||
|
LaunchContext(),
|
||||||
|
arguments["record_act"].default_value,
|
||||||
|
) == "false"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("arm", "use_mock"),
|
||||||
|
[("left", False), ("both", False), ("right", True)],
|
||||||
|
)
|
||||||
|
def test_act_recording_rejects_unsupported_modes(arm, use_mock) -> None:
|
||||||
|
with pytest.raises(ValueError, match="arm:=right use_mock:=false"):
|
||||||
|
arm_debug_launch._validate_act_mode(arm, use_mock, True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_act_recording_accepts_right_real_mode() -> None:
|
||||||
|
arm_debug_launch._validate_act_mode("right", False, True)
|
||||||
|
arm_debug_launch._validate_act_mode("both", True, False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_act_recorder_exit_does_not_shutdown_teleoperation() -> None:
|
||||||
|
recorder = arm_debug_launch._act_recorder_node()
|
||||||
|
|
||||||
|
assert recorder._ExecuteLocal__on_exit is None
|
||||||
|
|||||||
Reference in New Issue
Block a user