refactor: 插件化重构 — 重命名包、draccus 配置迁移、上下文注册、Pika 复用
1. 重命名 ufactory_lerobot → lerobot_robot_ufactory 以独立插件包形式支持 lerobot 加载,pyproject.toml 同步更新 2. 配置文件迁移至 draccus 格式 所有 YAML config 从 _target_ 格式迁移至 ChoiceRegistry type 格式 dict 值类型从具体类改为抽象基类(RobotConfig/TeleoperatorConfig) 确保嵌套 dict 中的 type 选择器能被 draccus 正确解析 3. teleop 上下文注册替代属性注入 新增 context.py(contextvars 实现),支持多 teleop 按 id 存取 UFBaseTeleop.connect/disconnect 自动注册/注销活跃 teleop UFMockRobot.get_observation 从上下文获取 teleop action 移除 record() 对 cfg.robot.teleop 的注入 4. 部分导入延迟加载 pika_device: serial/list_ports 延迟导入 gello_teleop: gello 依赖延迟导入 umi_teleop/camera_umi: XVLib 延迟导入 space_mouse: spnav 延迟导入 5. Pika 设备实例复用 PIKA_DEVICE_MAP 共享已连接的 Sense/Gripper,防止多实例化抢占端口 修复 pika_gripper 属性误用 _pika_sense_port 的 bug 6. 修复 multiple_uf_mock_robot robots 从 list 改为 dict[str, UFMockRobot],移除 keys 列表 send_action 改用 dict items 遍历 新增 MultipleUFMockRobot.cameras 聚合属性
This commit is contained in:
parent
83a1c46441
commit
bdc8ef05ab
4
.gitignore
vendored
4
.gitignore
vendored
@ -6,8 +6,8 @@ __pycache__/
|
||||
*$py.class
|
||||
*.so
|
||||
*.so.*
|
||||
!src/ufactory_lerobot/devices/umi/xvlib/lib*.so
|
||||
!src/ufactory_lerobot/devices/umi/xvlib/lib*.so.*
|
||||
!src/lerobot_robot_ufactory/devices/umi/xvlib/lib*.so
|
||||
!src/lerobot_robot_ufactory/devices/umi/xvlib/lib*.so.*
|
||||
*.egg
|
||||
*.egg-info/
|
||||
dist/
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
RobotConfig:
|
||||
_target_: ufactory_lerobot.robots.uf_robot.UFRobotConfig
|
||||
robot:
|
||||
type: uf::robot
|
||||
id: "uf_robot"
|
||||
robot_dof: 5
|
||||
control_space: "joint"
|
||||
@ -7,20 +7,9 @@ RobotConfig:
|
||||
gripper_type: 1
|
||||
start_joints: [0, 0, -90, 90, 0]
|
||||
|
||||
DatasetRecordConfig:
|
||||
_target_: lerobot.scripts.lerobot_record.DatasetRecordConfig
|
||||
# root of local repo: /home/<user_name>/.cache/huggingface/lerobot (default)
|
||||
root: "/home/uf/Data/lerobot_datas/record/ufactory/xarm5_gello_datas"
|
||||
repo_id: "ufactory/xarm5_gello_datas"
|
||||
single_task: "Pick up the purple grape and drop into the box on the left."
|
||||
fps: 30
|
||||
episode_time_s: 60 # max duration for one episode
|
||||
reset_time_s: 20 # (invalid) time for resetting env between episodes
|
||||
push_to_hub: False
|
||||
|
||||
# make sure to edit with your correct configurations!
|
||||
TeleoperatorConfig:
|
||||
_target_: ufactory_lerobot.teleoperators.gello_teleop.GelloTeleopConfig
|
||||
teleop:
|
||||
type: uf::gello_teleop
|
||||
port: "/dev/serial/by-id/usb-FTDI_USB__-__Serial_Converter_FTAJZYC7-if00-port0"
|
||||
joint_ids: [1, 2, 4, 6, 7]
|
||||
joint_signs: [1, 1, -1, 1, 1]
|
||||
@ -28,3 +17,12 @@ TeleoperatorConfig:
|
||||
gripper_id: 8
|
||||
torque_joint_ids: [3, 5]
|
||||
|
||||
dataset:
|
||||
# root of local repo: /home/<user_name>/.cache/huggingface/lerobot (default)
|
||||
root: "/home/uf/Data/lerobot_datas/record/ufactory/xarm5_gello_datas"
|
||||
repo_id: "ufactory/xarm5_gello_datas"
|
||||
single_task: "Pick up the purple grape and drop into the box on the left."
|
||||
fps: 30
|
||||
episode_time_s: 60 # max duration for one episode
|
||||
reset_time_s: 20 # time for resetting env between episodes
|
||||
push_to_hub: False
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
RobotConfig:
|
||||
_target_: ufactory_lerobot.robots.uf_robot.UFRobotConfig
|
||||
robot:
|
||||
type: uf::robot
|
||||
id: "uf_robot"
|
||||
robot_dof: 6
|
||||
control_space: "joint"
|
||||
@ -7,20 +7,9 @@ RobotConfig:
|
||||
gripper_type: 1
|
||||
start_joints: [0, 0, -90, 0, 90, 0]
|
||||
|
||||
DatasetRecordConfig:
|
||||
_target_: lerobot.scripts.lerobot_record.DatasetRecordConfig
|
||||
# root of local repo: /home/<user_name>/.cache/huggingface/lerobot (default)
|
||||
root: "/home/uf/Data/lerobot_datas/record/ufactory/xarm6_gello_datas"
|
||||
repo_id: "ufactory/xarm6_gello_datas"
|
||||
single_task: "Pick up the purple grape and drop into the box on the left."
|
||||
fps: 30
|
||||
episode_time_s: 60 # max duration for one episode
|
||||
reset_time_s: 20 # (invalid) time for resetting env between episodes
|
||||
push_to_hub: False
|
||||
|
||||
# make sure to edit with your correct configurations!
|
||||
TeleoperatorConfig:
|
||||
_target_: ufactory_lerobot.teleoperators.gello_teleop.GelloTeleopConfig
|
||||
teleop:
|
||||
type: uf::gello_teleop
|
||||
port: "/dev/serial/by-id/usb-FTDI_USB__-__Serial_Converter_FTAJZYC7-if00-port0"
|
||||
joint_ids: [1, 2, 4, 5, 6, 7]
|
||||
joint_signs: [1, 1, -1, 1, 1, 1]
|
||||
@ -28,3 +17,12 @@ TeleoperatorConfig:
|
||||
gripper_id: 8
|
||||
torque_joint_ids: [3]
|
||||
|
||||
dataset:
|
||||
# root of local repo: /home/<user_name>/.cache/huggingface/lerobot (default)
|
||||
root: "/home/uf/Data/lerobot_datas/record/ufactory/xarm6_gello_datas"
|
||||
repo_id: "ufactory/xarm6_gello_datas"
|
||||
single_task: "Pick up the purple grape and drop into the box on the left."
|
||||
fps: 30
|
||||
episode_time_s: 60 # max duration for one episode
|
||||
reset_time_s: 20 # time for resetting env between episodes
|
||||
push_to_hub: False
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
RobotConfig:
|
||||
_target_: ufactory_lerobot.robots.uf_robot.UFRobotConfig
|
||||
robot:
|
||||
type: uf::robot
|
||||
id: "uf_robot"
|
||||
robot_dof: 7
|
||||
control_space: "joint"
|
||||
@ -7,21 +7,19 @@ RobotConfig:
|
||||
gripper_type: 1
|
||||
start_joints: [0, 0, 0, 90, 0, 90, 0]
|
||||
|
||||
DatasetRecordConfig:
|
||||
_target_: lerobot.scripts.lerobot_record.DatasetRecordConfig
|
||||
# make sure to edit with your correct configurations!
|
||||
teleop:
|
||||
type: uf::gello_teleop
|
||||
id: "gello_teleop"
|
||||
port: "/dev/serial/by-id/usb-FTDI_USB__-__Serial_Converter_FTAJZYC7-if00-port0"
|
||||
start_joints: [0, 0, 0, 90, 0, 90, 0]
|
||||
|
||||
dataset:
|
||||
# root of local repo: /home/<user_name>/.cache/huggingface/lerobot (default)
|
||||
root: "/home/uf/Data/lerobot_datas/record/ufactory/xarm6_gello_datas"
|
||||
repo_id: "ufactory/xarm6_gello_datas"
|
||||
single_task: "Pick up the purple grape and drop into the box on the left."
|
||||
fps: 30
|
||||
episode_time_s: 60 # max duration for one episode
|
||||
reset_time_s: 20 # (invalid) time for resetting env between episodes
|
||||
reset_time_s: 20 # time for resetting env between episodes
|
||||
push_to_hub: False
|
||||
|
||||
# make sure to edit with your correct configurations!
|
||||
TeleoperatorConfig:
|
||||
_target_: ufactory_lerobot.teleoperators.gello_teleop.GelloTeleopConfig
|
||||
id: "gello_teleop"
|
||||
port: "/dev/serial/by-id/usb-FTDI_USB__-__Serial_Converter_FTAJZYC7-if00-port0"
|
||||
start_joints: [0, 0, 0, 90, 0, 90, 0]
|
||||
|
||||
|
||||
@ -1,21 +1,29 @@
|
||||
RobotConfig:
|
||||
_target_: ufactory_lerobot.robots.uf_mock_robot.UFMockRobotConfig
|
||||
robot:
|
||||
type: uf::mock_robot
|
||||
id: "uf_mock_robot"
|
||||
control_space: "cartesian"
|
||||
gripper_type: 2
|
||||
state_offset_action: 3
|
||||
|
||||
teleop_id: "pika_xxxx"
|
||||
cameras:
|
||||
fisheye:
|
||||
_target_: lerobot.cameras.opencv.configuration_opencv.OpenCVCameraConfig
|
||||
type: opencv
|
||||
index_or_path: "/dev/v4l/by-id/usb-DECXIN_CAMERA_DECXIN_CAMERA_01.00.00-video-index0"
|
||||
width: 640
|
||||
height: 480
|
||||
fps: 30
|
||||
fourcc: "MJPG"
|
||||
|
||||
DatasetRecordConfig:
|
||||
_target_: lerobot.scripts.lerobot_record.DatasetRecordConfig
|
||||
# make sure to edit with your correct configurations!
|
||||
teleop:
|
||||
type: uf::pika_teleop
|
||||
id: "pika_xxxx"
|
||||
# port: "/dev/ttyUSB0"
|
||||
scale_xyz: 1.5
|
||||
tracker_to_robot_eef: [0, 0, 0, 180, -90, 0]
|
||||
robot_base_pose: [400, 0, 400, 180, 0, 0]
|
||||
|
||||
dataset:
|
||||
# root of local repo: /home/<user_name>/.cache/huggingface/lerobot (default)
|
||||
root: "/home/uf/Data/lerobot_datas/record/ufactory/pika_datas"
|
||||
repo_id: "ufactory/pika_datas"
|
||||
@ -24,11 +32,3 @@ DatasetRecordConfig:
|
||||
episode_time_s: 60 # max duration for one episode
|
||||
reset_time_s: 20 # (invalid) time for resetting env between episodes
|
||||
push_to_hub: False
|
||||
|
||||
# make sure to edit with your correct configurations!
|
||||
TeleoperatorConfig:
|
||||
_target_: ufactory_lerobot.teleoperators.pika_teleop.PikaTeleopConfig
|
||||
# port: "/dev/ttyUSB0"
|
||||
scale_xyz: 1.5
|
||||
tracker_to_robot_eef: [0, 0, 0, 180, -90, 0]
|
||||
robot_base_pose: [400, 0, 400, 180, 0, 0]
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
RobotConfig:
|
||||
_target_: ufactory_lerobot.robots.uf_robot.UFRobotConfig
|
||||
robot:
|
||||
type: uf::robot
|
||||
id: "uf_robot"
|
||||
robot_dof: 7
|
||||
control_space: "cartesian"
|
||||
@ -8,18 +8,24 @@ RobotConfig:
|
||||
max_linear_velocity: 200
|
||||
start_joints: [-0.2, -13.9, -0.3, 52.6, 0.9, 66.6, 0.2]
|
||||
start_tcp_pose: [400, 0, 400, 180, 0, 0]
|
||||
|
||||
cameras:
|
||||
fisheye:
|
||||
_target_: lerobot.cameras.opencv.configuration_opencv.OpenCVCameraConfig
|
||||
type: opencv
|
||||
index_or_path: "/dev/v4l/by-id/usb-DECXIN_CAMERA_DECXIN_CAMERA_01.00.00-video-index0"
|
||||
width: 640
|
||||
height: 480
|
||||
fps: 30
|
||||
fourcc: "MJPG"
|
||||
|
||||
DatasetRecordConfig:
|
||||
_target_: lerobot.scripts.lerobot_record.DatasetRecordConfig
|
||||
# make sure to edit with your correct configurations!
|
||||
teleop:
|
||||
type: uf::pika_teleop
|
||||
# port: "/dev/ttyUSB0"
|
||||
scale_xyz: 1.5
|
||||
tracker_to_robot_eef: [0, 0, 0, 180, -90, 0]
|
||||
robot_base_pose: [400, 0, 400, 180, 0, 0]
|
||||
|
||||
dataset:
|
||||
# root of local repo: /home/<user_name>/.cache/huggingface/lerobot (default)
|
||||
root: "/home/uf/Data/lerobot_datas/record/ufactory/xarm7_pika_datas"
|
||||
repo_id: "ufactory/xarm7_pika_datas"
|
||||
@ -28,11 +34,3 @@ DatasetRecordConfig:
|
||||
episode_time_s: 60 # max duration for one episode
|
||||
reset_time_s: 20 # (invalid) time for resetting env between episodes
|
||||
push_to_hub: False
|
||||
|
||||
# make sure to edit with your correct configurations!
|
||||
TeleoperatorConfig:
|
||||
_target_: ufactory_lerobot.teleoperators.pika_teleop.PikaTeleopConfig
|
||||
# port: "/dev/ttyUSB0"
|
||||
scale_xyz: 1.5
|
||||
tracker_to_robot_eef: [0, 0, 0, 180, -90, 0]
|
||||
robot_base_pose: [400, 0, 400, 180, 0, 0]
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
RobotConfig:
|
||||
_target_: ufactory_lerobot.robots.uf_robot.UFRobotConfig
|
||||
robot:
|
||||
type: uf::robot
|
||||
id: "uf_robot"
|
||||
robot_dof: 7
|
||||
control_space: "cartesian"
|
||||
@ -8,20 +8,25 @@ RobotConfig:
|
||||
|
||||
cameras:
|
||||
overhead:
|
||||
_target_: lerobot.cameras.realsense.configuration_realsense.RealSenseCameraConfig
|
||||
type: intelrealsense
|
||||
serial_number_or_name: "Intel RealSense D435I"
|
||||
width: 640
|
||||
height: 480
|
||||
fps: 30
|
||||
tool:
|
||||
_target_: lerobot.cameras.realsense.configuration_realsense.RealSenseCameraConfig
|
||||
type: intelrealsense
|
||||
serial_number_or_name: "Intel RealSense D435"
|
||||
width: 640
|
||||
height: 480
|
||||
fps: 30
|
||||
|
||||
DatasetRecordConfig:
|
||||
_target_: lerobot.scripts.lerobot_record.DatasetRecordConfig
|
||||
# space mouse
|
||||
teleop:
|
||||
type: uf::spacemouse_teleop
|
||||
max_value: 300
|
||||
use_gripper: False
|
||||
|
||||
dataset:
|
||||
# root: /home/<user_name>/.cache/huggingface/lerobot (default)
|
||||
root: "/home/uf/Data/lerobot_datas/record/ufactory/xarm7_pushT"
|
||||
repo_id: "ufactory/xarm7_pushT"
|
||||
@ -30,9 +35,3 @@ DatasetRecordConfig:
|
||||
episode_time_s: 60 # max duration for one episode
|
||||
reset_time_s: 20 # (invalid) time for resetting env between episodes
|
||||
push_to_hub: False
|
||||
|
||||
# space mouse
|
||||
TeleoperatorConfig:
|
||||
_target_: ufactory_lerobot.teleoperators.space_mouse.SpaceMouseTeleopConfig
|
||||
max_value: 300
|
||||
use_gripper: False
|
||||
@ -1,42 +1,44 @@
|
||||
RobotConfig:
|
||||
_target_: ufactory_lerobot.robots.uf_mock_robot.MultipleUFMockRobotConfig
|
||||
robot:
|
||||
type: uf::multiple_mock_robot
|
||||
id: "multiple_uf_mock_robots"
|
||||
robots:
|
||||
left:
|
||||
_target_: ufactory_lerobot.robots.uf_mock_robot.UFMockRobotConfig
|
||||
type: uf::mock_robot
|
||||
id: "left_uf_mock_robot"
|
||||
control_space: "cartesian"
|
||||
gripper_type: 1
|
||||
state_offset_action: 3
|
||||
teleop_id: "umi_250801DR48FP26001318"
|
||||
cameras:
|
||||
fisheye:
|
||||
_target_: ufactory_lerobot.cameras.umi_camera.configuration_umi.UmiCameraConfig
|
||||
type: uf::umi_camera
|
||||
serial_number: "250801DR48FP26001318"
|
||||
width: 1280
|
||||
height: 1280
|
||||
fps: 30
|
||||
right:
|
||||
_target_: ufactory_lerobot.robots.uf_mock_robot.UFMockRobotConfig
|
||||
type: uf::mock_robot
|
||||
id: "right_uf_mock_robot"
|
||||
control_space: "cartesian"
|
||||
gripper_type: 1
|
||||
state_offset_action: 3
|
||||
teleop_id: "umi_250801DR48FP26001295"
|
||||
cameras:
|
||||
fisheye:
|
||||
_target_: ufactory_lerobot.cameras.umi_camera.configuration_umi.UmiCameraConfig
|
||||
type: uf::umi_camera
|
||||
serial_number: "250801DR48FP26001295"
|
||||
width: 1280
|
||||
height: 1280
|
||||
fps: 30
|
||||
|
||||
# make sure to edit with your correct configurations!
|
||||
TeleoperatorConfig:
|
||||
_target_: ufactory_lerobot.teleoperators.umi_teleop.MultipleUmiTeleopConfig
|
||||
teleop:
|
||||
type: uf::multiple_umi_teleop
|
||||
id: "multiple_teleops"
|
||||
teleops:
|
||||
left:
|
||||
_target_: ufactory_lerobot.teleoperators.umi_teleop.UmiTeleopConfig
|
||||
id: "left_teleop"
|
||||
type: uf::umi_teleop
|
||||
id: "umi_250801DR48FP26001318"
|
||||
serial_number: "250801DR48FP26001318"
|
||||
use_vive_tracker: False
|
||||
vive_tracker_id: "LHR-555DC7BF"
|
||||
@ -44,8 +46,8 @@ TeleoperatorConfig:
|
||||
tracker_to_robot_eef: [0, 0, 0, 0, 0, -90]
|
||||
robot_base_pose: [400, 0, 150, -90, 0, 0]
|
||||
right:
|
||||
_target_: ufactory_lerobot.teleoperators.umi_teleop.UmiTeleopConfig
|
||||
id: "right_teleop"
|
||||
type: uf::umi_teleop
|
||||
id: "umi_250801DR48FP26001295"
|
||||
serial_number: "250801DR48FP26001295"
|
||||
use_vive_tracker: False
|
||||
vive_tracker_id: "LHR-2425BAD3"
|
||||
@ -53,15 +55,14 @@ TeleoperatorConfig:
|
||||
tracker_to_robot_eef: [0, 0, 0, 0, 0, -90]
|
||||
robot_base_pose: [400, 0, 150, 90, 0, 0]
|
||||
|
||||
DatasetRecordConfig:
|
||||
_target_: lerobot.scripts.lerobot_record.DatasetRecordConfig
|
||||
dataset:
|
||||
# root of local repo: /home/<user_name>/.cache/huggingface/lerobot (default)
|
||||
root: "/home/uf/Data/lerobot_datas/record/ufactory/dual_xarm6_umi_datas"
|
||||
repo_id: "ufactory/dual_xarm6_umi_datas"
|
||||
repo_id: "ufactory/dual_umi_datas"
|
||||
single_task: "Pick up the purple grape and drop into the box on the left."
|
||||
fps: 60
|
||||
episode_time_s: 60 # max duration for one episode
|
||||
reset_time_s: 20 # (invalid) time for resetting env between episodes
|
||||
reset_time_s: 20 # time for resetting env between episodes
|
||||
num_episodes: 100
|
||||
push_to_hub: False
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
RobotConfig:
|
||||
_target_: ufactory_lerobot.robots.uf_robot.MultipleUFRobotConfig
|
||||
robot:
|
||||
type: uf::multiple_robot
|
||||
id: "multiple_uf_robots"
|
||||
async_connect: True
|
||||
async_configure: True
|
||||
async_action: False
|
||||
robots:
|
||||
left:
|
||||
_target_: ufactory_lerobot.robots.uf_robot.UFRobotConfig
|
||||
type: uf::robot
|
||||
id: "left_uf_robot"
|
||||
robot_dof: 6
|
||||
control_space: "cartesian"
|
||||
@ -15,16 +15,23 @@ RobotConfig:
|
||||
max_linear_velocity: 200
|
||||
start_joints: [-14.7, 25.6, -49.6, -282.5, 95.9, -65.9]
|
||||
start_tcp_pose: [400, 0, 150, -90, 0, 0]
|
||||
teleop_id: "umi_250801DR48FP26001318"
|
||||
# no_action: True
|
||||
cameras:
|
||||
fisheye:
|
||||
_target_: lerobot.cameras.opencv.configuration_opencv.OpenCVCameraConfig
|
||||
# type: uf::umi_camera
|
||||
# serial_number: "250801DR48FB26001379"
|
||||
# width: 1280
|
||||
# height: 1280
|
||||
# fps: 30
|
||||
type: opencv
|
||||
index_or_path: "/dev/v4l/by-id/usb-XVisio_Technology_XVisio_vSLAM_250801DR48FB26001379-video-index0"
|
||||
width: 1280
|
||||
height: 1280
|
||||
fps: 100
|
||||
fourcc: "YUYV"
|
||||
right:
|
||||
_target_: ufactory_lerobot.robots.uf_robot.UFRobotConfig
|
||||
type: uf::robot
|
||||
id: "right_uf_robot"
|
||||
robot_dof: 6
|
||||
control_space: "cartesian"
|
||||
@ -33,9 +40,16 @@ RobotConfig:
|
||||
max_linear_velocity: 200
|
||||
start_joints: [14.5, 25.5, -49.6, -76.8, 95.9, 66.6]
|
||||
start_tcp_pose: [400, 0, 150, 90, 0, 0]
|
||||
teleop_id: "umi_250801DR48FP26001295"
|
||||
# no_action: True
|
||||
cameras:
|
||||
fisheye:
|
||||
_target_: lerobot.cameras.opencv.configuration_opencv.OpenCVCameraConfig
|
||||
# type: uf::umi_camera
|
||||
# serial_number: "250801DR48FB26001396"
|
||||
# width: 1280
|
||||
# height: 1280
|
||||
# fps: 30
|
||||
type: opencv
|
||||
index_or_path: "/dev/v4l/by-id/usb-XVisio_Technology_XVisio_vSLAM_250801DR48FB26001396-video-index0"
|
||||
width: 1280
|
||||
height: 1280
|
||||
@ -43,13 +57,13 @@ RobotConfig:
|
||||
fourcc: "YUYV"
|
||||
|
||||
# make sure to edit with your correct configurations!
|
||||
TeleoperatorConfig:
|
||||
_target_: ufactory_lerobot.teleoperators.umi_teleop.MultipleUmiTeleopConfig
|
||||
teleop:
|
||||
type: uf::multiple_umi_teleop
|
||||
id: "multiple_teleops"
|
||||
teleops:
|
||||
left:
|
||||
_target_: ufactory_lerobot.teleoperators.umi_teleop.UmiTeleopConfig
|
||||
id: "left_teleop"
|
||||
type: uf::umi_teleop
|
||||
id: "umi_250801DR48FP26001318"
|
||||
serial_number: "250801DR48FP26001318"
|
||||
use_vive_tracker: False
|
||||
vive_tracker_id: "LHR-555DC7BF"
|
||||
@ -57,8 +71,8 @@ TeleoperatorConfig:
|
||||
tracker_to_robot_eef: [0, 0, 0, 0, 0, -90]
|
||||
robot_base_pose: [400, 0, 150, -90, 0, 0]
|
||||
right:
|
||||
_target_: ufactory_lerobot.teleoperators.umi_teleop.UmiTeleopConfig
|
||||
id: "right_teleop"
|
||||
type: uf::umi_teleop
|
||||
id: "umi_250801DR48FP26001295"
|
||||
serial_number: "250801DR48FP26001295"
|
||||
use_vive_tracker: False
|
||||
vive_tracker_id: "LHR-2425BAD3"
|
||||
@ -66,15 +80,14 @@ TeleoperatorConfig:
|
||||
tracker_to_robot_eef: [0, 0, 0, 0, 0, -90]
|
||||
robot_base_pose: [400, 0, 150, 90, 0, 0]
|
||||
|
||||
DatasetRecordConfig:
|
||||
_target_: lerobot.scripts.lerobot_record.DatasetRecordConfig
|
||||
dataset:
|
||||
# root of local repo: /home/<user_name>/.cache/huggingface/lerobot (default)
|
||||
root: "/home/uf/Data/lerobot_datas/record/ufactory/dual_xarm6_umi_datas"
|
||||
repo_id: "ufactory/dual_xarm6_umi_datas"
|
||||
single_task: "Pick up the purple grape and drop into the box on the left."
|
||||
fps: 60
|
||||
episode_time_s: 60 # max duration for one episode
|
||||
reset_time_s: 20 # (invalid) time for resetting env between episodes
|
||||
reset_time_s: 20 # time for resetting env between episodes
|
||||
num_episodes: 100
|
||||
push_to_hub: False
|
||||
|
||||
|
||||
@ -1,19 +1,21 @@
|
||||
RobotConfig:
|
||||
_target_: ufactory_lerobot.robots.uf_mock_robot.UFMockRobotConfig
|
||||
robot:
|
||||
type: uf::mock_robot
|
||||
id: "uf_mock_robot"
|
||||
control_space: "cartesian"
|
||||
gripper_type: 1
|
||||
state_offset_action: 3
|
||||
teleop_id: "umi_250801DR48FP26001295"
|
||||
cameras:
|
||||
fisheye:
|
||||
_target_: ufactory_lerobot.cameras.umi_camera.configuration_umi.UmiCameraConfig
|
||||
type: uf::umi_camera
|
||||
serial_number: "250801DR48FP26001295"
|
||||
width: 1280
|
||||
height: 1280
|
||||
fps: 100
|
||||
|
||||
TeleoperatorConfig:
|
||||
_target_: ufactory_lerobot.teleoperators.umi_teleop.UmiTeleopConfig
|
||||
teleop:
|
||||
type: uf::umi_teleop
|
||||
id: "umi_250801DR48FP26001295"
|
||||
serial_number: "250801DR48FP26001295"
|
||||
use_vive_tracker: False
|
||||
vive_tracker_id: "LHR-2425BAD3"
|
||||
@ -21,14 +23,13 @@ TeleoperatorConfig:
|
||||
tracker_to_robot_eef: [0, 0, 0, 0, 0, -90]
|
||||
robot_base_pose: [400, 0, 400, 180, 0, 0]
|
||||
|
||||
DatasetRecordConfig:
|
||||
_target_: lerobot.scripts.lerobot_record.DatasetRecordConfig
|
||||
dataset:
|
||||
# root of local repo: /home/<user_name>/.cache/huggingface/lerobot (default)
|
||||
root: "/home/uf/Data/lerobot_datas/record/ufactory/umi_datas"
|
||||
repo_id: "ufactory/umi_datas"
|
||||
single_task: "Pick up the purple grape and drop into the box on the left."
|
||||
fps: 60
|
||||
episode_time_s: 60 # max duration for one episode
|
||||
reset_time_s: 20 # (invalid) time for resetting env between episodes
|
||||
reset_time_s: 20 # time for resetting env between episodes
|
||||
num_episodes: 100
|
||||
push_to_hub: False
|
||||
@ -1,5 +1,5 @@
|
||||
RobotConfig:
|
||||
_target_: ufactory_lerobot.robots.uf_robot.UFRobotConfig
|
||||
robot:
|
||||
type: uf::robot
|
||||
id: "uf_robot"
|
||||
robot_dof: 6
|
||||
control_space: "cartesian"
|
||||
@ -10,15 +10,15 @@ RobotConfig:
|
||||
start_tcp_pose: [400, 0, 400, 180, 0, 0]
|
||||
cameras:
|
||||
fisheye:
|
||||
_target_: lerobot.cameras.opencv.configuration_opencv.OpenCVCameraConfig
|
||||
type: opencv
|
||||
index_or_path: "/dev/v4l/by-id/usb-XVisio_Technology_XVisio_vSLAM_250801DR48FB26001396-video-index0"
|
||||
width: 1280
|
||||
height: 1280
|
||||
fps: 100
|
||||
fourcc: "YUYV"
|
||||
|
||||
TeleoperatorConfig:
|
||||
_target_: ufactory_lerobot.teleoperators.umi_teleop.UmiTeleopConfig
|
||||
teleop:
|
||||
type: uf::umi_teleop
|
||||
serial_number: "250801DR48FP26001295"
|
||||
use_vive_tracker: False
|
||||
vive_tracker_id: "LHR-2425BAD3"
|
||||
@ -26,14 +26,13 @@ TeleoperatorConfig:
|
||||
tracker_to_robot_eef: [0, 0, 0, 0, 0, -90]
|
||||
robot_base_pose: [400, 0, 400, 180, 0, 0]
|
||||
|
||||
DatasetRecordConfig:
|
||||
_target_: lerobot.scripts.lerobot_record.DatasetRecordConfig
|
||||
dataset:
|
||||
# root of local repo: /home/<user_name>/.cache/huggingface/lerobot (default)
|
||||
root: "/home/uf/Data/lerobot_datas/record/ufactory/xarm6_umi_datas"
|
||||
repo_id: "ufactory/xarm6_umi_datas"
|
||||
single_task: "Pick up the purple grape and drop into the box on the left."
|
||||
fps: 60
|
||||
episode_time_s: 60 # max duration for one episode
|
||||
reset_time_s: 20 # (invalid) time for resetting env between episodes
|
||||
reset_time_s: 20 # time for resetting env between episodes
|
||||
num_episodes: 100
|
||||
push_to_hub: False
|
||||
@ -3,14 +3,14 @@ requires = ["setuptools>=68.0", "wheel>=0.42"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "ufactory_lerobot"
|
||||
name = "lerobot_robot_ufactory"
|
||||
version = "0.1.0"
|
||||
description = "UFACTORY 机械臂与 LeRobot 框架集成"
|
||||
readme = "README.md"
|
||||
license = { text = "Apache-2.0" }
|
||||
requires-python = ">=3.10"
|
||||
authors = [
|
||||
{ name = "ufactory_lerobot Contributors" },
|
||||
{ name = "lerobot_robot_ufactory Contributors" },
|
||||
]
|
||||
keywords = ["robotics", "lerobot", "ufactory", "xarm", "imitation-learning"]
|
||||
classifiers = [
|
||||
@ -32,11 +32,11 @@ dependencies = [
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
uf-robot-teleop = "ufactory_lerobot.scripts.uf_robot_teleop:main"
|
||||
uf-lerobot-record = "ufactory_lerobot.scripts.uf_lerobot_record:main"
|
||||
uf-lerobot-eval = "ufactory_lerobot.scripts.uf_lerobot_eval:main"
|
||||
uf-vive-calibrate = "ufactory_lerobot.scripts.vive_calibrate:main"
|
||||
uf-camera-view = "ufactory_lerobot.scripts.uf_camera_view:main"
|
||||
uf-robot-teleop = "lerobot_robot_ufactory.scripts.uf_robot_teleop:main"
|
||||
uf-lerobot-record = "lerobot_robot_ufactory.scripts.uf_lerobot_record:main"
|
||||
uf-lerobot-eval = "lerobot_robot_ufactory.scripts.uf_lerobot_eval:main"
|
||||
uf-vive-calibrate = "lerobot_robot_ufactory.scripts.vive_calibrate:main"
|
||||
uf-camera-view = "lerobot_robot_ufactory.scripts.uf_camera_view:main"
|
||||
|
||||
[project.optional-dependencies]
|
||||
# GELLO 遥操作
|
||||
@ -64,7 +64,7 @@ include-package-data = true
|
||||
where = ["src"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"ufactory_lerobot.devices.umi.xvlib" = ["*.so", "*.so.*"]
|
||||
"lerobot_robot_ufactory.devices.umi.xvlib" = ["*.so", "*.so.*"]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 100
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
from ufactory_lerobot.cameras.utils import make_cameras_from_configs as _uf_make_cameras_from_configs
|
||||
from ufactory_lerobot.robots.utils import make_robot_from_config as _uf_make_robot_from_config
|
||||
from ufactory_lerobot.teleoperators.utils import make_teleoperator_from_config as _uf_make_teleoperator_from_config
|
||||
from lerobot_robot_ufactory.cameras.utils import make_cameras_from_configs as _uf_make_cameras_from_configs
|
||||
from lerobot_robot_ufactory.robots.utils import make_robot_from_config as _uf_make_robot_from_config
|
||||
from lerobot_robot_ufactory.teleoperators.utils import make_teleoperator_from_config as _uf_make_teleoperator_from_config
|
||||
import lerobot.cameras as _lerobot_cameras
|
||||
import lerobot.robots as _lerobot_robot
|
||||
import lerobot.teleoperators as _lerobot_teleoperators
|
||||
@ -15,11 +15,11 @@ _lerobot_cameras_utils.make_cameras_from_configs = _uf_make_cameras_from_configs
|
||||
_lerobot_robot_utils.make_robot_from_config = _uf_make_robot_from_config
|
||||
_lerobot_teleoperators_utils.make_teleoperator_from_config = _uf_make_teleoperator_from_config
|
||||
# register plugin
|
||||
import ufactory_lerobot.cameras.umi_camera
|
||||
import ufactory_lerobot.robots.uf_robot
|
||||
import ufactory_lerobot.robots.uf_mock_robot
|
||||
import ufactory_lerobot.teleoperators.uf_mock_teleop
|
||||
import ufactory_lerobot.teleoperators.gello_teleop
|
||||
import ufactory_lerobot.teleoperators.pika_teleop
|
||||
import ufactory_lerobot.teleoperators.space_mouse
|
||||
import ufactory_lerobot.teleoperators.umi_teleop
|
||||
import lerobot_robot_ufactory.cameras.umi_camera
|
||||
import lerobot_robot_ufactory.robots.uf_robot
|
||||
import lerobot_robot_ufactory.robots.uf_mock_robot
|
||||
import lerobot_robot_ufactory.teleoperators.uf_mock_teleop
|
||||
import lerobot_robot_ufactory.teleoperators.gello_teleop
|
||||
import lerobot_robot_ufactory.teleoperators.pika_teleop
|
||||
import lerobot_robot_ufactory.teleoperators.space_mouse
|
||||
import lerobot_robot_ufactory.teleoperators.umi_teleop
|
||||
@ -51,7 +51,7 @@ class UmiCamera(Camera):
|
||||
self.rotation: int | None = get_cv2_rotation(config.rotation)
|
||||
|
||||
self.last_frame = None
|
||||
from ufactory_lerobot.devices.umi.xvlib import XVLib
|
||||
from lerobot_robot_ufactory.devices.umi.xvlib import XVLib
|
||||
self.xvlib = XVLib(self.serial_number)
|
||||
self.xvlib.xv_color_camera_init()
|
||||
|
||||
28
src/lerobot_robot_ufactory/context.py
Normal file
28
src/lerobot_robot_ufactory/context.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""上下文变量,用于跨模块共享运行时对象,避免属性注入。"""
|
||||
import contextvars
|
||||
|
||||
_active_teleops: contextvars.ContextVar = contextvars.ContextVar("active_teleops", default={})
|
||||
|
||||
|
||||
def register_teleop(teleop) -> None:
|
||||
"""注册 teleop, 以 teleop.id 为 key"""
|
||||
teleops = _active_teleops.get()
|
||||
teleops[teleop.id] = teleop
|
||||
_active_teleops.set(teleops)
|
||||
|
||||
|
||||
def unregister_teleop(teleop) -> None:
|
||||
"""移除 teleop"""
|
||||
teleops = _active_teleops.get()
|
||||
teleops.pop(teleop.id, None)
|
||||
_active_teleops.set(teleops)
|
||||
|
||||
|
||||
def get_active_teleop(teleop_id: str | None = None):
|
||||
"""获取 active teleop。不传 id 返回第一个;传 id 返回对应 teleop。"""
|
||||
teleops = _active_teleops.get()
|
||||
if not teleops:
|
||||
return None
|
||||
if teleop_id is not None:
|
||||
return teleops.get(teleop_id)
|
||||
return next(iter(teleops.values()))
|
||||
@ -72,6 +72,7 @@ class PikaDevice(object):
|
||||
# _pika_sense_port = None
|
||||
# _pika_gripper_port = None
|
||||
# _lock = threading.Lock()
|
||||
PIKA_DEVICE_MAP = {}
|
||||
|
||||
def __init__(self, dev_type=1, **kwargs):
|
||||
"""
|
||||
@ -144,6 +145,9 @@ class PikaDevice(object):
|
||||
if self._dev_type not in [1, 3]:
|
||||
return None
|
||||
if self._pika_sense is None:
|
||||
if self._pika_sense_port in self.PIKA_DEVICE_MAP:
|
||||
self._pika_sense = self.PIKA_DEVICE_MAP[self._pika_sense_port]
|
||||
return self._pika_sense
|
||||
from pika.sense import Sense
|
||||
# 初始化Sense对象
|
||||
self._pika_sense = Sense(port=self._pika_sense_port)
|
||||
@ -152,6 +156,7 @@ class PikaDevice(object):
|
||||
logger.error('连接Pika Sense设备失败')
|
||||
exit(1)
|
||||
logger.info('Pika Sense设备连接成功')
|
||||
self.PIKA_DEVICE_MAP[self._pika_sense_port] = self._pika_sense # 注册共享
|
||||
|
||||
# 配置Vive Tracker(可选)
|
||||
# sense.set_vive_tracker_config(config_path='path/to/config', lh_config='lighthouse_config')
|
||||
@ -186,9 +191,12 @@ class PikaDevice(object):
|
||||
if self._dev_type not in [2, 3]:
|
||||
return None
|
||||
if self._pika_gripper is None:
|
||||
if self._dev_type in [2, 3]:
|
||||
if self._pika_gripper_port in self.PIKA_DEVICE_MAP:
|
||||
self._pika_gripper = self.PIKA_DEVICE_MAP[self._pika_gripper_port]
|
||||
return self._pika_gripper
|
||||
from pika.gripper import Gripper
|
||||
self._pika_gripper = Gripper(port=self._pika_gripper_port)
|
||||
self.PIKA_DEVICE_MAP[self._pika_gripper_port] = self._pika_gripper # 注册共享
|
||||
# 连接设备
|
||||
if not self._pika_gripper.connect():
|
||||
logger.error('连接Pika Gripper设备失败')
|
||||
@ -15,60 +15,61 @@ class MultipleUFMockRobot(Robot):
|
||||
def __init__(self, config: MultipleUFMockRobotConfig):
|
||||
super().__init__(config)
|
||||
self.config = config
|
||||
self.keys = []
|
||||
self.robots = []
|
||||
self.robots: dict[str, UFMockRobot] = {}
|
||||
for key, robot_config in self.config.robots.items():
|
||||
self.keys.append(key)
|
||||
self.robots.append(UFMockRobot(robot_config, prefix=key))
|
||||
self.robots[key] = UFMockRobot(robot_config, prefix=key)
|
||||
|
||||
self.cameras = {}
|
||||
for robot in self.robots.values():
|
||||
self.cameras.update(robot.cameras)
|
||||
|
||||
@property
|
||||
def observation_features(self) -> dict:
|
||||
observation_features = {}
|
||||
for robot in self.robots:
|
||||
for robot in self.robots.values():
|
||||
observation_features.update(robot.observation_features)
|
||||
return observation_features
|
||||
|
||||
@property
|
||||
def action_features(self) -> dict:
|
||||
action_features = {}
|
||||
for robot in self.robots:
|
||||
for robot in self.robots.values():
|
||||
action_features.update(robot.action_features)
|
||||
return action_features
|
||||
|
||||
@property
|
||||
def is_connected(self) -> bool:
|
||||
return all(robot.is_connected for robot in self.robots)
|
||||
return all(robot.is_connected for robot in self.robots.values())
|
||||
|
||||
@property
|
||||
def is_calibrated(self) -> bool:
|
||||
return all(robot.is_calibrated for robot in self.robots)
|
||||
return all(robot.is_calibrated for robot in self.robots.values())
|
||||
|
||||
def connect(self, calibrate: bool = True) -> None:
|
||||
for robot in self.robots:
|
||||
for robot in self.robots.values():
|
||||
robot.connect(calibrate=calibrate)
|
||||
|
||||
def calibrate(self) -> None:
|
||||
for robot in self.robots:
|
||||
for robot in self.robots.values():
|
||||
robot.calibrate()
|
||||
|
||||
def configure(self) -> None:
|
||||
for robot in self.robots:
|
||||
for robot in self.robots.values():
|
||||
robot.configure()
|
||||
|
||||
def disconnect(self) -> None:
|
||||
for robot in self.robots:
|
||||
for robot in self.robots.values():
|
||||
robot.disconnect()
|
||||
|
||||
def get_observation(self) -> RobotObservation:
|
||||
observations = [robot.get_observation() for robot in self.robots]
|
||||
observations = [robot.get_observation() for robot in self.robots.values()]
|
||||
combined_observation = RobotObservation()
|
||||
for obs in observations:
|
||||
combined_observation.update(obs)
|
||||
return combined_observation
|
||||
|
||||
def send_action(self, action: RobotAction) -> RobotAction:
|
||||
for i in range(len(self.keys)):
|
||||
key = self.keys[i]
|
||||
for key, robot in self.robots.items():
|
||||
action_subset = {k: v for k, v in action.items() if k.startswith(f"{key}.")}
|
||||
self.robots[i].send_action(action_subset)
|
||||
robot.send_action(action_subset)
|
||||
return action
|
||||
@ -1,11 +1,13 @@
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from lerobot.robots import RobotConfig
|
||||
from .uf_mock_robot_config import UFMockRobotConfig
|
||||
|
||||
@RobotConfig.register_subclass("uf::multiple_mock_robot")
|
||||
@dataclass
|
||||
class MultipleUFMockRobotConfig(RobotConfig):
|
||||
robots: dict[str, UFMockRobotConfig]
|
||||
robots: dict[str, RobotConfig] = field(
|
||||
default_factory=lambda: {}
|
||||
)
|
||||
|
||||
def __post_init__(self):
|
||||
super().__post_init__()
|
||||
@ -1,6 +1,7 @@
|
||||
import numpy as np
|
||||
from lerobot.cameras.utils import make_cameras_from_configs
|
||||
from lerobot.robots import Robot
|
||||
from lerobot_robot_ufactory.context import get_active_teleop
|
||||
from .uf_mock_robot_config import UFMockRobotConfig
|
||||
|
||||
|
||||
@ -31,7 +32,6 @@ class UFMockRobot(Robot):
|
||||
self._jnt_obs_has_vel = config.observe_joint_vel if self._control_space == "joint" else False
|
||||
self._is_connected = False
|
||||
self._is_calibrated =True
|
||||
self._teleop = self.config.teleop
|
||||
self._cache_num = self.config.state_offset_action
|
||||
self._teleop_actions = []
|
||||
|
||||
@ -98,7 +98,8 @@ class UFMockRobot(Robot):
|
||||
pass # CHECK! currently No-op
|
||||
|
||||
def get_observation(self) -> dict[str, np.ndarray]:
|
||||
new_act = self._teleop.get_action()
|
||||
teleop = get_active_teleop(self.config.teleop_id)
|
||||
new_act = teleop.get_action() if teleop else {}
|
||||
# Capture images from cameras
|
||||
for cam_key, cam in self.cameras.items():
|
||||
new_act[f"{self.prefix}{cam_key}"] = cam.async_read()
|
||||
@ -5,6 +5,7 @@ from lerobot.robots import RobotConfig
|
||||
@RobotConfig.register_subclass("uf::mock_robot")
|
||||
@dataclass
|
||||
class UFMockRobotConfig(RobotConfig):
|
||||
teleop_id: str
|
||||
cameras: dict[str, CameraConfig] = field(
|
||||
default_factory=lambda: {}
|
||||
)
|
||||
@ -13,7 +14,6 @@ class UFMockRobotConfig(RobotConfig):
|
||||
control_space: str = "joint"
|
||||
gripper_type: int = 1 # 1: xArm Gripper, 10: Pika Gripper
|
||||
observe_joint_vel: bool = False # only effective in joint control mode
|
||||
teleop: None = None # from lerobot.teleoperators import Teleoperator
|
||||
state_offset_action: int = 3 # the number of previous teleop actions to be included in the observation
|
||||
|
||||
def __post_init__(self):
|
||||
@ -1,11 +1,13 @@
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from lerobot.robots import RobotConfig
|
||||
from .uf_robot_config import UFRobotConfig
|
||||
|
||||
@RobotConfig.register_subclass("uf::multiple_robot")
|
||||
@dataclass
|
||||
class MultipleUFRobotConfig(RobotConfig):
|
||||
robots: dict[str, UFRobotConfig]
|
||||
robots: dict[str, RobotConfig] = field(
|
||||
default_factory=lambda: {}
|
||||
)
|
||||
async_connect: bool = True
|
||||
async_configure: bool = True
|
||||
async_action: bool = False
|
||||
@ -10,7 +10,7 @@ from dataclasses import dataclass
|
||||
from threading import Thread, Event, Lock
|
||||
from lerobot.robots import Robot
|
||||
from lerobot.cameras.utils import make_cameras_from_configs
|
||||
from ufactory_lerobot.devices.pika import PikaDevice
|
||||
from lerobot_robot_ufactory.devices.pika import PikaDevice
|
||||
from .uf_robot_config import UFRobotConfig
|
||||
from xarm.wrapper import XArmAPI
|
||||
from xarm.core.utils import convert
|
||||
66
src/lerobot_robot_ufactory/scripts/uf_download_resources.py
Normal file
66
src/lerobot_robot_ufactory/scripts/uf_download_resources.py
Normal file
@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python
|
||||
"""Download XVSDK binaries and UMI shared libraries after install."""
|
||||
import os
|
||||
import sys
|
||||
import urllib.request
|
||||
import subprocess
|
||||
import site
|
||||
|
||||
RESOURCES = "https://raw.githubusercontent.com/xArm-Developer/ufactory_resources/main/fastumi"
|
||||
|
||||
FILES = [
|
||||
# (url_path, subdir)
|
||||
("libxvlib.so", ""),
|
||||
("opencv/libopencv_core.so.4.2", ""),
|
||||
("opencv/libopencv_imgproc.so.4.2", ""),
|
||||
]
|
||||
|
||||
DEB_FILE = "sdk/XVSDK_focal_amd64.deb"
|
||||
|
||||
|
||||
def get_pkg_dir():
|
||||
"""Find the installed ufactory_lerobot package directory."""
|
||||
for sp in site.getsitepackages():
|
||||
path = os.path.join(sp, "ufactory_lerobot", "devices", "umi", "xvlib")
|
||||
if os.path.isdir(path):
|
||||
return path
|
||||
# fallback: try user site
|
||||
usp = site.getusersitepackages()
|
||||
if usp:
|
||||
return os.path.join(usp, "ufactory_lerobot", "devices", "umi", "xvlib")
|
||||
sys.exit("Error: cannot find installed ufactory_lerobot package.")
|
||||
|
||||
|
||||
def download(url, dest):
|
||||
print(f" -> {dest}")
|
||||
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
||||
urllib.request.urlretrieve(url, dest)
|
||||
|
||||
|
||||
def main():
|
||||
pkg_dir = get_pkg_dir()
|
||||
print(f"Installing to: {pkg_dir}\n")
|
||||
|
||||
for name, subdir in FILES:
|
||||
url = f"{RESOURCES}/{name}"
|
||||
dest = os.path.join(pkg_dir, os.path.basename(name))
|
||||
print(f"Downloading {name} ...")
|
||||
download(url, dest)
|
||||
|
||||
# .deb: download to /tmp and install
|
||||
deb_url = f"{RESOURCES}/{DEB_FILE}"
|
||||
deb_path = "/tmp/XVSDK_focal_amd64.deb"
|
||||
print(f"\nDownloading {DEB_FILE} ...")
|
||||
download(deb_url, deb_path)
|
||||
|
||||
print("\nInstalling XVSDK ...")
|
||||
ret = subprocess.run(["sudo", "dpkg", "-i", deb_path]).returncode
|
||||
if ret != 0:
|
||||
subprocess.run(["sudo", "apt", "install", "-y", "--fix-broken"])
|
||||
os.unlink(deb_path)
|
||||
|
||||
print("\nDone.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -1,15 +1,13 @@
|
||||
import yaml
|
||||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import time
|
||||
import math
|
||||
import torch
|
||||
from pathlib import Path
|
||||
from dataclasses import asdict, dataclass
|
||||
from pprint import pformat
|
||||
from contextlib import nullcontext
|
||||
import numpy as np
|
||||
import ufactory_lerobot # patch
|
||||
import lerobot_robot_ufactory # patch
|
||||
from lerobot.scripts.lerobot_record import register_third_party_plugins
|
||||
from lerobot.datasets.pipeline_features import aggregate_pipeline_dataset_features, create_initial_features
|
||||
from lerobot.datasets.utils import build_dataset_frame, combine_feature_dicts
|
||||
@ -39,8 +37,8 @@ from lerobot.configs import parser
|
||||
from lerobot.configs.policies import PreTrainedConfig
|
||||
from lerobot.scripts.lerobot_record import DatasetRecordConfig
|
||||
from lerobot.datasets.lerobot_dataset import LeRobotDataset
|
||||
from ufactory_lerobot.utils.utils import instantiate_from_dict, init_keyboard_listener
|
||||
from ufactory_lerobot.devices.umi.vive_tracker.transformations import Transformations
|
||||
from lerobot_robot_ufactory.utils.utils import init_keyboard_listener
|
||||
from lerobot_robot_ufactory.devices.umi.vive_tracker.transformations import Transformations
|
||||
|
||||
|
||||
def continuous_rotvec(new_rv, prev_rv):
|
||||
@ -98,6 +96,7 @@ def compute_target_axis_angle(rot_prev, rot_delta):
|
||||
class EvalConfig:
|
||||
robot: RobotConfig
|
||||
dataset: DatasetRecordConfig
|
||||
teleop: dict = None # no use
|
||||
# Whether to control the robot with a policy
|
||||
policy: PreTrainedConfig | None = None
|
||||
n_episodes: int = 50
|
||||
@ -414,26 +413,19 @@ def eval_loop(cfg: EvalConfig, relative=False, rx_continuous=False):
|
||||
if not is_headless() and listener is not None:
|
||||
listener.stop()
|
||||
|
||||
@parser.wrap()
|
||||
def get_cfg(cfg: EvalConfig) -> EvalConfig:
|
||||
return cfg
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='configuration args')
|
||||
parser.add_argument('-c', '--config', type=str, required=True,
|
||||
help='configuration file path, e.g.my_config.yaml')
|
||||
parser.add_argument('--policy.path', type=str, required=True,
|
||||
help='configuration file path, e.g.my_config.yaml')
|
||||
parser.add_argument('--relative', action='store_true', help='is relative motion or not')
|
||||
parser.add_argument('--rx_continuous', action='store_true', help='rx continuous or not')
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
with open(args.config, 'r') as f:
|
||||
cfg = yaml.safe_load(f)
|
||||
except Exception as e:
|
||||
print(f"Error loading config yaml file: {e}")
|
||||
else:
|
||||
args, unknown = parser.parse_known_args()
|
||||
sys.argv = [sys.argv[0]] + unknown
|
||||
register_third_party_plugins()
|
||||
config = instantiate_from_dict(cfg)
|
||||
|
||||
eval_cfg = EvalConfig(robot=config["RobotConfig"], dataset=config["DatasetRecordConfig"])
|
||||
eval_loop(eval_cfg, args.relative, args.rx_continuous)
|
||||
cfg = get_cfg()
|
||||
eval_loop(cfg, args.relative, args.rx_continuous)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@ -1,4 +1,4 @@
|
||||
import yaml
|
||||
import sys
|
||||
import copy
|
||||
import time
|
||||
import queue
|
||||
@ -7,11 +7,11 @@ import logging
|
||||
import shutil
|
||||
import threading
|
||||
from pathlib import Path
|
||||
import ufactory_lerobot # patch
|
||||
import lerobot_robot_ufactory # patch
|
||||
from lerobot.scripts.lerobot_record import *
|
||||
from ufactory_lerobot.teleoperators.uf_mock_teleop import UFMockTeleop
|
||||
from ufactory_lerobot.teleoperators.base_teleop import UFBaseTeleop
|
||||
from ufactory_lerobot.utils.utils import instantiate_from_dict, init_keyboard_listener
|
||||
from lerobot_robot_ufactory.teleoperators.uf_mock_teleop import UFMockTeleop
|
||||
from lerobot_robot_ufactory.teleoperators.base_teleop import UFBaseTeleop
|
||||
from lerobot_robot_ufactory.utils.utils import init_keyboard_listener
|
||||
|
||||
|
||||
def _get_dataset_writer(dataset):
|
||||
@ -344,17 +344,14 @@ def record_loop(
|
||||
timestamp = time.perf_counter() - start_episode_t
|
||||
|
||||
|
||||
@parser.wrap()
|
||||
def record(cfg: RecordConfig, async_save: bool = False) -> LeRobotDataset:
|
||||
init_logging()
|
||||
logging.info(pformat(asdict(cfg)))
|
||||
if cfg.display_data:
|
||||
init_rerun(session_name="recording")
|
||||
|
||||
teleop = make_teleoperator_from_config(cfg.teleop) if cfg.teleop is not None else None
|
||||
if hasattr(cfg.robot, "teleop"):
|
||||
cfg.robot.teleop = teleop
|
||||
robot = make_robot_from_config(cfg.robot)
|
||||
teleop = make_teleoperator_from_config(cfg.teleop) if cfg.teleop is not None else None
|
||||
|
||||
teleop_action_processor, robot_action_processor, robot_observation_processor = make_default_processors()
|
||||
|
||||
@ -592,31 +589,28 @@ def record(cfg: RecordConfig, async_save: bool = False) -> LeRobotDataset:
|
||||
log_say("Exiting", cfg.play_sounds)
|
||||
return dataset
|
||||
|
||||
@parser.wrap()
|
||||
def get_cfg(cfg: RecordConfig) -> RecordConfig:
|
||||
return cfg
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='configuration args')
|
||||
parser.add_argument('-c', '--config', type=str, required=True,
|
||||
help='configuration file path, e.g.my_config.yaml')
|
||||
parser.add_argument('-r', '--resume',
|
||||
parser.add_argument('-r',
|
||||
action='store_true', # specify --resume if resume needs to be True
|
||||
default=False,
|
||||
help='Whether contitue recording on existing dataset (default: False)')
|
||||
parser.add_argument('-a', '--async-save',
|
||||
parser.add_argument('-a', '--async_save',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Enable async background saving (default: False)')
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
with open(Path(args.config).expanduser(), 'r') as f:
|
||||
cfg = yaml.safe_load(f)
|
||||
except Exception as e:
|
||||
print(f"Error loading config yaml file: {e}")
|
||||
else:
|
||||
args, unknown = parser.parse_known_args()
|
||||
sys.argv = [sys.argv[0]] + unknown
|
||||
register_third_party_plugins()
|
||||
config = instantiate_from_dict(cfg)
|
||||
|
||||
record_cfg = RecordConfig(resume=args.resume, play_sounds=False, robot=config["RobotConfig"], dataset=config["DatasetRecordConfig"], teleop=config["TeleoperatorConfig"])
|
||||
record(record_cfg, async_save=args.async_save)
|
||||
cfg = get_cfg()
|
||||
if args.r:
|
||||
cfg.resume = True
|
||||
cfg.play_sounds = False
|
||||
record(cfg, async_save=args.async_save)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@ -1,11 +1,11 @@
|
||||
import yaml
|
||||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import time
|
||||
from pathlib import Path
|
||||
from dataclasses import asdict, dataclass
|
||||
from pprint import pformat
|
||||
import ufactory_lerobot # patch
|
||||
import lerobot_robot_ufactory # patch
|
||||
from lerobot.scripts.lerobot_record import register_third_party_plugins
|
||||
from lerobot.processor import (
|
||||
make_default_processors,
|
||||
@ -23,14 +23,16 @@ from lerobot.utils.robot_utils import precise_sleep
|
||||
from lerobot.utils.utils import (
|
||||
init_logging,
|
||||
)
|
||||
from ufactory_lerobot.utils.utils import instantiate_from_dict, is_headless, init_keyboard_listener
|
||||
from ufactory_lerobot.teleoperators.base_teleop import UFBaseTeleop
|
||||
from lerobot.configs import parser
|
||||
from lerobot_robot_ufactory.utils.utils import is_headless, init_keyboard_listener
|
||||
from lerobot_robot_ufactory.teleoperators.base_teleop import UFBaseTeleop
|
||||
|
||||
|
||||
@dataclass
|
||||
class TeleopConfig:
|
||||
robot: RobotConfig
|
||||
teleop: TeleoperatorConfig
|
||||
dataset: dict = None # no use
|
||||
fps: int = 30
|
||||
|
||||
|
||||
@ -167,24 +169,17 @@ def teleop_loop(cfg: TeleopConfig):
|
||||
if is_evt and listener is not None:
|
||||
listener.stop()
|
||||
|
||||
@parser.wrap()
|
||||
def get_cfg(cfg: TeleopConfig) -> TeleopConfig:
|
||||
return cfg
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='configuration args')
|
||||
parser.add_argument('-c', '--config', type=str, required=True,
|
||||
help='configuration file path, e.g.my_config.yaml')
|
||||
parser.add_argument('-f', '--fps', type=int, default=30,
|
||||
help='control loop frequency in Hz (default: 30)')
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
with open(Path(args.config).expanduser(), 'r') as f:
|
||||
cfg = yaml.safe_load(f)
|
||||
except Exception as e:
|
||||
print(f"Error loading config yaml file: {e}")
|
||||
else:
|
||||
args, unknown = parser.parse_known_args()
|
||||
sys.argv = [sys.argv[0]] + unknown
|
||||
register_third_party_plugins()
|
||||
config = instantiate_from_dict(cfg, ignore_cameras=True)
|
||||
|
||||
teleop_cfg = TeleopConfig(robot=config["RobotConfig"], teleop=config["TeleoperatorConfig"], fps=args.fps)
|
||||
teleop_loop(teleop_cfg)
|
||||
cfg = get_cfg()
|
||||
teleop_loop(cfg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@ -1,4 +1,5 @@
|
||||
from lerobot.teleoperators import Teleoperator
|
||||
from lerobot_robot_ufactory.context import register_teleop, unregister_teleop
|
||||
|
||||
|
||||
class UFBaseTeleop(Teleoperator):
|
||||
@ -9,6 +10,12 @@ class UFBaseTeleop(Teleoperator):
|
||||
super().__init__(config)
|
||||
self.config = config
|
||||
|
||||
def connect(self, calibrate: bool = False) -> None:
|
||||
register_teleop(self)
|
||||
|
||||
def disconnect(self):
|
||||
unregister_teleop(self)
|
||||
|
||||
def set_teleop_enabled(self, enabled: bool, obs=None):
|
||||
"""
|
||||
启用/停用遥操作
|
||||
@ -103,6 +103,7 @@ class GelloTeleop(UFBaseTeleop):
|
||||
|
||||
self.configure()
|
||||
self._is_connected = True
|
||||
super().connect(calibrate)
|
||||
logger.info(f"{self} connected.")
|
||||
|
||||
@property
|
||||
@ -5,8 +5,8 @@ import math
|
||||
from typing import Any
|
||||
from threading import Thread, Event, Lock
|
||||
from lerobot.utils.errors import DeviceNotConnectedError
|
||||
from ufactory_lerobot.devices.pika import PikaDevice
|
||||
from ufactory_lerobot.devices.umi.vive_tracker.transformations import Transformations
|
||||
from lerobot_robot_ufactory.devices.pika import PikaDevice
|
||||
from lerobot_robot_ufactory.devices.umi.vive_tracker.transformations import Transformations
|
||||
from ..base_teleop import UFBaseTeleop
|
||||
from .pika_teleop_config import PikaTeleopConfig
|
||||
|
||||
@ -87,6 +87,7 @@ class PikaTeleop(UFBaseTeleop, Thread):
|
||||
pass
|
||||
|
||||
def connect(self, calibrate: bool = False) -> None:
|
||||
super().connect(calibrate)
|
||||
self.start()
|
||||
|
||||
def disconnect(self):
|
||||
@ -40,6 +40,7 @@ class MultipleUmiTeleop(UFBaseTeleop):
|
||||
return all(teleop.is_calibrated for teleop in self.teleops.values())
|
||||
|
||||
def connect(self, calibrate: bool = True) -> None:
|
||||
super().connect(calibrate)
|
||||
for teleop in self.teleops.values():
|
||||
teleop.connect(calibrate=calibrate)
|
||||
|
||||
@ -52,6 +53,7 @@ class MultipleUmiTeleop(UFBaseTeleop):
|
||||
teleop.configure()
|
||||
|
||||
def disconnect(self) -> None:
|
||||
super().disconnect()
|
||||
for teleop in self.teleops.values():
|
||||
teleop.disconnect()
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from lerobot.teleoperators import TeleoperatorConfig
|
||||
from .umi_teleop_config import UmiTeleopConfig
|
||||
|
||||
@ -22,7 +22,9 @@ from .umi_teleop_config import UmiTeleopConfig
|
||||
@TeleoperatorConfig.register_subclass("uf::multiple_umi_teleop")
|
||||
@dataclass
|
||||
class MultipleUmiTeleopConfig(TeleoperatorConfig):
|
||||
teleops: dict[str, UmiTeleopConfig]
|
||||
teleops: dict[str, TeleoperatorConfig] = field(
|
||||
default_factory=lambda: {}
|
||||
)
|
||||
|
||||
def __post_init__(self):
|
||||
self.id = 'multiple_umi_teleop' if self.id is None else self.id
|
||||
@ -3,8 +3,8 @@
|
||||
import math
|
||||
from typing import Any
|
||||
from lerobot.utils.errors import DeviceAlreadyConnectedError, DeviceNotConnectedError
|
||||
from ufactory_lerobot.devices.umi.vive_tracker.transformations import Transformations
|
||||
from ufactory_lerobot.devices.umi.vive_tracker import ViveTracker
|
||||
from lerobot_robot_ufactory.devices.umi.vive_tracker.transformations import Transformations
|
||||
from lerobot_robot_ufactory.devices.umi.vive_tracker import ViveTracker
|
||||
from ..base_teleop import UFBaseTeleop
|
||||
from .umi_teleop_config import UmiTeleopConfig
|
||||
|
||||
@ -26,7 +26,7 @@ class UmiTeleop(UFBaseTeleop):
|
||||
self.tracker = None
|
||||
self.xvlib = None
|
||||
|
||||
# from ufactory_lerobot.devices.umi.xvlib import XVLib
|
||||
# from lerobot_robot_ufactory.devices.umi.xvlib import XVLib
|
||||
# self.tracker = ViveTracker() if self.config.use_vive_tracker else None
|
||||
# self.xvlib = XVLib(self.config.serial_number, not self.config.use_vive_tracker, self.config.use_gripper)
|
||||
|
||||
@ -84,7 +84,7 @@ class UmiTeleop(UFBaseTeleop):
|
||||
pass
|
||||
|
||||
def connect(self, calibrate: bool = False) -> None:
|
||||
from ufactory_lerobot.devices.umi.xvlib import XVLib
|
||||
from lerobot_robot_ufactory.devices.umi.xvlib import XVLib
|
||||
self.tracker = ViveTracker() if self.config.use_vive_tracker else None
|
||||
self.xvlib = XVLib(self.config.serial_number, not self.config.use_vive_tracker, self.config.use_gripper)
|
||||
if not self.config.use_vive_tracker:
|
||||
@ -92,8 +92,10 @@ class UmiTeleop(UFBaseTeleop):
|
||||
if self.config.use_gripper:
|
||||
self.xvlib.xv_clamp_stream_init()
|
||||
self._is_connected = True
|
||||
super().connect(calibrate)
|
||||
|
||||
def disconnect(self):
|
||||
super().disconnect()
|
||||
if self.xvlib:
|
||||
self.xvlib.xv_uninit()
|
||||
self._is_connected = False
|
||||
@ -30,4 +30,4 @@ class UmiTeleopConfig(TeleoperatorConfig):
|
||||
robot_base_pose: Tuple[float, ...] = (300, 0, 300, 180, -90, 0) # [x, y, z, roll(°), pitch(°), yaw(°)]
|
||||
|
||||
def __post_init__(self):
|
||||
self.id = 'umi_teleop' if self.id is None else self.id
|
||||
self.id = self.serial_number if self.id is None else self.id
|
||||
Loading…
Reference in New Issue
Block a user