From ffe9f7faeacbd308c06eac46ac264ddbc594dd01 Mon Sep 17 00:00:00 2001 From: Dennis Da Date: Wed, 12 Nov 2025 23:13:39 -0800 Subject: [PATCH] Initial commit --- .gitattributes | 23 + .gitignore | 175 + LICENSE | 38 + Makefile | 16 + README.md | 140 + docker/.bashrc | 160 + docker/.tmux.conf | 84 + docker/70-manus-hid.rules | 5 + docker/Dockerfile.deploy | 127 + docker/Dockerfile.deploy.base | 155 + docker/build_deploy_base.sh | 55 + docker/entrypoint/bash.sh | 5 + docker/entrypoint/deploy.sh | 20 + docker/entrypoint/install_deps.sh | 23 + docker/image_name.txt | 1 + docker/kill_all_containers.sh | 79 + docker/kill_gr00t_wbc_processors.sh | 192 + docker/publish.sh | 3 + docker/run_docker.sh | 454 +++ .../unitree_sdk2_python/.gitignore | 39 + .../unitree_sdk2_python/LICENSE | 29 + .../unitree_sdk2_python/README zh.md | 121 + .../unitree_sdk2_python/README.md | 118 + .../example/b2/camera/camera_opencv.py | 51 + .../example/b2/camera/capture_image.py | 51 + .../example/b2/high_level/b2_sport_client.py | 105 + .../example/b2/low_level/b2_stand_example.py | 175 + .../b2/low_level/unitree_legged_const.py | 20 + .../example/b2w/camera/camera_opencv.py | 51 + .../example/b2w/camera/capture_image.py | 51 + .../b2w/high_level/b2w_sport_client.py | 101 + .../b2w/low_level/b2w_stand_example.py | 196 ++ .../b2w/low_level/unitree_legged_const.py | 24 + .../g1/audio/g1_audio_client_example.py | 44 + .../g1/high_level/g1_arm5_sdk_dds_example.py | 192 + .../g1/high_level/g1_arm7_sdk_dds_example.py | 194 + .../g1/high_level/g1_loco_client_example.py | 117 + .../g1/low_level/g1_low_level_example.py | 205 ++ .../g1/low_level/g1_move_hands_example.py | 225 ++ .../unitree_sdk2_python/example/g1/readme.md | 5 + .../example/go2/front_camera/camera_opencv.py | 41 + .../example/go2/front_camera/capture_image.py | 30 + .../go2/high_level/go2_sport_client.py | 170 + .../go2/high_level/go2_utlidar_switch.py | 39 + .../go2/low_level/go2_stand_example.py | 176 + .../go2/low_level/unitree_legged_const.py | 20 + .../go2w/high_level/go2w_sport_client.py | 99 + .../go2w/low_level/go2w_stand_example.py | 196 ++ .../go2w/low_level/unitree_legged_const.py | 24 + .../h1/high_level/h1_loco_client_example.py | 96 + .../h1/low_level/h1_low_level_example.py | 167 + .../h1/low_level/unitree_legged_const.py | 5 + .../h1_2/low_level/h1_2_low_level_example.py | 201 ++ .../example/helloworld/publisher.py | 28 + .../example/helloworld/subscriber.py | 20 + .../example/helloworld/user_data.py | 9 + .../motionSwitcher/motion_switcher_example.py | 36 + .../obstacles_avoid/obstacles_avoid_move.py | 35 + .../obstacles_avoid/obstacles_avoid_switch.py | 94 + .../example/vui_client/vui_client_example.py | 77 + .../wireless_controller.py | 131 + .../unitree_sdk2_python/pyproject.toml | 3 + .../unitree_sdk2_python/setup.py | 21 + .../unitree_sdk2py/__init__.py | 10 + .../b2/back_video/back_video_api.py | 16 + .../b2/back_video/back_video_client.py | 23 + .../b2/front_video/front_video_api.py | 16 + .../b2/front_video/front_video_client.py | 23 + .../b2/robot_state/robot_state_api.py | 25 + .../b2/robot_state/robot_state_client.py | 84 + .../unitree_sdk2py/b2/sport/sport_api.py | 58 + .../unitree_sdk2py/b2/sport/sport_client.py | 241 ++ .../unitree_sdk2py/b2/vui/vui_api.py | 21 + .../unitree_sdk2py/b2/vui/vui_client.py | 86 + .../comm/motion_switcher/__init__.py | 0 .../motion_switcher/motion_switcher_api.py | 29 + .../motion_switcher/motion_switcher_client.py | 51 + .../unitree_sdk2py/core/__init__.py | 0 .../unitree_sdk2py/core/channel.py | 290 ++ .../unitree_sdk2py/core/channel_config.py | 25 + .../unitree_sdk2py/core/channel_name.py | 34 + .../unitree_sdk2py/g1/audio/g1_audio_api.py | 24 + .../g1/audio/g1_audio_client.py | 62 + .../unitree_sdk2py/g1/loco/g1_loco_api.py | 32 + .../unitree_sdk2py/g1/loco/g1_loco_client.py | 127 + .../unitree_sdk2py/go2/__init__.py | 0 .../go2/obstacles_avoid/__init__.py | 0 .../obstacles_avoid/obstacles_avoid_api.py | 19 + .../obstacles_avoid/obstacles_avoid_client.py | 60 + .../go2/robot_state/__init__.py | 0 .../go2/robot_state/robot_state_api.py | 25 + .../go2/robot_state/robot_state_client.py | 84 + .../unitree_sdk2py/go2/sport/__init__.py | 0 .../unitree_sdk2py/go2/sport/sport_api.py | 74 + .../unitree_sdk2py/go2/sport/sport_client.py | 446 +++ .../unitree_sdk2py/go2/video/__init__.py | 0 .../unitree_sdk2py/go2/video/video_api.py | 16 + .../unitree_sdk2py/go2/video/video_client.py | 23 + .../unitree_sdk2py/go2/vui/__init__.py | 0 .../unitree_sdk2py/go2/vui/vui_api.py | 21 + .../unitree_sdk2py/go2/vui/vui_client.py | 86 + .../unitree_sdk2py/h1/loco/h1_loco_api.py | 31 + .../unitree_sdk2py/h1/loco/h1_loco_client.py | 83 + .../unitree_sdk2py/idl/__init__.py | 12 + .../idl/builtin_interfaces/__init__.py | 9 + .../idl/builtin_interfaces/msg/__init__.py | 9 + .../idl/builtin_interfaces/msg/dds_/_Time_.py | 28 + .../builtin_interfaces/msg/dds_/__init__.py | 9 + .../unitree_sdk2py/idl/default.py | 272 ++ .../idl/geometry_msgs/__init__.py | 9 + .../idl/geometry_msgs/msg/__init__.py | 9 + .../idl/geometry_msgs/msg/dds_/_Point32_.py | 29 + .../geometry_msgs/msg/dds_/_PointStamped_.py | 31 + .../idl/geometry_msgs/msg/dds_/_Point_.py | 29 + .../idl/geometry_msgs/msg/dds_/_Pose2D_.py | 29 + .../geometry_msgs/msg/dds_/_PoseStamped_.py | 32 + .../msg/dds_/_PoseWithCovarianceStamped_.py | 32 + .../msg/dds_/_PoseWithCovariance_.py | 28 + .../idl/geometry_msgs/msg/dds_/_Pose_.py | 28 + .../msg/dds_/_QuaternionStamped_.py | 32 + .../geometry_msgs/msg/dds_/_Quaternion_.py | 30 + .../geometry_msgs/msg/dds_/_TwistStamped_.py | 32 + .../msg/dds_/_TwistWithCovarianceStamped_.py | 32 + .../msg/dds_/_TwistWithCovariance_.py | 28 + .../idl/geometry_msgs/msg/dds_/_Twist_.py | 28 + .../idl/geometry_msgs/msg/dds_/_Vector3_.py | 29 + .../idl/geometry_msgs/msg/dds_/__init__.py | 23 + .../unitree_sdk2py/idl/nav_msgs/__init__.py | 9 + .../idl/nav_msgs/msg/__init__.py | 9 + .../idl/nav_msgs/msg/dds_/_MapMetaData_.py | 35 + .../idl/nav_msgs/msg/dds_/_OccupancyGrid_.py | 33 + .../idl/nav_msgs/msg/dds_/_Odometry_.py | 35 + .../idl/nav_msgs/msg/dds_/__init__.py | 11 + .../idl/sensor_msgs/__init__.py | 9 + .../idl/sensor_msgs/msg/__init__.py | 9 + .../dds_/PointField_Constants/_PointField_.py | 28 + .../msg/dds_/PointField_Constants/__init__.py | 9 + .../idl/sensor_msgs/msg/dds_/_PointCloud2_.py | 39 + .../idl/sensor_msgs/msg/dds_/_PointField_.py | 30 + .../idl/sensor_msgs/msg/dds_/__init__.py | 11 + .../unitree_sdk2py/idl/std_msgs/__init__.py | 9 + .../idl/std_msgs/msg/__init__.py | 9 + .../idl/std_msgs/msg/dds_/_Header_.py | 32 + .../idl/std_msgs/msg/dds_/_String_.py | 27 + .../idl/std_msgs/msg/dds_/__init__.py | 10 + .../idl/unitree_api/__init__.py | 9 + .../idl/unitree_api/msg/__init__.py | 9 + .../unitree_api/msg/dds_/_RequestHeader_.py | 29 + .../unitree_api/msg/dds_/_RequestIdentity_.py | 28 + .../unitree_api/msg/dds_/_RequestLease_.py | 27 + .../unitree_api/msg/dds_/_RequestPolicy_.py | 28 + .../idl/unitree_api/msg/dds_/_Request_.py | 28 + .../unitree_api/msg/dds_/_ResponseHeader_.py | 28 + .../unitree_api/msg/dds_/_ResponseStatus_.py | 27 + .../idl/unitree_api/msg/dds_/_Response_.py | 28 + .../idl/unitree_api/msg/dds_/__init__.py | 16 + .../unitree_sdk2py/idl/unitree_go/__init__.py | 9 + .../idl/unitree_go/msg/__init__.py | 9 + .../idl/unitree_go/msg/dds_/_AudioData_.py | 28 + .../idl/unitree_go/msg/dds_/_BmsCmd_.py | 28 + .../idl/unitree_go/msg/dds_/_BmsState_.py | 35 + .../idl/unitree_go/msg/dds_/_Error_.py | 28 + .../msg/dds_/_Go2FrontVideoData_.py | 30 + .../idl/unitree_go/msg/dds_/_HeightMap_.py | 33 + .../idl/unitree_go/msg/dds_/_IMUState_.py | 31 + .../unitree_go/msg/dds_/_InterfaceConfig_.py | 29 + .../idl/unitree_go/msg/dds_/_LidarState_.py | 43 + .../idl/unitree_go/msg/dds_/_LowCmd_.py | 40 + .../idl/unitree_go/msg/dds_/_LowState_.py | 48 + .../idl/unitree_go/msg/dds_/_MotorCmd_.py | 33 + .../idl/unitree_go/msg/dds_/_MotorCmds_.py | 23 + .../idl/unitree_go/msg/dds_/_MotorState_.py | 37 + .../idl/unitree_go/msg/dds_/_MotorStates_.py | 23 + .../idl/unitree_go/msg/dds_/_PathPoint_.py | 33 + .../idl/unitree_go/msg/dds_/_Req_.py | 28 + .../idl/unitree_go/msg/dds_/_Res_.py | 29 + .../unitree_go/msg/dds_/_SportModeState_.py | 42 + .../idl/unitree_go/msg/dds_/_TimeSpec_.py | 28 + .../idl/unitree_go/msg/dds_/_UwbState_.py | 43 + .../idl/unitree_go/msg/dds_/_UwbSwitch_.py | 27 + .../msg/dds_/_WirelessController_.py | 31 + .../idl/unitree_go/msg/dds_/__init__.py | 31 + .../idl/unitree_hg/.idlpy_manifest | 43 + .../unitree_sdk2py/idl/unitree_hg/__init__.py | 9 + .../idl/unitree_hg/msg/.idlpy_manifest | 43 + .../idl/unitree_hg/msg/__init__.py | 9 + .../idl/unitree_hg/msg/dds_/.idlpy_manifest | 43 + .../idl/unitree_hg/msg/dds_/_BmsCmd_.py | 28 + .../idl/unitree_hg/msg/dds_/_BmsState_.py | 39 + .../idl/unitree_hg/msg/dds_/_HandCmd_.py | 28 + .../idl/unitree_hg/msg/dds_/_HandState_.py | 35 + .../idl/unitree_hg/msg/dds_/_IMUState_.py | 31 + .../idl/unitree_hg/msg/dds_/_LowCmd_.py | 31 + .../idl/unitree_hg/msg/dds_/_LowState_.py | 37 + .../unitree_hg/msg/dds_/_MainBoardState_.py | 30 + .../idl/unitree_hg/msg/dds_/_MotorCmd_.py | 33 + .../idl/unitree_hg/msg/dds_/_MotorState_.py | 36 + .../idl/unitree_hg/msg/dds_/_OdoState_.py | 32 + .../unitree_hg/msg/dds_/_PressSensorState_.py | 30 + .../idl/unitree_hg/msg/dds_/__init__.py | 20 + .../unitree_sdk2py/rpc/__init__.py | 0 .../unitree_sdk2py/rpc/client.py | 89 + .../unitree_sdk2py/rpc/client_base.py | 93 + .../unitree_sdk2py/rpc/client_stub.py | 69 + .../unitree_sdk2py/rpc/internal.py | 31 + .../unitree_sdk2py/rpc/lease_client.py | 113 + .../unitree_sdk2py/rpc/lease_server.py | 151 + .../unitree_sdk2py/rpc/request_future.py | 46 + .../unitree_sdk2py/rpc/server.py | 122 + .../unitree_sdk2py/rpc/server_base.py | 32 + .../unitree_sdk2py/rpc/server_stub.py | 78 + .../client/obstacles_avoid_client_example.py | 91 + .../client/robot_service_client_example.py | 50 + .../test/client/sport_client_example.py | 109 + .../test/client/video_client_example.py | 26 + .../test/client/vui_client_example.py | 74 + .../unitree_sdk2py/test/crc/test_crc.py | 27 + .../test/helloworld/helloworld.py | 6 + .../test/helloworld/publisher.py | 22 + .../test/helloworld/subscriber.py | 19 + .../test/lowlevel/lowlevel_control.py | 51 + .../test/lowlevel/read_lowstate.py | 24 + .../test/lowlevel/sub_lowstate.py | 15 + .../test/lowlevel/unitree_go2_const.py | 20 + .../unitree_sdk2py/test/rpc/test_api.py | 9 + .../test/rpc/test_client_example.py | 62 + .../test/rpc/test_server_example.py | 45 + .../unitree_sdk2py/utils/__init__.py | 0 .../unitree_sdk2py/utils/bqueue.py | 58 + .../unitree_sdk2py/utils/clib_lookup.py | 17 + .../unitree_sdk2py/utils/crc.py | 228 ++ .../unitree_sdk2py/utils/future.py | 104 + .../unitree_sdk2py/utils/hz_sample.py | 24 + .../unitree_sdk2py/utils/joystick.py | 251 ++ .../unitree_sdk2py/utils/lib/crc_aarch64.so | 3 + .../unitree_sdk2py/utils/lib/crc_amd64.so | 3 + .../unitree_sdk2py/utils/singleton.py | 11 + .../unitree_sdk2py/utils/thread.py | 83 + .../unitree_sdk2py/utils/timerfd.py | 45 + gr00t_wbc/__init__.py | 3 + gr00t_wbc/control/__init__.py | 0 gr00t_wbc/control/base/__init__.py | 0 gr00t_wbc/control/base/env.py | 45 + gr00t_wbc/control/base/humanoid_env.py | 60 + gr00t_wbc/control/base/policy.py | 47 + gr00t_wbc/control/base/sensor.py | 35 + gr00t_wbc/control/envs/__init__.py | 0 gr00t_wbc/control/envs/g1/__init__.py | 0 gr00t_wbc/control/envs/g1/g1_body.py | 67 + gr00t_wbc/control/envs/g1/g1_env.py | 324 ++ gr00t_wbc/control/envs/g1/g1_hand.py | 89 + gr00t_wbc/control/envs/g1/sim/__init__.py | 0 gr00t_wbc/control/envs/g1/sim/base_sim.py | 772 ++++ .../envs/g1/sim/image_publish_utils.py | 256 ++ gr00t_wbc/control/envs/g1/sim/metric_utils.py | 71 + gr00t_wbc/control/envs/g1/sim/robocasa_sim.py | 63 + gr00t_wbc/control/envs/g1/sim/sim_utilts.py | 96 + .../control/envs/g1/sim/simulator_factory.py | 144 + .../envs/g1/sim/unitree_sdk2py_bridge.py | 459 +++ gr00t_wbc/control/envs/g1/utils/__init__.py | 0 .../control/envs/g1/utils/command_sender.py | 146 + .../control/envs/g1/utils/joint_safety.py | 534 +++ .../control/envs/g1/utils/state_processor.py | 143 + gr00t_wbc/control/envs/robocasa/__init__.py | 0 .../control/envs/robocasa/async_env_server.py | 303 ++ gr00t_wbc/control/envs/robocasa/sync_env.py | 584 +++ .../control/envs/robocasa/utils/__init__.py | 0 .../envs/robocasa/utils/cam_key_converter.py | 73 + .../envs/robocasa/utils/controller_utils.py | 54 + .../envs/robocasa/utils/robocasa_env.py | 443 +++ .../robocasa/utils/robot_key_converter.py | 301 ++ .../control/envs/robocasa/utils/sim_utils.py | 8 + gr00t_wbc/control/main/__init__.py | 0 gr00t_wbc/control/main/config_template.py | 45 + gr00t_wbc/control/main/constants.py | 16 + gr00t_wbc/control/main/teleop/__init__.py | 0 .../control/main/teleop/configs/configs.py | 484 +++ .../teleop/configs/g1_29dof_gear_wbc.yaml | 421 +++ .../main/teleop/configs/g1_gear_wbc.yaml | 45 + .../main/teleop/configs/identifiers.py | 14 + .../main/teleop/playback_sync_sim_data.py | 627 ++++ .../control/main/teleop/run_camera_viewer.py | 249 ++ .../main/teleop/run_g1_control_loop.py | 236 ++ .../main/teleop/run_g1_data_exporter.py | 364 ++ .../main/teleop/run_navigation_policy_loop.py | 68 + gr00t_wbc/control/main/teleop/run_sim_loop.py | 61 + .../teleop/run_sync_sim_data_collection.py | 213 ++ .../main/teleop/run_teleop_policy_loop.py | 110 + gr00t_wbc/control/policy/__init__.py | 0 .../policy/g1_decoupled_whole_body_policy.py | 157 + .../control/policy/g1_gear_wbc_policy.py | 295 ++ gr00t_wbc/control/policy/identity_policy.py | 25 + .../control/policy/interpolation_policy.py | 297 ++ .../policy/keyboard_navigation_policy.py | 87 + .../control/policy/lerobot_replay_policy.py | 107 + gr00t_wbc/control/policy/teleop_policy.py | 207 ++ .../control/policy/wbc_policy_factory.py | 65 + gr00t_wbc/control/robot_model/__init__.py | 3 + .../robot_model/instantiation/__init__.py | 15 + .../control/robot_model/instantiation/g1.py | 62 + .../robot_model/model_data/g1/g1_29dof.urdf | 1091 ++++++ .../model_data/g1/g1_29dof_old.xml | 568 +++ .../model_data/g1/g1_29dof_with_hand.urdf | 1497 ++++++++ .../model_data/g1/g1_29dof_with_hand.xml | 748 ++++ ...9dof_with_hand_rev_1_0_activatedfinger.xml | 669 ++++ .../model_data/g1/lift_box_43dof.xml | 49 + .../model_data/g1/meshes/head_link.STL | 3 + .../g1/meshes/left_ankle_pitch_link.STL | 3 + .../g1/meshes/left_ankle_roll_link.STL | 3 + .../model_data/g1/meshes/left_elbow_link.STL | 3 + .../g1/meshes/left_elbow_link_merge.STL | 3 + .../g1/meshes/left_hand_index_0_link.STL | 3 + .../g1/meshes/left_hand_index_1_link.STL | 3 + .../g1/meshes/left_hand_middle_0_link.STL | 3 + .../g1/meshes/left_hand_middle_1_link.STL | 3 + .../g1/meshes/left_hand_palm_link.STL | 3 + .../g1/meshes/left_hand_thumb_0_link.STL | 3 + .../g1/meshes/left_hand_thumb_1_link.STL | 3 + .../g1/meshes/left_hand_thumb_2_link.STL | 3 + .../g1/meshes/left_hip_pitch_link.STL | 3 + .../g1/meshes/left_hip_roll_link.STL | 3 + .../g1/meshes/left_hip_yaw_link.STL | 3 + .../model_data/g1/meshes/left_knee_link.STL | 3 + .../model_data/g1/meshes/left_rubber_hand.STL | 3 + .../g1/meshes/left_shoulder_pitch_link.STL | 3 + .../g1/meshes/left_shoulder_roll_link.STL | 3 + .../g1/meshes/left_shoulder_yaw_link.STL | 3 + .../g1/meshes/left_wrist_pitch_link.STL | 3 + .../g1/meshes/left_wrist_roll_link.STL | 3 + .../g1/meshes/left_wrist_roll_rubber_hand.STL | 3 + .../g1/meshes/left_wrist_yaw_link.STL | 3 + .../model_data/g1/meshes/logo_link.STL | 3 + .../model_data/g1/meshes/pelvis.STL | 3 + .../g1/meshes/pelvis_contour_link.STL | 3 + .../g1/meshes/right_ankle_pitch_link.STL | 3 + .../g1/meshes/right_ankle_roll_link.STL | 3 + .../model_data/g1/meshes/right_elbow_link.STL | 3 + .../g1/meshes/right_elbow_link_merge.STL | 3 + .../g1/meshes/right_hand_index_0_link.STL | 3 + .../g1/meshes/right_hand_index_1_link.STL | 3 + .../g1/meshes/right_hand_middle_0_link.STL | 3 + .../g1/meshes/right_hand_middle_1_link.STL | 3 + .../g1/meshes/right_hand_palm_link.STL | 3 + .../g1/meshes/right_hand_thumb_0_link.STL | 3 + .../g1/meshes/right_hand_thumb_1_link.STL | 3 + .../g1/meshes/right_hand_thumb_2_link.STL | 3 + .../g1/meshes/right_hip_pitch_link.STL | 3 + .../g1/meshes/right_hip_roll_link.STL | 3 + .../g1/meshes/right_hip_yaw_link.STL | 3 + .../model_data/g1/meshes/right_knee_link.STL | 3 + .../g1/meshes/right_rubber_hand.STL | 3 + .../g1/meshes/right_shoulder_pitch_link.STL | 3 + .../g1/meshes/right_shoulder_roll_link.STL | 3 + .../g1/meshes/right_shoulder_yaw_link.STL | 3 + .../g1/meshes/right_wrist_pitch_link.STL | 3 + .../g1/meshes/right_wrist_roll_link.STL | 3 + .../meshes/right_wrist_roll_rubber_hand.STL | 3 + .../g1/meshes/right_wrist_yaw_link.STL | 3 + .../g1/meshes/torso_constraint_L_link.STL | 3 + .../g1/meshes/torso_constraint_L_rod_link.STL | 3 + .../g1/meshes/torso_constraint_R_link.STL | 3 + .../g1/meshes/torso_constraint_R_rod_link.STL | 3 + .../model_data/g1/meshes/torso_link.STL | 3 + .../g1/meshes/torso_link_rev_1_0.STL | 3 + .../g1/meshes/waist_constraint_L.STL | 3 + .../g1/meshes/waist_constraint_R.STL | 3 + .../model_data/g1/meshes/waist_roll_link.STL | 3 + .../g1/meshes/waist_roll_link_rev_1_0.STL | 3 + .../g1/meshes/waist_support_link.STL | 3 + .../model_data/g1/meshes/waist_yaw_link.STL | 3 + .../g1/meshes/waist_yaw_link_rev_1_0.STL | 3 + .../model_data/g1/pnp_bottle_43dof.xml | 49 + .../model_data/g1/pnp_cube_43dof.xml | 49 + .../robot_model/model_data/g1/scene_29dof.xml | 33 + .../g1/scene_29dof_activated3dex.xml | 30 + .../robot_model/model_data/g1/scene_43dof.xml | 30 + gr00t_wbc/control/robot_model/robot_model.py | 772 ++++ .../robot_model/supplemental_info/__init__.py | 5 + .../supplemental_info/g1/__init__.py | 0 .../g1/g1_supplemental_info.py | 330 ++ .../robot_supplemental_info.py | 91 + gr00t_wbc/control/sensor/__init__.py | 0 gr00t_wbc/control/sensor/composed_camera.py | 440 +++ gr00t_wbc/control/sensor/oak.py | 324 ++ gr00t_wbc/control/sensor/sensor_server.py | 128 + .../device/SDKClient_Linux/ClientLogging.hpp | 158 + .../ClientPlatformSpecific.cpp | 559 +++ .../ClientPlatformSpecific.hpp | 90 + .../ClientPlatformSpecificTypes.hpp | 48 + .../teleop/device/SDKClient_Linux/Dockerfile | 88 + .../SDKClient_Linux/Dockerfile.Integrated | 69 + .../teleop/device/SDKClient_Linux/Main.cpp | 52 + .../teleop/device/SDKClient_Linux/Makefile | 59 + .../ManusSDK/include/ManusSDK.h | 1058 ++++++ .../include/ManusSDKTypeInitializers.h | 397 +++ .../ManusSDK/include/ManusSDKTypes.h | 1717 +++++++++ .../ManusSDK/lib/libManusSDK.so | 3 + .../ManusSDK/lib/libManusSDK_Integrated.so | 3 + ...anusServer.cpython-310-x86_64-linux-gnu.so | 3 + .../teleop/device/SDKClient_Linux/Readme.md | 4 + .../device/SDKClient_Linux/SDKClient.cpp | 3118 +++++++++++++++++ .../device/SDKClient_Linux/SDKClient.hpp | 349 ++ .../SDKClient_Linux.code-workspace | 7 + .../SDKClient_Linux/SDKClient_Linux.vcxproj | 249 ++ .../SDKClient_Linux.vcxproj.filters | 25 + .../objects/ClientPlatformSpecific.d | 4 + .../objects/ClientPlatformSpecific.o | Bin 0 -> 562984 bytes .../device/SDKClient_Linux/objects/Main.d | 124 + .../device/SDKClient_Linux/objects/Main.o | Bin 0 -> 3823648 bytes .../SDKClient_Linux/objects/SDKClient.d | 5 + .../SDKClient_Linux/objects/SDKClient.o | Bin 0 -> 1904960 bytes .../teleop/device/SDKClient_Linux/test.py | 14 + .../control/teleop/device/iphone/iphone.py | 102 + gr00t_wbc/control/teleop/device/manus.py | 138 + ...it_PC_Service_1.0.0_ubuntu_22.04_amd64.deb | 3 + .../pico/roboticsservice_1.0.0.0_arm64.deb | 3 + .../control/teleop/device/pico/xr_client.py | 155 + gr00t_wbc/control/teleop/gui/cli.py | 20 + gr00t_wbc/control/teleop/gui/core/events3d.py | 253 ++ gr00t_wbc/control/teleop/gui/core/gui3d.py | 670 ++++ .../control/teleop/gui/core/guicommon.py | 860 +++++ gr00t_wbc/control/teleop/gui/core/module3d.py | 1307 +++++++ .../control/teleop/gui/core/selection.py | 165 + .../control/teleop/gui/library/getpath.py | 446 +++ gr00t_wbc/control/teleop/gui/library/image.py | 358 ++ .../control/teleop/gui/library/language.py | 141 + gr00t_wbc/control/teleop/gui/library/log.py | 283 ++ .../control/teleop/gui/library/matrix.py | 156 + gr00t_wbc/control/teleop/gui/library/mh.py | 112 + .../control/teleop/gui/library/profiler.py | 100 + gr00t_wbc/control/teleop/gui/library/qtgui.py | 190 + .../control/teleop/gui/library/universal.py | 82 + .../control/teleop/gui/library/xdg_parser.py | 101 + gr00t_wbc/control/teleop/gui/main.py | 549 +++ gr00t_wbc/control/teleop/main/test_iphone.py | 306 ++ gr00t_wbc/control/teleop/main/test_manus.py | 102 + gr00t_wbc/control/teleop/main/test_vive.py | 82 + .../teleop/pre_processor/fingers/fingers.py | 16 + .../teleop/pre_processor/pre_processor.py | 13 + .../teleop/pre_processor/wrists/wrists.py | 110 + .../teleop/solver/body/body_ik_solver.py | 179 + .../solver/body/body_ik_solver_settings.py | 35 + .../solver/hand/g1_gripper_ik_solver.py | 137 + .../instantiation/g1_hand_ik_instantiation.py | 8 + gr00t_wbc/control/teleop/solver/solver.py | 17 + .../control/teleop/streamers/base_streamer.py | 46 + .../teleop/streamers/dummy_streamer.py | 81 + .../teleop/streamers/iphone_streamer.py | 150 + .../teleop/streamers/joycon_streamer.py | 695 ++++ .../teleop/streamers/leapmotion_streamer.py | 289 ++ .../teleop/streamers/manus_streamer.py | 90 + .../control/teleop/streamers/pico_streamer.py | 280 ++ .../control/teleop/streamers/vive_streamer.py | 131 + .../control/teleop/teleop_retargeting_ik.py | 148 + gr00t_wbc/control/teleop/teleop_streamer.py | 238 ++ gr00t_wbc/control/utils/__init__.py | 0 gr00t_wbc/control/utils/cv_bridge.py | 396 +++ gr00t_wbc/control/utils/episode_state.py | 32 + gr00t_wbc/control/utils/gear_wbc_utils.py | 100 + gr00t_wbc/control/utils/img_viewer.py | 91 + .../control/utils/keyboard_dispatcher.py | 255 ++ gr00t_wbc/control/utils/logging_utils.py | 83 + gr00t_wbc/control/utils/n1_utils.py | 226 ++ gr00t_wbc/control/utils/network_utils.py | 137 + gr00t_wbc/control/utils/ros_utils.py | 201 ++ gr00t_wbc/control/utils/run_real_checklist.py | 121 + gr00t_wbc/control/utils/service.py | 182 + gr00t_wbc/control/utils/sync_sim_utils.py | 590 ++++ gr00t_wbc/control/utils/telemetry.py | 129 + .../control/utils/term_color_constants.py | 19 + gr00t_wbc/control/utils/text_to_speech.py | 28 + .../visualization/humanoid_visualizer.py | 52 + .../visualization/meshcat_visualizer_env.py | 77 + gr00t_wbc/data/constants.py | 5 + gr00t_wbc/data/exporter.py | 514 +++ gr00t_wbc/data/utils.py | 156 + gr00t_wbc/data/video_writer.py | 102 + gr00t_wbc/data/viz/rerun_viz.py | 213 ++ .../dexmg/gr00trobocasa/requirements.txt | 1 + .../dexmg/gr00trobocasa/robocasa/__init__.py | 73 + .../environments/locomanipulation/__init__.py | 13 + .../environments/locomanipulation/base.py | 1658 +++++++++ .../locomanipulation/locomanip.py | 83 + .../locomanipulation/locomanip_basic.py | 576 +++ .../locomanipulation/locomanip_pnp.py | 99 + .../third_party_controller/__init__.py | 0 .../default_mink_ik_g1_gear_wbc.json | 113 + .../default_mink_ik_g1_gear_wbc_gc.json | 117 + .../dexmg/gr00trobocasa/robocasa/macros.py | 32 + .../gr00trobocasa/robocasa/models/__init__.py | 3 + .../arenas/gear_factory/gear_factory.xml | 95 + .../gear_factory/meshes/gear_factory_0.obj | 3 + .../gear_factory/meshes/gear_factory_1.obj | 3 + .../gear_factory/meshes/gear_factory_10.obj | 3 + .../gear_factory/meshes/gear_factory_11.obj | 3 + .../gear_factory/meshes/gear_factory_2.obj | 3 + .../gear_factory/meshes/gear_factory_3.obj | 3 + .../gear_factory/meshes/gear_factory_4.obj | 3 + .../gear_factory/meshes/gear_factory_5.obj | 3 + .../gear_factory/meshes/gear_factory_6.obj | 3 + .../gear_factory/meshes/gear_factory_7.obj | 3 + .../gear_factory/meshes/gear_factory_8.obj | 3 + .../gear_factory/meshes/gear_factory_9.obj | 3 + .../meshes/gear_factory_poles.obj | 3 + .../meshes/gear_factory_walls.obj | 3 + .../gear_factory/meshes/paint_lines.obj | 3 + .../gear_factory/textures/Fence_Metal_AO.png | 3 + .../gear_factory/textures/Laser_Beam.png | 3 + .../gear_factory/textures/Light_Curtain.png | 3 + .../gear_factory/textures/Rack_3@Medium.png | 3 + .../gear_factory/textures/Rack_4@Medium.png | 3 + .../gear_factory/textures/Robots_1@Medium.png | 3 + .../textures/Robots_1_Fence@Medium.png | 3 + .../gear_factory/textures/Robots_2@Medium.png | 3 + .../textures/Robots_2_Fence@Medium.png | 3 + .../gear_factory/textures/Steel_ASTM_AO.png | 3 + .../textures/T_Floor_A1_Albedo.png | 3 + .../gear_factory/textures/factory@1024.png | 3 + .../textures/mtl-base_color@Small.png | 3 + .../assets/arenas/gear_lab/gear_lab.xml | 27 + .../assets/arenas/gear_lab/textures/floor.png | 3 + .../assets/arenas/gear_lab/textures/wall.png | 3 + .../models/assets/arenas/ground_arena.xml | 28 + .../collision/tmpm_h04v7s_collision_0.obj | 3 + .../collision/tmpm_h04v7s_collision_1.obj | 3 + .../collision/tmpm_h04v7s_collision_2.obj | 3 + .../collision/tmpm_h04v7s_collision_3.obj | 3 + .../collision/tmpm_h04v7s_collision_4.obj | 3 + .../omniverse/locomanip/apple_0/model.xml | 35 + .../locomanip/apple_0/visual/image0.png | 3 + .../locomanip/apple_0/visual/material.mtl | 8 + .../apple_0/visual/model_normalized_0.obj | 3 + .../locomanip/cardbox_a1/material.mtl | 6 + .../cardbox_a1/meshes/cardbox_a1.obj | 3 + .../omniverse/locomanip/cardbox_a1/model.xml | 29 + .../textures/T_Cardbox_A1_Albedo.png | 3 + .../locomanip/cardbox_b1/material.mtl | 6 + .../cardbox_b1/meshes/cardbox_b1.obj | 3 + .../omniverse/locomanip/cardbox_b1/model.xml | 29 + .../textures/T_Cardbox_B3_Albedo.png | 3 + .../locomanip/cardbox_c1/material.mtl | 6 + .../cardbox_c1/meshes/cardbox_c1.obj | 3 + .../omniverse/locomanip/cardbox_c1/model.xml | 29 + .../textures/T_Cardbox_C2_Albedo.png | 3 + .../locomanip/cardbox_d1/material.mtl | 6 + .../cardbox_d1/meshes/cardbox_d1.obj | 3 + .../omniverse/locomanip/cardbox_d1/model.xml | 29 + .../textures/T_Cardbox_D2_Albedo.png | 3 + .../omniverse/locomanip/clock_0/material.mtl | 5 + .../locomanip/clock_0/meshes/clock_0_0_0.obj | 3 + .../locomanip/clock_0/meshes/clock_0_0_1.obj | 3 + .../locomanip/clock_0/meshes/clock_0_0_2.obj | 3 + .../locomanip/clock_0/meshes/clock_0_0_3.obj | 3 + .../locomanip/clock_0/meshes/clock_0_0_4.obj | 3 + .../locomanip/clock_0/meshes/clock_0_0_5.obj | 3 + .../locomanip/clock_0/meshes/clock_0_0_6.obj | 3 + .../meshes/clock_0_0_collision_0_0.obj | 3 + .../meshes/clock_0_0_collision_0_1.obj | 3 + .../meshes/clock_0_0_collision_0_10.obj | 3 + .../meshes/clock_0_0_collision_0_11.obj | 3 + .../meshes/clock_0_0_collision_0_2.obj | 3 + .../meshes/clock_0_0_collision_0_3.obj | 3 + .../meshes/clock_0_0_collision_0_4.obj | 3 + .../meshes/clock_0_0_collision_0_5.obj | 3 + .../meshes/clock_0_0_collision_0_6.obj | 3 + .../meshes/clock_0_0_collision_0_7.obj | 3 + .../meshes/clock_0_0_collision_0_8.obj | 3 + .../meshes/clock_0_0_collision_0_9.obj | 3 + .../omniverse/locomanip/clock_0/model.xml | 72 + .../clock_0/textures/chasovaya_baseColor.png | 3 + .../clock_0/textures/ciferblat_baseColor.png | 3 + .../clock_0/textures/hrom_baseColor.png | 3 + .../clock_0/textures/korpus_baseColor.png | 3 + .../clock_0/textures/minutnaya_baseColor.png | 3 + .../locomanip/control_box/material.mtl | 6 + .../control_box/meshes/control_box_0.obj | 3 + .../control_box/meshes/control_box_1.obj | 3 + .../control_box/meshes/control_box_2.obj | 3 + .../control_box/meshes/control_box_3.obj | 3 + .../omniverse/locomanip/control_box/model.xml | 48 + .../textures/Switch_Box_Rack_AO.png | 3 + .../control_conveyorbelt_a08/material.mtl | 5 + .../meshes/ControlPanel_AO.png | 3 + .../meshes/control_conveyorbelt_a08_0.obj | 3 + .../meshes/control_conveyorbelt_a08_1.obj | 3 + .../meshes/control_conveyorbelt_a08_4.obj | 3 + .../meshes/control_conveyorbelt_a08_body.mtl | 12 + .../meshes/control_conveyorbelt_a08_body.obj | 3 + .../control_conveyorbelt_a08_button.obj | 3 + .../control_conveyorbelt_a08_collision_0.obj | 3 + .../control_conveyorbelt_a08_collision_1.obj | 3 + .../control_conveyorbelt_a08_collision_2.obj | 3 + .../meshes/control_conveyorbelt_a08_lever.obj | 3 + .../control_conveyorbelt_a08/model.xml | 59 + .../textures/ControlPanel_AO.png | 3 + .../locomanip/dock_board_a12/material.mtl | 6 + .../meshes/dock_board_a12_0.obj | 3 + .../meshes/dock_board_a12_1.obj | 3 + .../meshes/dock_board_a12_2.obj | 3 + .../meshes/dock_board_a12_convex_border_l.obj | 3 + .../meshes/dock_board_a12_convex_border_r.obj | 3 + .../meshes/dock_board_a12_convex_platform.obj | 3 + .../locomanip/dock_board_a12/model.xml | 41 + .../T_Aluminium_Brushed_A1_Albedo.png | 3 + .../textures/T_Plastic_Orange_B_Albedo.png | 3 + .../t_metal_circles_grid_grey_a_Albedo.png | 3 + .../meshes/ergo_table_0.obj | 3 + .../meshes/ergo_table_1.obj | 3 + .../meshes/ergo_table_2.obj | 3 + .../meshes/ergo_table_3.obj | 3 + .../locomanip/factory_ergo_table/model.xml | 40 + .../textures/Rubber_Smooth_BaseColor.png | 3 + .../omniverse/locomanip/jug_a01/material.mtl | 6 + .../locomanip/jug_a01/meshes/jug_a01.obj | 3 + .../omniverse/locomanip/jug_a01/model.xml | 31 + .../locomanip/jug_a01/textures/Jug_A.png | 3 + .../omniverse/locomanip/lab_shelf/model.xml | 71 + .../locomanip/lab_shelf_board/model.xml | 35 + .../locomanip/lab_shelf_cabinet/model.xml | 86 + .../locomanip/lab_table/material.mtl | 6 + .../locomanip/lab_table/meshes/lab_table.obj | 3 + .../omniverse/locomanip/lab_table/model.xml | 33 + .../locomanip/lab_table/textures/Image_0.png | 3 + .../locomanip/longbox_a08/material.mtl | 6 + .../longbox_a08/meshes/longbox_a08_0.obj | 3 + .../longbox_a08/meshes/longbox_a08_1.obj | 3 + .../omniverse/locomanip/longbox_a08/model.xml | 33 + .../textures/T_LongBox_A01_Tile_Albedo.png | 3 + .../textures/T_LongBox_A01_Trim_Albedo.png | 3 + .../locomanip/longbox_a09/material.mtl | 6 + .../longbox_a09/meshes/longbox_a09_0.obj | 3 + .../longbox_a09/meshes/longbox_a09_1.obj | 3 + .../omniverse/locomanip/longbox_a09/model.xml | 33 + .../textures/T_LongBox_A01_Tile_Albedo.png | 3 + .../textures/T_LongBox_A01_Trim_Albedo.png | 3 + .../mobile_shelving_cart/material.mtl | 5 + .../meshes/mobile_shelving_cart_0.obj | 3 + .../meshes/mobile_shelving_cart_1.obj | 3 + .../meshes/mobile_shelving_cart_2.obj | 3 + .../meshes/mobile_shelving_cart_3.obj | 3 + .../meshes/mobile_shelving_cart_4.obj | 3 + .../mobile_shelving_cart_wheel_disk.obj | 3 + .../mobile_shelving_cart_wheel_hinge.obj | 3 + .../mobile_shelving_cart_wheel_rubber.obj | 3 + .../locomanip/mobile_shelving_cart/model.xml | 97 + .../T_Aluminium_Brushed_A1_Albedo.png | 3 + .../textures/T_Plastic_Blue_A_Albedo.png | 3 + .../locomanip/pallet_b1/material.mtl | 6 + .../locomanip/pallet_b1/meshes/pallet_b1.obj | 3 + .../omniverse/locomanip/pallet_b1/model.xml | 29 + .../pallet_b1/textures/T_Pallet_A2_Albedo.png | 3 + .../locomanip/pallet_c1/material.mtl | 6 + .../locomanip/pallet_c1/meshes/pallet_c1.obj | 3 + .../omniverse/locomanip/pallet_c1/model.xml | 29 + .../pallet_c1/textures/T_Pallet_A3_Albedo.png | 3 + .../collision/tmpzdovka8d_collision_0.obj | 3 + .../collision/tmpzdovka8d_collision_1.obj | 3 + .../collision/tmpzdovka8d_collision_2.obj | 3 + .../collision/tmpzdovka8d_collision_3.obj | 3 + .../omniverse/locomanip/plate_1/model.xml | 32 + .../locomanip/plate_1/visual/material.mtl | 7 + .../plate_1/visual/model_normalized_0.obj | 3 + .../locomanip/rubix_cube_1/material.mtl | 6 + .../rubix_cube_1/meshes/rubix_cube_1_0.obj | 3 + .../meshes/rubix_cube_1_0_collision_0_0.obj | 3 + .../meshes/rubix_cube_1_0_collision_0_1.obj | 3 + .../meshes/rubix_cube_1_0_collision_0_2.obj | 3 + .../meshes/rubix_cube_1_0_collision_0_3.obj | 3 + .../locomanip/rubix_cube_1/model.xml | 34 + .../textures/Material.002_baseColor.png | 3 + .../locomanip/shelf_a12/material.mtl | 6 + .../shelf_a12/meshes/shelf_a12_0.obj | 3 + .../shelf_a12/meshes/shelf_a12_1.obj | 3 + .../omniverse/locomanip/shelf_a12/model.xml | 44 + .../shelf_a12/textures/Shelf_A12_Poles.png | 3 + .../shelf_a12/textures/Shelf_A12_Shelves.png | 3 + .../omniverse/locomanip/target_zone/model.xml | 25 + .../target_zone/textures/Target_Zone.png | 3 + .../omniverse/locomanip/tote_f01/material.mtl | 5 + .../locomanip/tote_f01/meshes/tote_f01_0.obj | 3 + .../locomanip/tote_f01/meshes/tote_f01_1.obj | 3 + .../omniverse/locomanip/tote_f01/model.xml | 32 + .../omniverse/locomanip/tote_f02/material.mtl | 5 + .../locomanip/tote_f02/meshes/tote_f02_0.obj | 3 + .../locomanip/tote_f02/meshes/tote_f02_1.obj | 3 + .../omniverse/locomanip/tote_f02/model.xml | 34 + .../workshop_trolley_a01/material.mtl | 6 + .../meshes/wheel_disk.obj | 3 + .../meshes/wheel_hinge.obj | 3 + .../meshes/wheel_rubber.obj | 3 + .../meshes/workshop_trolley_a01_0.obj | 3 + .../meshes/workshop_trolley_a01_1.obj | 3 + .../meshes/workshop_trolley_a01_2.obj | 3 + .../meshes/workshop_trolley_a01_3.obj | 3 + .../meshes/workshop_trolley_a01_4.obj | 3 + .../meshes/workshop_trolley_a01_5.obj | 3 + .../locomanip/workshop_trolley_a01/model.xml | 85 + .../textures/T_Plastic_Blue_A_Albedo.png | 3 + .../robots/unitree_g1/g1_29dof_rev_1_0.xml | 431 +++ .../unitree_g1/g1_threefinger_left_hand.xml | 146 + .../unitree_g1/g1_threefinger_right_hand.xml | 148 + .../robots/unitree_g1/meshes/head_link.STL | 3 + .../meshes/left_ankle_pitch_link.STL | 3 + .../meshes/left_ankle_roll_link.STL | 3 + .../unitree_g1/meshes/left_elbow_link.STL | 3 + .../meshes/left_elbow_link_merge.STL | 3 + .../meshes/left_hand_index_0_link.STL | 3 + .../meshes/left_hand_index_1_link.STL | 3 + .../meshes/left_hand_middle_0_link.STL | 3 + .../meshes/left_hand_middle_1_link.STL | 3 + .../unitree_g1/meshes/left_hand_palm_link.STL | 3 + .../meshes/left_hand_thumb_0_link.STL | 3 + .../meshes/left_hand_thumb_1_link.STL | 3 + .../meshes/left_hand_thumb_2_link.STL | 3 + .../unitree_g1/meshes/left_hip_pitch_link.STL | 3 + .../unitree_g1/meshes/left_hip_roll_link.STL | 3 + .../unitree_g1/meshes/left_hip_yaw_link.STL | 3 + .../unitree_g1/meshes/left_knee_link.STL | 3 + .../unitree_g1/meshes/left_rubber_hand.STL | 3 + .../meshes/left_shoulder_pitch_link.STL | 3 + .../meshes/left_shoulder_roll_link.STL | 3 + .../meshes/left_shoulder_yaw_link.STL | 3 + .../meshes/left_wrist_pitch_link.STL | 3 + .../meshes/left_wrist_roll_link.STL | 3 + .../meshes/left_wrist_roll_rubber_hand.STL | 3 + .../unitree_g1/meshes/left_wrist_yaw_link.STL | 3 + .../robots/unitree_g1/meshes/logo_link.STL | 3 + .../robots/unitree_g1/meshes/pelvis.STL | 3 + .../unitree_g1/meshes/pelvis_contour_link.STL | 3 + .../meshes/right_ankle_pitch_link.STL | 3 + .../meshes/right_ankle_roll_link.STL | 3 + .../unitree_g1/meshes/right_elbow_link.STL | 3 + .../meshes/right_elbow_link_merge.STL | 3 + .../meshes/right_hand_index_0_link.STL | 3 + .../meshes/right_hand_index_1_link.STL | 3 + .../meshes/right_hand_middle_0_link.STL | 3 + .../meshes/right_hand_middle_1_link.STL | 3 + .../meshes/right_hand_palm_link.STL | 3 + .../meshes/right_hand_thumb_0_link.STL | 3 + .../meshes/right_hand_thumb_1_link.STL | 3 + .../meshes/right_hand_thumb_2_link.STL | 3 + .../meshes/right_hip_pitch_link.STL | 3 + .../unitree_g1/meshes/right_hip_roll_link.STL | 3 + .../unitree_g1/meshes/right_hip_yaw_link.STL | 3 + .../unitree_g1/meshes/right_knee_link.STL | 3 + .../unitree_g1/meshes/right_rubber_hand.STL | 3 + .../meshes/right_shoulder_pitch_link.STL | 3 + .../meshes/right_shoulder_roll_link.STL | 3 + .../meshes/right_shoulder_yaw_link.STL | 3 + .../meshes/right_wrist_pitch_link.STL | 3 + .../meshes/right_wrist_roll_link.STL | 3 + .../meshes/right_wrist_roll_rubber_hand.STL | 3 + .../meshes/right_wrist_yaw_link.STL | 3 + .../meshes/torso_constraint_L_link.STL | 3 + .../meshes/torso_constraint_L_rod_link.STL | 3 + .../meshes/torso_constraint_R_link.STL | 3 + .../meshes/torso_constraint_R_rod_link.STL | 3 + .../robots/unitree_g1/meshes/torso_link.STL | 3 + .../unitree_g1/meshes/torso_link_rev_1_0.STL | 3 + .../unitree_g1/meshes/waist_constraint_L.STL | 3 + .../unitree_g1/meshes/waist_constraint_R.STL | 3 + .../unitree_g1/meshes/waist_roll_link.STL | 3 + .../meshes/waist_roll_link_rev_1_0.STL | 3 + .../unitree_g1/meshes/waist_support_link.STL | 3 + .../unitree_g1/meshes/waist_yaw_link.STL | 3 + .../meshes/waist_yaw_link_rev_1_0.STL | 3 + .../robocasa/models/grippers/__init__.py | 1 + .../models/grippers/g1_threefinger_hands.py | 170 + .../robocasa/models/objects/__init__.py | 13 + .../models/objects/composite/__init__.py | 6 + .../robocasa/models/objects/composite/bin.py | 205 ++ .../objects/composite/box_pattern_object.py | 124 + .../robocasa/models/objects/composite/lid.py | 136 + .../models/objects/composite/needle.py | 109 + .../objects/composite/pot_with_handles.py | 396 +++ .../models/objects/composite/ring_tripod.py | 194 + .../models/objects/composite_body/__init__.py | 8 + .../composite_body/bin_with_handles.py | 162 + .../objects/composite_body/coffee_machine.py | 244 ++ .../models/objects/composite_body/cup.py | 183 + .../inverse_stacked_cylinder.py | 139 + .../objects/composite_body/lightbulb.py | 153 + .../objects/composite_body/sliding_box.py | 132 + .../models/objects/composite_body/socket.py | 334 ++ .../objects/composite_body/spray_bottle.py | 207 ++ .../objects/composite_body/stacked_box.py | 111 + .../composite_body/stacked_cylinder.py | 127 + .../objects/composite_body/stove_plug.py | 396 +++ .../robocasa/models/objects/objects.py | 276 ++ .../robocasa/models/objects/xml_objects.py | 268 ++ .../robocasa/models/robots/__init__.py | 779 ++++ .../models/robots/manipulators/__init__.py | 1 + .../models/robots/manipulators/g1_robot.py | 391 +++ .../robocasa/models/scenes/__init__.py | 3 + .../robocasa/models/scenes/factory_arena.py | 14 + .../robocasa/models/scenes/ground_arena.py | 13 + .../robocasa/models/scenes/lab_arena.py | 12 + .../gr00trobocasa/robocasa/utils/__init__.py | 1 + .../robocasa/utils/camera_utils.py | 246 ++ .../robocasa/utils/config_utils.py | 63 + .../robocasa/utils/cotrain_utils.py | 25 + .../robocasa/utils/dataset_registry.py | 352 ++ .../robocasa/utils/dexmg_utils.py | 99 + .../gr00trobocasa/robocasa/utils/env_utils.py | 105 + .../robocasa/utils/gym_utils/__init__.py | 4 + .../utils/gym_utils/gymnasium_basic.py | 417 +++ .../robocasa/utils/model_zoo/__init__.py | 3 + .../robocasa/utils/model_zoo/file_utils.py | 35 + .../utils/model_zoo/fixture_template.xml | 27 + .../robocasa/utils/model_zoo/install_vhacd.sh | 21 + .../robocasa/utils/model_zoo/log_utils.py | 46 + .../utils/model_zoo/mjcf_gen_utils.py | 800 +++++ .../robocasa/utils/model_zoo/mjcf_obj.py | 251 ++ .../robocasa/utils/model_zoo/mtl_utils.py | 198 ++ .../utils/model_zoo/object_play_env.py | 250 ++ .../utils/model_zoo/object_template.xml | 15 + .../robocasa/utils/model_zoo/parser_utils.py | 61 + .../robocasa/utils/object_utils.py | 621 ++++ .../robocasa/utils/placement_samplers.py | 711 ++++ .../robocasa/utils/postprocess_xml_utils.py | 78 + .../robocasa/utils/scene/__init__.py | 0 .../robocasa/utils/scene/configs.py | 126 + .../robocasa/utils/scene/scene.py | 146 + .../robocasa/utils/scene/success_criteria.py | 157 + .../robocasa/utils/texture_swap.py | 670 ++++ .../robocasa/utils/transform_utils.py | 56 + .../robocasa/utils/usd/README.md | 30 + .../robocasa/utils/usd/__init__.py | 4 + .../robocasa/utils/usd/component.py | 429 +++ .../gr00trobocasa/robocasa/utils/usd/demo.py | 58 + .../robocasa/utils/usd/exporter.py | 403 +++ .../robocasa/utils/usd/exporter_test.py | 71 + .../robocasa/utils/usd/shapes.py | 138 + .../gr00trobocasa/robocasa/utils/usd/utils.py | 27 + .../robocasa/utils/visuals_utls.py | 31 + .../robocasa/wrappers/__init__.py | 1 + .../robocasa/wrappers/ik_wrapper.py | 226 ++ gr00t_wbc/dexmg/gr00trobocasa/setup.py | 30 + gr00t_wbc/sim2mujoco/.gitattributes | 1 + gr00t_wbc/sim2mujoco/.gitignore | 1 + gr00t_wbc/sim2mujoco/README.md | 5 + gr00t_wbc/sim2mujoco/requirements.txt | 8 + .../sim2mujoco/resources/robots/g1/README.md | 30 + .../sim2mujoco/resources/robots/g1/g1.urdf | 1262 +++++++ .../sim2mujoco/resources/robots/g1/g1.xml | 429 +++ .../sim2mujoco/resources/robots/g1/g1.yaml | 34 + .../resources/robots/g1/g1_gear_wbc.xml | 415 +++ .../resources/robots/g1/g1_gear_wbc.yaml | 40 + .../resources/robots/g1/meshes/head_link.STL | 3 + .../g1/meshes/left_ankle_pitch_link.STL | 3 + .../robots/g1/meshes/left_ankle_roll_link.STL | 3 + .../robots/g1/meshes/left_elbow_link.STL | 3 + .../g1/meshes/left_hand_index_0_link.STL | 3 + .../g1/meshes/left_hand_index_1_link.STL | 3 + .../g1/meshes/left_hand_middle_0_link.STL | 3 + .../g1/meshes/left_hand_middle_1_link.STL | 3 + .../robots/g1/meshes/left_hand_palm_link.STL | 3 + .../g1/meshes/left_hand_thumb_0_link.STL | 3 + .../g1/meshes/left_hand_thumb_1_link.STL | 3 + .../g1/meshes/left_hand_thumb_2_link.STL | 3 + .../robots/g1/meshes/left_hip_pitch_link.STL | 3 + .../robots/g1/meshes/left_hip_roll_link.STL | 3 + .../robots/g1/meshes/left_hip_yaw_link.STL | 3 + .../robots/g1/meshes/left_knee_link.STL | 3 + .../robots/g1/meshes/left_rubber_hand.STL | 3 + .../g1/meshes/left_shoulder_pitch_link.STL | 3 + .../g1/meshes/left_shoulder_roll_link.STL | 3 + .../g1/meshes/left_shoulder_yaw_link.STL | 3 + .../g1/meshes/left_wrist_pitch_link.STL | 3 + .../robots/g1/meshes/left_wrist_roll_link.STL | 3 + .../g1/meshes/left_wrist_roll_rubber_hand.STL | 3 + .../robots/g1/meshes/left_wrist_yaw_link.STL | 3 + .../resources/robots/g1/meshes/logo_link.STL | 3 + .../resources/robots/g1/meshes/pelvis.STL | 3 + .../robots/g1/meshes/pelvis_contour_link.STL | 3 + .../g1/meshes/right_ankle_pitch_link.STL | 3 + .../g1/meshes/right_ankle_roll_link.STL | 3 + .../robots/g1/meshes/right_elbow_link.STL | 3 + .../g1/meshes/right_hand_index_0_link.STL | 3 + .../g1/meshes/right_hand_index_1_link.STL | 3 + .../g1/meshes/right_hand_middle_0_link.STL | 3 + .../g1/meshes/right_hand_middle_1_link.STL | 3 + .../robots/g1/meshes/right_hand_palm_link.STL | 3 + .../g1/meshes/right_hand_thumb_0_link.STL | 3 + .../g1/meshes/right_hand_thumb_1_link.STL | 3 + .../g1/meshes/right_hand_thumb_2_link.STL | 3 + .../robots/g1/meshes/right_hip_pitch_link.STL | 3 + .../robots/g1/meshes/right_hip_roll_link.STL | 3 + .../robots/g1/meshes/right_hip_yaw_link.STL | 3 + .../robots/g1/meshes/right_knee_link.STL | 3 + .../robots/g1/meshes/right_rubber_hand.STL | 3 + .../g1/meshes/right_shoulder_pitch_link.STL | 3 + .../g1/meshes/right_shoulder_roll_link.STL | 3 + .../g1/meshes/right_shoulder_yaw_link.STL | 3 + .../g1/meshes/right_wrist_pitch_link.STL | 3 + .../g1/meshes/right_wrist_roll_link.STL | 3 + .../meshes/right_wrist_roll_rubber_hand.STL | 3 + .../robots/g1/meshes/right_wrist_yaw_link.STL | 3 + .../g1/meshes/torso_constraint_L_link.STL | 3 + .../g1/meshes/torso_constraint_L_rod_link.STL | 3 + .../g1/meshes/torso_constraint_R_link.STL | 3 + .../g1/meshes/torso_constraint_R_rod_link.STL | 3 + .../resources/robots/g1/meshes/torso_link.STL | 3 + .../g1/meshes/torso_link_23dof_rev_1_0.STL | 3 + .../robots/g1/meshes/torso_link_rev_1_0.STL | 3 + .../robots/g1/meshes/waist_constraint_L.STL | 3 + .../robots/g1/meshes/waist_constraint_R.STL | 3 + .../robots/g1/meshes/waist_roll_link.STL | 3 + .../g1/meshes/waist_roll_link_rev_1_0.STL | 3 + .../robots/g1/meshes/waist_support_link.STL | 3 + .../robots/g1/meshes/waist_yaw_link.STL | 3 + .../g1/meshes/waist_yaw_link_rev_1_0.STL | 3 + .../GR00T-WholeBodyControl-Balance.onnx | 3 + .../policy/GR00T-WholeBodyControl-Walk.onnx | 3 + .../g1/policy/NVIDIA Open Model License | 62 + .../sim2mujoco/scripts/run_mujoco_gear_wbc.py | 307 ++ .../scripts/run_mujoco_gear_wbc_gait.py | 303 ++ gr00t_wbc/version.py | 11 + install_scripts/install_leap_sdk.sh | 19 + install_scripts/install_ros.sh | 33 + lint.sh | 45 + pyproject.toml | 179 + scripts/deploy_g1.py | 472 +++ scripts/leap_tracking_example.py | 50 + scripts/run_webcam_recorder.py | 392 +++ tests/conftest.py | 15 + tests/control/__init__.py | 0 tests/control/main/__init__.py | 0 tests/control/main/teleop/__init__.py | 0 .../main/teleop/test_g1_control_loop.py | 465 +++ tests/control/main/test_data_exporter_loop.py | 403 +++ tests/control/policy/__init__.py | 0 .../policy/interpolation_policy/__init__.py | 0 .../test_interpolation_policy.py | 47 + .../test_interpolation_ramp_up.py | 78 + .../interpolation_policy/trajectory.png | 3 + .../interpolation_policy/trajectory_data.npy | Bin 0 -> 142128 bytes tests/control/robot_model/__init__.py | 0 tests/control/robot_model/robot_model_test.py | 911 +++++ tests/control/teleop/__init__.py | 0 .../teleop/test_teleop_retargeting_ik.py | 196 ++ tests/control/visualization/__init__.py | 0 .../test_meshcat_visualizer_env.py | 84 + tests/data/test_exporter.py | 522 +++ .../all_joints_raw_data_replay.pkl | 3 + tests/replay_data/calibration_data.pkl | 3 + tests/replay_data/g1_pnpbottle.parquet | 3 + tests/replay_data/interpolation_data.pkl | 3 + tests/replay_data/sin_wave_arm_replay.pkl | 3 + tests/replay_data/target_joints_data.pkl | 3 + tests/sim/test_sim_data_collection.py | 64 + 951 files changed, 75739 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 docker/.bashrc create mode 100644 docker/.tmux.conf create mode 100644 docker/70-manus-hid.rules create mode 100644 docker/Dockerfile.deploy create mode 100644 docker/Dockerfile.deploy.base create mode 100644 docker/build_deploy_base.sh create mode 100755 docker/entrypoint/bash.sh create mode 100755 docker/entrypoint/deploy.sh create mode 100755 docker/entrypoint/install_deps.sh create mode 100644 docker/image_name.txt create mode 100755 docker/kill_all_containers.sh create mode 100755 docker/kill_gr00t_wbc_processors.sh create mode 100644 docker/publish.sh create mode 100755 docker/run_docker.sh create mode 100644 external_dependencies/unitree_sdk2_python/.gitignore create mode 100644 external_dependencies/unitree_sdk2_python/LICENSE create mode 100644 external_dependencies/unitree_sdk2_python/README zh.md create mode 100644 external_dependencies/unitree_sdk2_python/README.md create mode 100644 external_dependencies/unitree_sdk2_python/example/b2/camera/camera_opencv.py create mode 100644 external_dependencies/unitree_sdk2_python/example/b2/camera/capture_image.py create mode 100644 external_dependencies/unitree_sdk2_python/example/b2/high_level/b2_sport_client.py create mode 100644 external_dependencies/unitree_sdk2_python/example/b2/low_level/b2_stand_example.py create mode 100644 external_dependencies/unitree_sdk2_python/example/b2/low_level/unitree_legged_const.py create mode 100644 external_dependencies/unitree_sdk2_python/example/b2w/camera/camera_opencv.py create mode 100644 external_dependencies/unitree_sdk2_python/example/b2w/camera/capture_image.py create mode 100644 external_dependencies/unitree_sdk2_python/example/b2w/high_level/b2w_sport_client.py create mode 100644 external_dependencies/unitree_sdk2_python/example/b2w/low_level/b2w_stand_example.py create mode 100644 external_dependencies/unitree_sdk2_python/example/b2w/low_level/unitree_legged_const.py create mode 100644 external_dependencies/unitree_sdk2_python/example/g1/audio/g1_audio_client_example.py create mode 100644 external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_arm5_sdk_dds_example.py create mode 100644 external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_arm7_sdk_dds_example.py create mode 100644 external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_loco_client_example.py create mode 100644 external_dependencies/unitree_sdk2_python/example/g1/low_level/g1_low_level_example.py create mode 100644 external_dependencies/unitree_sdk2_python/example/g1/low_level/g1_move_hands_example.py create mode 100644 external_dependencies/unitree_sdk2_python/example/g1/readme.md create mode 100644 external_dependencies/unitree_sdk2_python/example/go2/front_camera/camera_opencv.py create mode 100644 external_dependencies/unitree_sdk2_python/example/go2/front_camera/capture_image.py create mode 100644 external_dependencies/unitree_sdk2_python/example/go2/high_level/go2_sport_client.py create mode 100644 external_dependencies/unitree_sdk2_python/example/go2/high_level/go2_utlidar_switch.py create mode 100644 external_dependencies/unitree_sdk2_python/example/go2/low_level/go2_stand_example.py create mode 100644 external_dependencies/unitree_sdk2_python/example/go2/low_level/unitree_legged_const.py create mode 100644 external_dependencies/unitree_sdk2_python/example/go2w/high_level/go2w_sport_client.py create mode 100644 external_dependencies/unitree_sdk2_python/example/go2w/low_level/go2w_stand_example.py create mode 100644 external_dependencies/unitree_sdk2_python/example/go2w/low_level/unitree_legged_const.py create mode 100644 external_dependencies/unitree_sdk2_python/example/h1/high_level/h1_loco_client_example.py create mode 100644 external_dependencies/unitree_sdk2_python/example/h1/low_level/h1_low_level_example.py create mode 100644 external_dependencies/unitree_sdk2_python/example/h1/low_level/unitree_legged_const.py create mode 100644 external_dependencies/unitree_sdk2_python/example/h1_2/low_level/h1_2_low_level_example.py create mode 100644 external_dependencies/unitree_sdk2_python/example/helloworld/publisher.py create mode 100644 external_dependencies/unitree_sdk2_python/example/helloworld/subscriber.py create mode 100644 external_dependencies/unitree_sdk2_python/example/helloworld/user_data.py create mode 100644 external_dependencies/unitree_sdk2_python/example/motionSwitcher/motion_switcher_example.py create mode 100644 external_dependencies/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_move.py create mode 100644 external_dependencies/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_switch.py create mode 100644 external_dependencies/unitree_sdk2_python/example/vui_client/vui_client_example.py create mode 100644 external_dependencies/unitree_sdk2_python/example/wireless_controller/wireless_controller.py create mode 100644 external_dependencies/unitree_sdk2_python/pyproject.toml create mode 100644 external_dependencies/unitree_sdk2_python/setup.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_api.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_api.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_api.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_api.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_api.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_api.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel_config.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel_name.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_api.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_api.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_api.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_api.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_api.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/video_api.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/video_client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/vui/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_api.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/h1/loco/h1_loco_api.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/h1/loco/h1_loco_client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/dds_/_Time_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/dds_/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/default.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Point32_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PointStamped_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Point_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Pose2D_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseStamped_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseWithCovarianceStamped_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseWithCovariance_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Pose_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_QuaternionStamped_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Quaternion_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistStamped_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistWithCovarianceStamped_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistWithCovariance_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Twist_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Vector3_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_MapMetaData_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_OccupancyGrid_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_Odometry_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/PointField_Constants/_PointField_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/PointField_Constants/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/_PointCloud2_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/_PointField_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/_Header_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/_String_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestHeader_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestIdentity_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestLease_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestPolicy_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_Request_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_ResponseHeader_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_ResponseStatus_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_Response_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_AudioData_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_BmsCmd_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_BmsState_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Error_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Go2FrontVideoData_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_HeightMap_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_IMUState_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_InterfaceConfig_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LidarState_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LowCmd_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LowState_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorCmd_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorCmds_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorState_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorStates_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_PathPoint_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Req_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Res_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_SportModeState_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_TimeSpec_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_UwbState_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_UwbSwitch_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_WirelessController_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/.idlpy_manifest create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/.idlpy_manifest create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/.idlpy_manifest create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_BmsCmd_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_BmsState_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_HandCmd_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_HandState_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_IMUState_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_LowCmd_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_LowState_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MainBoardState_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MotorCmd_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MotorState_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_OdoState_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_PressSensorState_.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/client_base.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/client_stub.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/internal.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/lease_client.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/lease_server.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/request_future.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/server.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/server_base.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/server_stub.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/obstacles_avoid_client_example.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/robot_service_client_example.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/sport_client_example.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/video_client_example.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/vui_client_example.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/crc/test_crc.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/helloworld/helloworld.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/helloworld/publisher.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/helloworld/subscriber.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/lowlevel_control.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/read_lowstate.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/sub_lowstate.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/unitree_go2_const.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_api.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_client_example.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_server_example.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/__init__.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/bqueue.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/clib_lookup.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/crc.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/future.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/hz_sample.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/joystick.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/lib/crc_aarch64.so create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/lib/crc_amd64.so create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/singleton.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/thread.py create mode 100644 external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/timerfd.py create mode 100644 gr00t_wbc/__init__.py create mode 100644 gr00t_wbc/control/__init__.py create mode 100644 gr00t_wbc/control/base/__init__.py create mode 100644 gr00t_wbc/control/base/env.py create mode 100644 gr00t_wbc/control/base/humanoid_env.py create mode 100755 gr00t_wbc/control/base/policy.py create mode 100644 gr00t_wbc/control/base/sensor.py create mode 100644 gr00t_wbc/control/envs/__init__.py create mode 100644 gr00t_wbc/control/envs/g1/__init__.py create mode 100644 gr00t_wbc/control/envs/g1/g1_body.py create mode 100644 gr00t_wbc/control/envs/g1/g1_env.py create mode 100644 gr00t_wbc/control/envs/g1/g1_hand.py create mode 100644 gr00t_wbc/control/envs/g1/sim/__init__.py create mode 100644 gr00t_wbc/control/envs/g1/sim/base_sim.py create mode 100644 gr00t_wbc/control/envs/g1/sim/image_publish_utils.py create mode 100644 gr00t_wbc/control/envs/g1/sim/metric_utils.py create mode 100644 gr00t_wbc/control/envs/g1/sim/robocasa_sim.py create mode 100644 gr00t_wbc/control/envs/g1/sim/sim_utilts.py create mode 100644 gr00t_wbc/control/envs/g1/sim/simulator_factory.py create mode 100644 gr00t_wbc/control/envs/g1/sim/unitree_sdk2py_bridge.py create mode 100644 gr00t_wbc/control/envs/g1/utils/__init__.py create mode 100644 gr00t_wbc/control/envs/g1/utils/command_sender.py create mode 100644 gr00t_wbc/control/envs/g1/utils/joint_safety.py create mode 100644 gr00t_wbc/control/envs/g1/utils/state_processor.py create mode 100644 gr00t_wbc/control/envs/robocasa/__init__.py create mode 100644 gr00t_wbc/control/envs/robocasa/async_env_server.py create mode 100644 gr00t_wbc/control/envs/robocasa/sync_env.py create mode 100644 gr00t_wbc/control/envs/robocasa/utils/__init__.py create mode 100644 gr00t_wbc/control/envs/robocasa/utils/cam_key_converter.py create mode 100644 gr00t_wbc/control/envs/robocasa/utils/controller_utils.py create mode 100644 gr00t_wbc/control/envs/robocasa/utils/robocasa_env.py create mode 100644 gr00t_wbc/control/envs/robocasa/utils/robot_key_converter.py create mode 100644 gr00t_wbc/control/envs/robocasa/utils/sim_utils.py create mode 100644 gr00t_wbc/control/main/__init__.py create mode 100644 gr00t_wbc/control/main/config_template.py create mode 100644 gr00t_wbc/control/main/constants.py create mode 100644 gr00t_wbc/control/main/teleop/__init__.py create mode 100644 gr00t_wbc/control/main/teleop/configs/configs.py create mode 100644 gr00t_wbc/control/main/teleop/configs/g1_29dof_gear_wbc.yaml create mode 100644 gr00t_wbc/control/main/teleop/configs/g1_gear_wbc.yaml create mode 100644 gr00t_wbc/control/main/teleop/configs/identifiers.py create mode 100644 gr00t_wbc/control/main/teleop/playback_sync_sim_data.py create mode 100644 gr00t_wbc/control/main/teleop/run_camera_viewer.py create mode 100644 gr00t_wbc/control/main/teleop/run_g1_control_loop.py create mode 100644 gr00t_wbc/control/main/teleop/run_g1_data_exporter.py create mode 100644 gr00t_wbc/control/main/teleop/run_navigation_policy_loop.py create mode 100644 gr00t_wbc/control/main/teleop/run_sim_loop.py create mode 100644 gr00t_wbc/control/main/teleop/run_sync_sim_data_collection.py create mode 100644 gr00t_wbc/control/main/teleop/run_teleop_policy_loop.py create mode 100644 gr00t_wbc/control/policy/__init__.py create mode 100644 gr00t_wbc/control/policy/g1_decoupled_whole_body_policy.py create mode 100644 gr00t_wbc/control/policy/g1_gear_wbc_policy.py create mode 100644 gr00t_wbc/control/policy/identity_policy.py create mode 100644 gr00t_wbc/control/policy/interpolation_policy.py create mode 100644 gr00t_wbc/control/policy/keyboard_navigation_policy.py create mode 100644 gr00t_wbc/control/policy/lerobot_replay_policy.py create mode 100644 gr00t_wbc/control/policy/teleop_policy.py create mode 100644 gr00t_wbc/control/policy/wbc_policy_factory.py create mode 100644 gr00t_wbc/control/robot_model/__init__.py create mode 100644 gr00t_wbc/control/robot_model/instantiation/__init__.py create mode 100644 gr00t_wbc/control/robot_model/instantiation/g1.py create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/g1_29dof.urdf create mode 100755 gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_old.xml create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_with_hand.urdf create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_with_hand.xml create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_with_hand_rev_1_0_activatedfinger.xml create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/lift_box_43dof.xml create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/head_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_ankle_pitch_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_ankle_roll_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_elbow_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_elbow_link_merge.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_index_0_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_index_1_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_middle_0_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_middle_1_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_palm_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_thumb_0_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_thumb_1_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_thumb_2_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hip_pitch_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hip_roll_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hip_yaw_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_knee_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_rubber_hand.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_shoulder_pitch_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_shoulder_roll_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_shoulder_yaw_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_pitch_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_roll_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_roll_rubber_hand.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_yaw_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/logo_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/pelvis.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/pelvis_contour_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_ankle_pitch_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_ankle_roll_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_elbow_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_elbow_link_merge.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_index_0_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_index_1_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_middle_0_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_middle_1_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_palm_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_thumb_0_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_thumb_1_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_thumb_2_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hip_pitch_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hip_roll_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hip_yaw_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_knee_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_rubber_hand.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_shoulder_pitch_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_shoulder_roll_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_shoulder_yaw_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_pitch_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_roll_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_roll_rubber_hand.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_yaw_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_L_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_L_rod_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_R_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_R_rod_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_link_rev_1_0.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_constraint_L.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_constraint_R.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_roll_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_roll_link_rev_1_0.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_support_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_yaw_link.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_yaw_link_rev_1_0.STL create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/pnp_bottle_43dof.xml create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/pnp_cube_43dof.xml create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/scene_29dof.xml create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/scene_29dof_activated3dex.xml create mode 100644 gr00t_wbc/control/robot_model/model_data/g1/scene_43dof.xml create mode 100644 gr00t_wbc/control/robot_model/robot_model.py create mode 100644 gr00t_wbc/control/robot_model/supplemental_info/__init__.py create mode 100644 gr00t_wbc/control/robot_model/supplemental_info/g1/__init__.py create mode 100644 gr00t_wbc/control/robot_model/supplemental_info/g1/g1_supplemental_info.py create mode 100644 gr00t_wbc/control/robot_model/supplemental_info/robot_supplemental_info.py create mode 100644 gr00t_wbc/control/sensor/__init__.py create mode 100644 gr00t_wbc/control/sensor/composed_camera.py create mode 100644 gr00t_wbc/control/sensor/oak.py create mode 100644 gr00t_wbc/control/sensor/sensor_server.py create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientLogging.hpp create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientPlatformSpecific.cpp create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientPlatformSpecific.hpp create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientPlatformSpecificTypes.hpp create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/Dockerfile create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/Dockerfile.Integrated create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/Main.cpp create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/Makefile create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/include/ManusSDK.h create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/include/ManusSDKTypeInitializers.h create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/include/ManusSDKTypes.h create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/lib/libManusSDK.so create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/lib/libManusSDK_Integrated.so create mode 100755 gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusServer.cpython-310-x86_64-linux-gnu.so create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/Readme.md create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient.cpp create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient.hpp create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient_Linux.code-workspace create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient_Linux.vcxproj create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient_Linux.vcxproj.filters create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/ClientPlatformSpecific.d create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/ClientPlatformSpecific.o create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/Main.d create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/Main.o create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/SDKClient.d create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/SDKClient.o create mode 100644 gr00t_wbc/control/teleop/device/SDKClient_Linux/test.py create mode 100644 gr00t_wbc/control/teleop/device/iphone/iphone.py create mode 100644 gr00t_wbc/control/teleop/device/manus.py create mode 100644 gr00t_wbc/control/teleop/device/pico/XRoboToolkit_PC_Service_1.0.0_ubuntu_22.04_amd64.deb create mode 100644 gr00t_wbc/control/teleop/device/pico/roboticsservice_1.0.0.0_arm64.deb create mode 100644 gr00t_wbc/control/teleop/device/pico/xr_client.py create mode 100644 gr00t_wbc/control/teleop/gui/cli.py create mode 100644 gr00t_wbc/control/teleop/gui/core/events3d.py create mode 100644 gr00t_wbc/control/teleop/gui/core/gui3d.py create mode 100644 gr00t_wbc/control/teleop/gui/core/guicommon.py create mode 100644 gr00t_wbc/control/teleop/gui/core/module3d.py create mode 100644 gr00t_wbc/control/teleop/gui/core/selection.py create mode 100755 gr00t_wbc/control/teleop/gui/library/getpath.py create mode 100644 gr00t_wbc/control/teleop/gui/library/image.py create mode 100644 gr00t_wbc/control/teleop/gui/library/language.py create mode 100644 gr00t_wbc/control/teleop/gui/library/log.py create mode 100644 gr00t_wbc/control/teleop/gui/library/matrix.py create mode 100644 gr00t_wbc/control/teleop/gui/library/mh.py create mode 100644 gr00t_wbc/control/teleop/gui/library/profiler.py create mode 100644 gr00t_wbc/control/teleop/gui/library/qtgui.py create mode 100644 gr00t_wbc/control/teleop/gui/library/universal.py create mode 100644 gr00t_wbc/control/teleop/gui/library/xdg_parser.py create mode 100644 gr00t_wbc/control/teleop/gui/main.py create mode 100644 gr00t_wbc/control/teleop/main/test_iphone.py create mode 100644 gr00t_wbc/control/teleop/main/test_manus.py create mode 100644 gr00t_wbc/control/teleop/main/test_vive.py create mode 100644 gr00t_wbc/control/teleop/pre_processor/fingers/fingers.py create mode 100644 gr00t_wbc/control/teleop/pre_processor/pre_processor.py create mode 100644 gr00t_wbc/control/teleop/pre_processor/wrists/wrists.py create mode 100644 gr00t_wbc/control/teleop/solver/body/body_ik_solver.py create mode 100644 gr00t_wbc/control/teleop/solver/body/body_ik_solver_settings.py create mode 100644 gr00t_wbc/control/teleop/solver/hand/g1_gripper_ik_solver.py create mode 100644 gr00t_wbc/control/teleop/solver/hand/instantiation/g1_hand_ik_instantiation.py create mode 100644 gr00t_wbc/control/teleop/solver/solver.py create mode 100644 gr00t_wbc/control/teleop/streamers/base_streamer.py create mode 100644 gr00t_wbc/control/teleop/streamers/dummy_streamer.py create mode 100644 gr00t_wbc/control/teleop/streamers/iphone_streamer.py create mode 100644 gr00t_wbc/control/teleop/streamers/joycon_streamer.py create mode 100644 gr00t_wbc/control/teleop/streamers/leapmotion_streamer.py create mode 100644 gr00t_wbc/control/teleop/streamers/manus_streamer.py create mode 100644 gr00t_wbc/control/teleop/streamers/pico_streamer.py create mode 100644 gr00t_wbc/control/teleop/streamers/vive_streamer.py create mode 100644 gr00t_wbc/control/teleop/teleop_retargeting_ik.py create mode 100644 gr00t_wbc/control/teleop/teleop_streamer.py create mode 100644 gr00t_wbc/control/utils/__init__.py create mode 100644 gr00t_wbc/control/utils/cv_bridge.py create mode 100644 gr00t_wbc/control/utils/episode_state.py create mode 100644 gr00t_wbc/control/utils/gear_wbc_utils.py create mode 100644 gr00t_wbc/control/utils/img_viewer.py create mode 100644 gr00t_wbc/control/utils/keyboard_dispatcher.py create mode 100644 gr00t_wbc/control/utils/logging_utils.py create mode 100644 gr00t_wbc/control/utils/n1_utils.py create mode 100644 gr00t_wbc/control/utils/network_utils.py create mode 100644 gr00t_wbc/control/utils/ros_utils.py create mode 100755 gr00t_wbc/control/utils/run_real_checklist.py create mode 100644 gr00t_wbc/control/utils/service.py create mode 100644 gr00t_wbc/control/utils/sync_sim_utils.py create mode 100644 gr00t_wbc/control/utils/telemetry.py create mode 100644 gr00t_wbc/control/utils/term_color_constants.py create mode 100644 gr00t_wbc/control/utils/text_to_speech.py create mode 100644 gr00t_wbc/control/visualization/humanoid_visualizer.py create mode 100644 gr00t_wbc/control/visualization/meshcat_visualizer_env.py create mode 100644 gr00t_wbc/data/constants.py create mode 100644 gr00t_wbc/data/exporter.py create mode 100644 gr00t_wbc/data/utils.py create mode 100644 gr00t_wbc/data/video_writer.py create mode 100644 gr00t_wbc/data/viz/rerun_viz.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/requirements.txt create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/environments/locomanipulation/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/environments/locomanipulation/base.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/environments/locomanipulation/locomanip.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/environments/locomanipulation/locomanip_basic.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/environments/locomanipulation/locomanip_pnp.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/examples/third_party_controller/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/examples/third_party_controller/default_mink_ik_g1_gear_wbc.json create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/examples/third_party_controller/default_mink_ik_g1_gear_wbc_gc.json create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/macros.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/gear_factory.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/meshes/gear_factory_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/meshes/gear_factory_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/meshes/gear_factory_10.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/meshes/gear_factory_11.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/meshes/gear_factory_2.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/meshes/gear_factory_3.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/meshes/gear_factory_4.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/meshes/gear_factory_5.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/meshes/gear_factory_6.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/meshes/gear_factory_7.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/meshes/gear_factory_8.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/meshes/gear_factory_9.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/meshes/gear_factory_poles.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/meshes/gear_factory_walls.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/meshes/paint_lines.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/textures/Fence_Metal_AO.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/textures/Laser_Beam.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/textures/Light_Curtain.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/textures/Rack_3@Medium.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/textures/Rack_4@Medium.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/textures/Robots_1@Medium.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/textures/Robots_1_Fence@Medium.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/textures/Robots_2@Medium.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/textures/Robots_2_Fence@Medium.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/textures/Steel_ASTM_AO.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/textures/T_Floor_A1_Albedo.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/textures/factory@1024.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_factory/textures/mtl-base_color@Small.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_lab/gear_lab.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_lab/textures/floor.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/gear_lab/textures/wall.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/arenas/ground_arena.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/apple_0/collision/tmpm_h04v7s_collision_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/apple_0/collision/tmpm_h04v7s_collision_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/apple_0/collision/tmpm_h04v7s_collision_2.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/apple_0/collision/tmpm_h04v7s_collision_3.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/apple_0/collision/tmpm_h04v7s_collision_4.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/apple_0/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/apple_0/visual/image0.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/apple_0/visual/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/apple_0/visual/model_normalized_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_a1/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_a1/meshes/cardbox_a1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_a1/model.xml create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_a1/textures/T_Cardbox_A1_Albedo.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_b1/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_b1/meshes/cardbox_b1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_b1/model.xml create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_b1/textures/T_Cardbox_B3_Albedo.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_c1/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_c1/meshes/cardbox_c1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_c1/model.xml create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_c1/textures/T_Cardbox_C2_Albedo.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_d1/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_d1/meshes/cardbox_d1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_d1/model.xml create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/cardbox_d1/textures/T_Cardbox_D2_Albedo.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_2.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_3.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_4.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_5.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_6.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_collision_0_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_collision_0_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_collision_0_10.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_collision_0_11.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_collision_0_2.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_collision_0_3.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_collision_0_4.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_collision_0_5.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_collision_0_6.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_collision_0_7.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_collision_0_8.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/meshes/clock_0_0_collision_0_9.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/textures/chasovaya_baseColor.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/textures/ciferblat_baseColor.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/textures/hrom_baseColor.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/textures/korpus_baseColor.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/clock_0/textures/minutnaya_baseColor.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_box/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_box/meshes/control_box_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_box/meshes/control_box_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_box/meshes/control_box_2.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_box/meshes/control_box_3.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_box/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_box/textures/Switch_Box_Rack_AO.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_conveyorbelt_a08/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_conveyorbelt_a08/meshes/ControlPanel_AO.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_conveyorbelt_a08/meshes/control_conveyorbelt_a08_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_conveyorbelt_a08/meshes/control_conveyorbelt_a08_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_conveyorbelt_a08/meshes/control_conveyorbelt_a08_4.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_conveyorbelt_a08/meshes/control_conveyorbelt_a08_body.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_conveyorbelt_a08/meshes/control_conveyorbelt_a08_body.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_conveyorbelt_a08/meshes/control_conveyorbelt_a08_button.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_conveyorbelt_a08/meshes/control_conveyorbelt_a08_collision_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_conveyorbelt_a08/meshes/control_conveyorbelt_a08_collision_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_conveyorbelt_a08/meshes/control_conveyorbelt_a08_collision_2.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_conveyorbelt_a08/meshes/control_conveyorbelt_a08_lever.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_conveyorbelt_a08/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/control_conveyorbelt_a08/textures/ControlPanel_AO.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/dock_board_a12/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/dock_board_a12/meshes/dock_board_a12_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/dock_board_a12/meshes/dock_board_a12_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/dock_board_a12/meshes/dock_board_a12_2.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/dock_board_a12/meshes/dock_board_a12_convex_border_l.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/dock_board_a12/meshes/dock_board_a12_convex_border_r.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/dock_board_a12/meshes/dock_board_a12_convex_platform.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/dock_board_a12/model.xml create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/dock_board_a12/textures/T_Aluminium_Brushed_A1_Albedo.png create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/dock_board_a12/textures/T_Plastic_Orange_B_Albedo.png create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/dock_board_a12/textures/t_metal_circles_grid_grey_a_Albedo.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/factory_ergo_table/meshes/ergo_table_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/factory_ergo_table/meshes/ergo_table_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/factory_ergo_table/meshes/ergo_table_2.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/factory_ergo_table/meshes/ergo_table_3.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/factory_ergo_table/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/factory_ergo_table/textures/Rubber_Smooth_BaseColor.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/jug_a01/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/jug_a01/meshes/jug_a01.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/jug_a01/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/jug_a01/textures/Jug_A.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/lab_shelf/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/lab_shelf_board/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/lab_shelf_cabinet/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/lab_table/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/lab_table/meshes/lab_table.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/lab_table/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/lab_table/textures/Image_0.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/longbox_a08/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/longbox_a08/meshes/longbox_a08_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/longbox_a08/meshes/longbox_a08_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/longbox_a08/model.xml create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/longbox_a08/textures/T_LongBox_A01_Tile_Albedo.png create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/longbox_a08/textures/T_LongBox_A01_Trim_Albedo.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/longbox_a09/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/longbox_a09/meshes/longbox_a09_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/longbox_a09/meshes/longbox_a09_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/longbox_a09/model.xml create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/longbox_a09/textures/T_LongBox_A01_Tile_Albedo.png create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/longbox_a09/textures/T_LongBox_A01_Trim_Albedo.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/mobile_shelving_cart/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/mobile_shelving_cart/meshes/mobile_shelving_cart_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/mobile_shelving_cart/meshes/mobile_shelving_cart_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/mobile_shelving_cart/meshes/mobile_shelving_cart_2.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/mobile_shelving_cart/meshes/mobile_shelving_cart_3.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/mobile_shelving_cart/meshes/mobile_shelving_cart_4.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/mobile_shelving_cart/meshes/mobile_shelving_cart_wheel_disk.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/mobile_shelving_cart/meshes/mobile_shelving_cart_wheel_hinge.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/mobile_shelving_cart/meshes/mobile_shelving_cart_wheel_rubber.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/mobile_shelving_cart/model.xml create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/mobile_shelving_cart/textures/T_Aluminium_Brushed_A1_Albedo.png create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/mobile_shelving_cart/textures/T_Plastic_Blue_A_Albedo.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/pallet_b1/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/pallet_b1/meshes/pallet_b1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/pallet_b1/model.xml create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/pallet_b1/textures/T_Pallet_A2_Albedo.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/pallet_c1/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/pallet_c1/meshes/pallet_c1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/pallet_c1/model.xml create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/pallet_c1/textures/T_Pallet_A3_Albedo.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/plate_1/collision/tmpzdovka8d_collision_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/plate_1/collision/tmpzdovka8d_collision_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/plate_1/collision/tmpzdovka8d_collision_2.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/plate_1/collision/tmpzdovka8d_collision_3.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/plate_1/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/plate_1/visual/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/plate_1/visual/model_normalized_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/rubix_cube_1/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/rubix_cube_1/meshes/rubix_cube_1_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/rubix_cube_1/meshes/rubix_cube_1_0_collision_0_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/rubix_cube_1/meshes/rubix_cube_1_0_collision_0_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/rubix_cube_1/meshes/rubix_cube_1_0_collision_0_2.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/rubix_cube_1/meshes/rubix_cube_1_0_collision_0_3.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/rubix_cube_1/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/rubix_cube_1/textures/Material.002_baseColor.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/shelf_a12/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/shelf_a12/meshes/shelf_a12_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/shelf_a12/meshes/shelf_a12_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/shelf_a12/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/shelf_a12/textures/Shelf_A12_Poles.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/shelf_a12/textures/Shelf_A12_Shelves.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/target_zone/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/target_zone/textures/Target_Zone.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/tote_f01/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/tote_f01/meshes/tote_f01_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/tote_f01/meshes/tote_f01_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/tote_f01/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/tote_f02/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/tote_f02/meshes/tote_f02_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/tote_f02/meshes/tote_f02_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/tote_f02/model.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/workshop_trolley_a01/material.mtl create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/workshop_trolley_a01/meshes/wheel_disk.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/workshop_trolley_a01/meshes/wheel_hinge.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/workshop_trolley_a01/meshes/wheel_rubber.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/workshop_trolley_a01/meshes/workshop_trolley_a01_0.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/workshop_trolley_a01/meshes/workshop_trolley_a01_1.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/workshop_trolley_a01/meshes/workshop_trolley_a01_2.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/workshop_trolley_a01/meshes/workshop_trolley_a01_3.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/workshop_trolley_a01/meshes/workshop_trolley_a01_4.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/workshop_trolley_a01/meshes/workshop_trolley_a01_5.obj create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/workshop_trolley_a01/model.xml create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/objects/omniverse/locomanip/workshop_trolley_a01/textures/T_Plastic_Blue_A_Albedo.png create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/g1_29dof_rev_1_0.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/g1_threefinger_left_hand.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/g1_threefinger_right_hand.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/head_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_ankle_pitch_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_ankle_roll_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_elbow_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_elbow_link_merge.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_hand_index_0_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_hand_index_1_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_hand_middle_0_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_hand_middle_1_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_hand_palm_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_hand_thumb_0_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_hand_thumb_1_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_hand_thumb_2_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_hip_pitch_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_hip_roll_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_hip_yaw_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_knee_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_rubber_hand.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_shoulder_pitch_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_shoulder_roll_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_shoulder_yaw_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_wrist_pitch_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_wrist_roll_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_wrist_roll_rubber_hand.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/left_wrist_yaw_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/logo_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/pelvis.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/pelvis_contour_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_ankle_pitch_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_ankle_roll_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_elbow_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_elbow_link_merge.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_hand_index_0_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_hand_index_1_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_hand_middle_0_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_hand_middle_1_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_hand_palm_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_hand_thumb_0_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_hand_thumb_1_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_hand_thumb_2_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_hip_pitch_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_hip_roll_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_hip_yaw_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_knee_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_rubber_hand.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_shoulder_pitch_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_shoulder_roll_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_shoulder_yaw_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_wrist_pitch_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_wrist_roll_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_wrist_roll_rubber_hand.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/right_wrist_yaw_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/torso_constraint_L_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/torso_constraint_L_rod_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/torso_constraint_R_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/torso_constraint_R_rod_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/torso_link.STL create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/torso_link_rev_1_0.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/waist_constraint_L.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/waist_constraint_R.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/waist_roll_link.STL create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/waist_roll_link_rev_1_0.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/waist_support_link.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/waist_yaw_link.STL create mode 100755 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/assets/robots/unitree_g1/meshes/waist_yaw_link_rev_1_0.STL create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/grippers/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/grippers/g1_threefinger_hands.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite/bin.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite/box_pattern_object.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite/lid.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite/needle.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite/pot_with_handles.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite/ring_tripod.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite_body/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite_body/bin_with_handles.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite_body/coffee_machine.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite_body/cup.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite_body/inverse_stacked_cylinder.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite_body/lightbulb.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite_body/sliding_box.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite_body/socket.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite_body/spray_bottle.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite_body/stacked_box.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite_body/stacked_cylinder.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/composite_body/stove_plug.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/objects.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/objects/xml_objects.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/robots/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/robots/manipulators/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/robots/manipulators/g1_robot.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/scenes/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/scenes/factory_arena.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/scenes/ground_arena.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/models/scenes/lab_arena.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/camera_utils.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/config_utils.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/cotrain_utils.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/dataset_registry.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/dexmg_utils.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/env_utils.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/gym_utils/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/gym_utils/gymnasium_basic.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/model_zoo/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/model_zoo/file_utils.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/model_zoo/fixture_template.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/model_zoo/install_vhacd.sh create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/model_zoo/log_utils.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/model_zoo/mjcf_gen_utils.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/model_zoo/mjcf_obj.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/model_zoo/mtl_utils.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/model_zoo/object_play_env.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/model_zoo/object_template.xml create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/model_zoo/parser_utils.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/object_utils.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/placement_samplers.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/postprocess_xml_utils.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/scene/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/scene/configs.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/scene/scene.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/scene/success_criteria.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/texture_swap.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/transform_utils.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/usd/README.md create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/usd/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/usd/component.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/usd/demo.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/usd/exporter.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/usd/exporter_test.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/usd/shapes.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/usd/utils.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/utils/visuals_utls.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/wrappers/__init__.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/robocasa/wrappers/ik_wrapper.py create mode 100644 gr00t_wbc/dexmg/gr00trobocasa/setup.py create mode 100644 gr00t_wbc/sim2mujoco/.gitattributes create mode 100644 gr00t_wbc/sim2mujoco/.gitignore create mode 100644 gr00t_wbc/sim2mujoco/README.md create mode 100644 gr00t_wbc/sim2mujoco/requirements.txt create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/README.md create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/g1.urdf create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/g1.xml create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/g1.yaml create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/g1_gear_wbc.xml create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/g1_gear_wbc.yaml create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/head_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_ankle_pitch_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_ankle_roll_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_elbow_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_hand_index_0_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_hand_index_1_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_hand_middle_0_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_hand_middle_1_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_hand_palm_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_hand_thumb_0_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_hand_thumb_1_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_hand_thumb_2_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_hip_pitch_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_hip_roll_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_hip_yaw_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_knee_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_rubber_hand.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_shoulder_pitch_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_shoulder_roll_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_shoulder_yaw_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_wrist_pitch_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_wrist_roll_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_wrist_roll_rubber_hand.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/left_wrist_yaw_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/logo_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/pelvis.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/pelvis_contour_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_ankle_pitch_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_ankle_roll_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_elbow_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_hand_index_0_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_hand_index_1_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_hand_middle_0_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_hand_middle_1_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_hand_palm_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_hand_thumb_0_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_hand_thumb_1_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_hand_thumb_2_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_hip_pitch_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_hip_roll_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_hip_yaw_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_knee_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_rubber_hand.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_shoulder_pitch_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_shoulder_roll_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_shoulder_yaw_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_wrist_pitch_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_wrist_roll_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_wrist_roll_rubber_hand.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/right_wrist_yaw_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/torso_constraint_L_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/torso_constraint_L_rod_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/torso_constraint_R_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/torso_constraint_R_rod_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/torso_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/torso_link_23dof_rev_1_0.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/torso_link_rev_1_0.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/waist_constraint_L.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/waist_constraint_R.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/waist_roll_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/waist_roll_link_rev_1_0.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/waist_support_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/waist_yaw_link.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/meshes/waist_yaw_link_rev_1_0.STL create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/policy/GR00T-WholeBodyControl-Balance.onnx create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/policy/GR00T-WholeBodyControl-Walk.onnx create mode 100644 gr00t_wbc/sim2mujoco/resources/robots/g1/policy/NVIDIA Open Model License create mode 100644 gr00t_wbc/sim2mujoco/scripts/run_mujoco_gear_wbc.py create mode 100644 gr00t_wbc/sim2mujoco/scripts/run_mujoco_gear_wbc_gait.py create mode 100644 gr00t_wbc/version.py create mode 100644 install_scripts/install_leap_sdk.sh create mode 100644 install_scripts/install_ros.sh create mode 100755 lint.sh create mode 100644 pyproject.toml create mode 100644 scripts/deploy_g1.py create mode 100644 scripts/leap_tracking_example.py create mode 100755 scripts/run_webcam_recorder.py create mode 100644 tests/conftest.py create mode 100644 tests/control/__init__.py create mode 100644 tests/control/main/__init__.py create mode 100644 tests/control/main/teleop/__init__.py create mode 100644 tests/control/main/teleop/test_g1_control_loop.py create mode 100644 tests/control/main/test_data_exporter_loop.py create mode 100644 tests/control/policy/__init__.py create mode 100644 tests/control/policy/interpolation_policy/__init__.py create mode 100644 tests/control/policy/interpolation_policy/test_interpolation_policy.py create mode 100644 tests/control/policy/interpolation_policy/test_interpolation_ramp_up.py create mode 100644 tests/control/policy/interpolation_policy/trajectory.png create mode 100644 tests/control/policy/interpolation_policy/trajectory_data.npy create mode 100644 tests/control/robot_model/__init__.py create mode 100644 tests/control/robot_model/robot_model_test.py create mode 100644 tests/control/teleop/__init__.py create mode 100644 tests/control/teleop/test_teleop_retargeting_ik.py create mode 100644 tests/control/visualization/__init__.py create mode 100644 tests/control/visualization/test_meshcat_visualizer_env.py create mode 100644 tests/data/test_exporter.py create mode 100644 tests/replay_data/all_joints_raw_data_replay.pkl create mode 100644 tests/replay_data/calibration_data.pkl create mode 100644 tests/replay_data/g1_pnpbottle.parquet create mode 100644 tests/replay_data/interpolation_data.pkl create mode 100644 tests/replay_data/sin_wave_arm_replay.pkl create mode 100644 tests/replay_data/target_joints_data.pkl create mode 100644 tests/sim/test_sim_data_collection.py diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..264e058 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,23 @@ +# Machine learning models and data files +*.pt filter=lfs diff=lfs merge=lfs -text +*.onnx filter=lfs diff=lfs merge=lfs -text +*.pkl filter=lfs diff=lfs merge=lfs -text +# 3D assets and models +*.usd filter=lfs diff=lfs merge=lfs -text +*.usda filter=lfs diff=lfs merge=lfs -text +*.STL filter=lfs diff=lfs merge=lfs -text +*.stl filter=lfs diff=lfs merge=lfs -text +# Shared libraries +*.a filter=lfs diff=lfs merge=lfs -text +*.so* filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.gif filter=lfs diff=lfs merge=lfs -text +*.safetensors filter=lfs diff=lfs merge=lfs -text +*.deb filter=lfs diff=lfs merge=lfs -text +# Collect demo in sim +*.hdf5 filter=lfs diff=lfs merge=lfs -text +*.parquet filter=lfs diff=lfs merge=lfs -text +*.obj filter=lfs diff=lfs merge=lfs -text +*.dae filter=lfs diff=lfs merge=lfs -text +*.so filter=lfs diff=lfs merge=lfs -text +*.so.* filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..920382b --- /dev/null +++ b/.gitignore @@ -0,0 +1,175 @@ + # Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions + + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# IDE +.idea/ +.vscode/ + +# log +outputs/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +outputs/ + +.DS_Store + + +# Mujoco +MUJOCO_LOG.TXT + +# IsaacDeploy +external_dependencies/isaac_teleop_app/isaac-deploy + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7f9b118 --- /dev/null +++ b/LICENSE @@ -0,0 +1,38 @@ +NVIDIA License + +1. Definitions + +“Licensor” means any person or entity that distributes its Work. +“Work” means (a) the original work of authorship made available under this license, which may include software, documentation, or other files, and (b) any additions to or derivative works thereof that are made available under this license. +The terms “reproduce,” “reproduction,” “derivative works,” and “distribution” have the meaning as provided under U.S. copyright law; provided, however, that for the purposes of this license, derivative works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work. +Works are “made available” under this license by including in or with the Work either (a) a copyright notice referencing the applicability of this license to the Work, or (b) a copy of this license. + +2. License Grant + +2.1 Copyright Grant. Subject to the terms and conditions of this license, each Licensor grants to you a perpetual, worldwide, non-exclusive, royalty-free, copyright license to use, reproduce, prepare derivative works of, publicly display, publicly perform, sublicense and distribute its Work and any resulting derivative works in any form. + +3. Limitations + +3.1 Redistribution. You may reproduce or distribute the Work only if (a) you do so under this license, (b) you include a complete copy of this license with your distribution, and (c) you retain without modification any copyright, patent, trademark, or attribution notices that are present in the Work. + +3.2 Derivative Works. You may specify that additional or different terms apply to the use, reproduction, and distribution of your derivative works of the Work (“Your Terms”) only if (a) Your Terms provide that the use limitation in Section 3.3 applies to your derivative works; (b) you comply with Other Licenses, and (c) you identify the specific derivative works that are subject to Your Terms and Other Licenses, as applicable. Notwithstanding Your Terms, this license (including the redistribution requirements in Section 3.1) will continue to apply to the Work itself. + +3.3 Use Limitation. The Work and any derivative works thereof only may be used or intended for use non-commercially. As used herein, “non-commercially” means for non-commercial research purposes only, and excludes any military, surveillance, service of nuclear technology or biometric processing purposes. + +3.4 Patent Claims. If you bring or threaten to bring a patent claim against any Licensor (including any claim, cross-claim or counterclaim in a lawsuit) to enforce any patents that you allege are infringed by any Work, then your rights under this license from such Licensor (including the grant in Section 2.1) will terminate immediately. + +3.5 Trademarks. This license does not grant any rights to use any Licensor’s or its affiliates’ names, logos, or trademarks, except as necessary to reproduce the notices described in this license. + +3.6 Termination. If you violate any term of this license, then your rights under this license (including the grant in Section 2.1) will terminate immediately. + +3.7 Components Under Other Licenses. The Work may include or be distributed with components provided with separate legal notices or terms that accompany the components, such as open source software licenses and other license terms, including but not limited to the Meta OPT-IML 175B License Agreement (“Other Licenses”). The components are subject to the applicable Other Licenses, including any proprietary notices, disclaimers, requirements and extended use rights; except that this Agreement will prevail regarding the use of third-party software, unless a third-party software license requires it license terms to prevail. + +4. Disclaimer of Warranty. + +THE WORK IS PROVIDED “AS IS” WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WARRANTIES OR CONDITIONS OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT. YOU BEAR THE RISK OF UNDERTAKING ANY ACTIVITIES UNDER THIS LICENSE. + +5. Limitation of Liability. + +EXCEPT AS PROHIBITED BY APPLICABLE LAW, IN NO EVENT AND UNDER NO LEGAL THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE SHALL ANY LICENSOR BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATED TO THIS LICENSE, THE USE OR INABILITY TO USE THE WORK (INCLUDING BUT NOT LIMITED TO LOSS OF GOODWILL, BUSINESS INTERRUPTION, LOST PROFITS OR DATA, COMPUTER FAILURE OR MALFUNCTION, OR ANY OTHER DAMAGES OR LOSSES), EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..09d1141 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +.PHONY : run-checks +run-checks : + isort --check . + black --check . + ruff check . + # mypy . + +.PHONY : format +format : + isort . + black . + +.PHONY : build +build : + rm -rf *.egg-info/ + python -m build diff --git a/README.md b/README.md new file mode 100644 index 0000000..b593832 --- /dev/null +++ b/README.md @@ -0,0 +1,140 @@ +# gr00t_wbc + +Software stack for loco-manipulation experiments across multiple humanoid platforms, with primary support for the Unitree G1. This repository provides whole-body control policies, a teleoperation stack, and a data exporter. + +--- + +## System Installation + +### Prerequisites +- Ubuntu 22.04 +- NVIDIA GPU with a recent driver +- Docker and NVIDIA Container Toolkit (required for GPU access inside the container) + +### Repository Setup +Install Git and Git LFS: +```bash +sudo apt update +sudo apt install git git-lfs +git lfs install +``` + +Clone the repository: +```bash +mkdir -p ~/Projects +cd ~/Projects +git clone https://github.com/NVlabs/gr00t_wbc.git +cd gr00t_wbc +``` + +### Docker Environment +We provide a Docker image with all dependencies pre-installed. + +Install a fresh image and start a container: +```bash +./docker/run_docker.sh --install --root +``` +This pulls the latest `gr00t_wbc` image from `docker.io/nvgear`. + +Start or re-enter a container: +```bash +./docker/run_docker.sh --root +``` + +Use `--root` to run as the `root` user. To run as a normal user, build the image locally: +```bash +./docker/run_docker.sh --build +``` +--- + +## Running the Control Stack + +Once inside the container, the control policies can be launched directly. + +- Simulation: + ```bash + python gr00t_wbc/control/main/teleop/run_g1_control_loop.py + ``` +- Real robot: Ensure the host machine network is configured per the [G1 SDK Development Guide](https://support.unitree.com/home/en/G1_developer) and set a static IP at `192.168.123.222`, subnet mask `255.255.255.0`: + ```bash + python gr00t_wbc/control/main/teleop/run_g1_control_loop.py --interface real + ``` + +Keyboard shortcuts (terminal window): +- `]`: Activate policy +- `o`: Deactivate policy +- `9`: Release / Hold the robot +- `w` / `s`: Move forward / backward +- `a` / `d`: Strafe left / right +- `q` / `e`: Rotate left / right +- `z`: Zero navigation commands +- `1` / `2`: Raise / lower the base height +- `backspace` (viewer): Reset the robot in the visualizer + +--- + +## Running the Teleoperation Stack + +The teleoperation policy primarily uses Pico controllers for coordinated hand and body control. It also supports other teleoperation devices, including LeapMotion and HTC Vive with Nintendo Switch Joy-Con controllers. + +Keep `run_g1_control_loop.py` running, and in another terminal run: + +```bash +python gr00t_wbc/control/main/teleop/run_teleop_policy_loop.py --hand_control_device=pico --body_control_device=pico +``` + +### Pico Setup and Controls +Configure the teleop app on your Pico headset by following the [XR Robotics guidelines](https://github.com/XR-Robotics). + +The necessary PC software is pre-installed in the Docker container. Only the [XRoboToolkit-PC-Service](https://github.com/XR-Robotics/XRoboToolkit-PC-Service) component is needed. + +Prerequisites: Connect the Pico to the same network as the host computer. + +Controller bindings: +- `menu + left trigger`: Toggle lower-body policy +- `menu + right trigger`: Toggle upper-body policy +- `Left stick`: X/Y translation +- `Right stick`: Yaw rotation +- `L/R triggers`: Control hand grippers + +Pico unit test: +```bash +python gr00t_wbc/control/teleop/streamers/pico_streamer.py +``` + +--- + +## Running the Data Collection Stack + +Run the full stack (control loop, teleop policy, and camera forwarder) via the deployment helper: +```bash +python scripts/deploy_g1.py \ + --interface sim \ + --camera_host localhost \ + --sim_in_single_process \ + --simulator robocasa \ + --image-publish \ + --enable-offscreen \ + --env_name PnPBottle \ + --hand_control_device=pico \ + --body_control_device=pico +``` + +The `tmux` session `g1_deployment` is created with panes for: +- `control_data_teleop`: Main control loop, data collection, and teleoperation policy +- `camera`: Camera forwarder +- `camera_viewer`: Optional live camera feed + +Operations in the `controller` window (`control_data_teleop` pane, left): +- `]`: Activate policy +- `o`: Deactivate policy +- `k`: Reset the simulation and policies +- `` ` ``: Terminate the tmux session +- `ctrl + d`: Exit the shell in the pane + +Operations in the `data exporter` window (`control_data_teleop` pane, right top): +- Enter the task prompt + +Operations on Pico controllers: +- `A`: Start/Stop recording +- `B`: Discard trajectory diff --git a/docker/.bashrc b/docker/.bashrc new file mode 100644 index 0000000..ecd18c6 --- /dev/null +++ b/docker/.bashrc @@ -0,0 +1,160 @@ +# ~/.bashrc: executed by bash(1) for non-login shells. +# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) +# for examples + +# If not running interactively, don't do anything +case $- in + *i*) ;; + *) return;; +esac + +# don't put duplicate lines or lines starting with space in the history. +# See bash(1) for more options +HISTCONTROL=ignoreboth + +# append to the history file, don't overwrite it +shopt -s histappend + +# for setting history length see HISTSIZE and HISTFILESIZE in bash(1) +HISTSIZE=1000 +HISTFILESIZE=2000 + +# check the window size after each command and, if necessary, +# update the values of LINES and COLUMNS. +shopt -s checkwinsize + +# If set, the pattern "**" used in a pathname expansion context will +# match all files and zero or more directories and subdirectories. +#shopt -s globstar + +# make less more friendly for non-text input files, see lesspipe(1) +[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" + +# set variable identifying the chroot you work in (used in the prompt below) +if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then + debian_chroot=$(cat /etc/debian_chroot) +fi + +# set a fancy prompt (non-color, unless we know we "want" color) +case "$TERM" in + xterm-color|*-256color) color_prompt=yes;; +esac + +# uncomment for a colored prompt, if the terminal has the capability; turned +# off by default to not distract the user: the focus in a terminal window +# should be on the output of commands, not on the prompt +force_color_prompt=yes + +if [ -n "$force_color_prompt" ]; then + if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then + # We have color support; assume it's compliant with Ecma-48 + # (ISO/IEC-6429). (Lack of such support is extremely rare, and such + # a case would tend to support setf rather than setaf.) + color_prompt=yes + else + color_prompt= + fi +fi + +if [ "$color_prompt" = yes ]; then + PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' +else + PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' +fi +unset color_prompt force_color_prompt + +# If this is an xterm set the title to user@host:dir +case "$TERM" in +xterm*|rxvt*) + PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1" + ;; +*) + ;; +esac + +# enable color support of ls and also add handy aliases +if [ -x /usr/bin/dircolors ]; then + test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" + alias ls='ls --color=auto' + #alias dir='dir --color=auto' + #alias vdir='vdir --color=auto' + + alias grep='grep --color=auto' + alias fgrep='fgrep --color=auto' + alias egrep='egrep --color=auto' +fi + +# colored GCC warnings and errors +export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01' + +# Set terminal type for color support +export TERM=xterm-256color + +# some more ls aliases +alias ll='ls -alF' +alias la='ls -A' +alias l='ls -CF' + +# Add an "alert" alias for long running commands. Use like so: +# sleep 10; alert +alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"' + +# Alias definitions. +# You may want to put all your additions into a separate file like +# ~/.bash_aliases, instead of adding them here directly. +# See /usr/share/doc/bash-doc/examples in the bash-doc package. + +if [ -f ~/.bash_aliases ]; then + . ~/.bash_aliases +fi + +# enable programmable completion features (you don't need to enable +# this, if it's already enabled in /etc/bash.bashrc and /etc/profile +# sources /etc/bash.bashrc). +if ! shopt -oq posix; then + if [ -f /usr/share/bash-completion/bash_completion ]; then + . /usr/share/bash-completion/bash_completion + elif [ -f /etc/bash_completion ]; then + . /etc/bash_completion + fi +fi + +# useful commands +bind '"\e[A": history-search-backward' +bind '"\e[B": history-search-forward' + +# Store the last 10 directories in a history file +CD_HISTFILE=~/.cd_history +CD_HISTSIZE=10 + +cd() { + local histfile="${CD_HISTFILE:-$HOME/.cd_history}" + local max="${CD_HISTSIZE:-10}" + + case "$1" in + --) [ -f "$histfile" ] && tac "$histfile" | nl -w2 -s' ' || echo "No directory history yet."; return ;; + -[0-9]*) + local idx=${1#-} + local dir=$(tac "$histfile" 2>/dev/null | sed -n "${idx}p") + [ -n "$dir" ] && builtin cd "$dir" || echo "Invalid selection: $1" + return ;; + esac + + builtin cd "$@" || return + + [[ $(tail -n1 "$histfile" 2>/dev/null) != "$PWD" ]] && echo "$PWD" >> "$histfile" + tail -n "$max" "$histfile" > "${histfile}.tmp" && mv "${histfile}.tmp" "$histfile" +} + +# Manus to LD_LIBRARY_PATH +export LD_LIBRARY_PATH=$GR00T_WBC_DIR/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/lib:$LD_LIBRARY_PATH + +# CUDA support +export LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:/usr/lib:/lib/x86_64-linux-gnu:/lib64:/lib:$LD_LIBRARY_PATH + +# gr00t_wbc aliases +alias dg="python $GR00T_WBC_DIR/scripts/deploy_g1.py" +alias rsl="python $GR00T_WBC_DIR/gr00t_wbc/control/main/teleop/run_sim_loop.py" +alias rgcl="python $GR00T_WBC_DIR/gr00t_wbc/control/main/teleop/run_g1_control_loop.py" +alias rtpl="python $GR00T_WBC_DIR/gr00t_wbc/control/main/teleop/run_teleop_policy_loop.py" +alias tgcl="pytest $GR00T_WBC_DIR/tests/control/main/teleop/test_g1_control_loop.py -s" diff --git a/docker/.tmux.conf b/docker/.tmux.conf new file mode 100644 index 0000000..0da4a31 --- /dev/null +++ b/docker/.tmux.conf @@ -0,0 +1,84 @@ +# Enable mouse mode +set -g mouse on + +# Start window numbering at 0 (default) +set -g base-index 0 +setw -g pane-base-index 0 + +# Increase scrollback buffer size +set -g history-limit 50000 + +# Use Alt-arrow keys without prefix key to switch panes +bind -n M-Left select-pane -L +bind -n M-Right select-pane -R +bind -n M-Up select-pane -U +bind -n M-Down select-pane -D + +# Use Alt-1,2,3... to switch windows +bind -n M-1 select-window -t 1 +bind -n M-2 select-window -t 2 +bind -n M-3 select-window -t 3 +bind -n M-4 select-window -t 4 +bind -n M-5 select-window -t 5 +bind -n M-6 select-window -t 6 +bind -n M-7 select-window -t 7 +bind -n M-8 select-window -t 8 +bind -n M-9 select-window -t 9 + +# Split panes using Alt-| and Alt-- +bind -n M-| split-window -h +bind -n M-- split-window -v + +# Easy config reload +bind -n M-r source-file ~/.tmux.conf \; display-message "Config reloaded!" + +# Status bar customization +set -g status-style bg=colour240,fg=colour255 +set -g status-left "#[fg=colour255,bg=colour240] #S #[fg=colour240,bg=colour238]" +set -g status-right "#[fg=colour255,bg=colour240] %H:%M #[fg=colour240,bg=colour238]" + +# Window status format +setw -g window-status-format "#[fg=colour255,bg=colour238] #I:#W " +setw -g window-status-current-format "#[fg=colour238,bg=colour255]#[fg=colour238,bg=colour255] #I:#W #[fg=colour255,bg=colour238]" + +# Pane border colors +set -g pane-border-style fg=colour240 +set -g pane-active-border-style fg=colour255 + +# Message text +set -g message-style bg=colour238,fg=colour255 + +# Clock mode +setw -g clock-mode-colour colour255 + +# Enable focus events +set -g focus-events on + +# Increase escape time +set -sg escape-time 0 + +# Enable true color support +set -ga terminal-overrides ",*256col*:Tc" + +# Set default terminal mode to 256 colors +set -g default-terminal "screen-256color" + +# Display a message when a window is created +set -g display-time 4000 + +# Automatically set window title +setw -g automatic-rename on +set -g set-titles on +set -g set-titles-string "#T" + +# Enable clipboard integration +set -g @plugin 'tmux-plugins/tmux-yank' + +# List of plugins +set -g @plugin 'tmux-plugins/tpm' +set -g @plugin 'tmux-plugins/tmux-sensible' +set -g @plugin 'tmux-plugins/tmux-resurrect' +set -g @plugin 'tmux-plugins/tmux-continuum' + +# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf) +run '~/.tmux/plugins/tpm/tpm' \ No newline at end of file diff --git a/docker/70-manus-hid.rules b/docker/70-manus-hid.rules new file mode 100644 index 0000000..3c2bb32 --- /dev/null +++ b/docker/70-manus-hid.rules @@ -0,0 +1,5 @@ + # HIDAPI/libusb + SUBSYSTEMS=="usb", ATTRS{idVendor}=="3325", MODE:="0666" + + # HIDAPI/hidraw + KERNEL=="hidraw*", ATTRS{idVendor}=="3325", MODE:="0666" \ No newline at end of file diff --git a/docker/Dockerfile.deploy b/docker/Dockerfile.deploy new file mode 100644 index 0000000..7ba55e3 --- /dev/null +++ b/docker/Dockerfile.deploy @@ -0,0 +1,127 @@ +FROM nvgear/ros-2:latest + +# Accept build argument for username +ARG USERNAME +ARG USERID +ARG HOME_DIR +ARG WORKTREE_NAME + +# Create user with the same name as host +RUN if [ "$USERID" != "0" ]; then \ + useradd -m -u ${USERID} -s /bin/bash ${USERNAME} && \ + echo "${USERNAME} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers && \ + # Add user to video and render groups for GPU access + usermod -a -G video,render ${USERNAME} || true; \ + fi + +# Copy .bashrc with color settings before switching user +COPY --chown=${USERNAME}:${USERNAME} docker/.bashrc ${HOME_DIR}/.bashrc + +# Install Manus udev rules +COPY --chown=${USERNAME}:${USERNAME} docker/70-manus-hid.rules /etc/udev/rules.d/70-manus-hid.rules + +# Copy tmux configuration +COPY --chown=${USERNAME}:${USERNAME} docker/.tmux.conf ${HOME_DIR}/.tmux.conf + +# Switch to user +USER ${USERNAME} + +# Install tmux plugin manager and uv in parallel +RUN git clone https://github.com/tmux-plugins/tpm ${HOME_DIR}/.tmux/plugins/tpm & \ + curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=${HOME_DIR}/.cargo/bin sh & \ + wait + +# Install tmux plugins automatically +RUN ${HOME_DIR}/.tmux/plugins/tpm/bin/install_plugins || true + +# Add uv to PATH +ENV PATH="${HOME_DIR}/.cargo/bin:$PATH" +ENV UV_PYTHON=${HOME_DIR}/venv/bin/python + +# Create venv +RUN uv venv --python 3.10 ${HOME_DIR}/venv + +# Install hardware-specific packages (x86 only - not available on ARM64/Orin) +USER root +COPY --chown=${USERNAME}:${USERNAME} gr00t_wbc/control/teleop/device/pico/XRoboToolkit_PC_Service_1.0.0_ubuntu_22.04_amd64.deb ${HOME_DIR}/XRoboToolkit_PC_Service_1.0.0_ubuntu_22.04_amd64.deb +COPY --chown=${USERNAME}:${USERNAME} gr00t_wbc/control/teleop/device/pico/roboticsservice_1.0.0.0_arm64.deb ${HOME_DIR}/roboticsservice_1.0.0.0_arm64.deb + +RUN if [ "$(dpkg --print-architecture)" = "amd64" ]; then \ + # Ultra Leap setup + wget -qO - https://repo.ultraleap.com/keys/apt/gpg | gpg --dearmor | tee /etc/apt/trusted.gpg.d/ultraleap.gpg && \ + echo 'deb [arch=amd64] https://repo.ultraleap.com/apt stable main' | tee /etc/apt/sources.list.d/ultraleap.list && \ + apt-get update && \ + echo "yes" | DEBIAN_FRONTEND=noninteractive apt-get install -y ultraleap-hand-tracking libhidapi-dev && \ + # Space Mouse udev rules + echo 'KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev"' > /etc/udev/rules.d/99-hidraw-permissions.rules && \ + usermod -aG plugdev ${USERNAME}; \ + # Pico setup + apt-get install -y xdg-utils && \ + dpkg -i ${HOME_DIR}/XRoboToolkit_PC_Service_1.0.0_ubuntu_22.04_amd64.deb; \ + else \ + echo "Skipping x86-only hardware packages on $(dpkg --print-architecture)"; \ + fi + +USER ${USERNAME} +# Install hardware Python packages (x86 only) with caching +RUN --mount=type=cache,target=${HOME_DIR}/.cache/uv,uid=${USERID},gid=${USERID} \ + if [ "$(dpkg --print-architecture)" = "amd64" ]; then \ + # Ultra Leap Python bindings + git clone https://github.com/ultraleap/leapc-python-bindings ${HOME_DIR}/leapc-python-bindings && \ + cd ${HOME_DIR}/leapc-python-bindings && \ + UV_CONCURRENT_DOWNLOADS=8 uv pip install -r requirements.txt && \ + MAKEFLAGS="-j$(nproc)" ${HOME_DIR}/venv/bin/python -m build leapc-cffi && \ + uv pip install leapc-cffi/dist/leapc_cffi-0.0.1.tar.gz && \ + uv pip install -e leapc-python-api && \ + # Space Mouse Python package + uv pip install pyspacemouse && \ + # Pico Python bindings + git clone https://github.com/XR-Robotics/XRoboToolkit-PC-Service-Pybind.git ${HOME_DIR}/XRoboToolkit-PC-Service-Pybind && \ + cd ${HOME_DIR}/XRoboToolkit-PC-Service-Pybind && \ + uv pip install setuptools pybind11 && \ + sed -i "s|pip install|uv pip install|g" setup_ubuntu.sh && \ + sed -i "s|pip uninstall|uv pip uninstall|g" setup_ubuntu.sh && \ + sed -i "s|python setup.py install|${HOME_DIR}/venv/bin/python setup.py install|g" setup_ubuntu.sh && \ + bash setup_ubuntu.sh; \ + fi + +# Install Python dependencies using uv with caching +RUN --mount=type=cache,target=${HOME_DIR}/.cache/uv,uid=${USERID},gid=${USERID} \ + UV_CONCURRENT_DOWNLOADS=8 uv pip install --upgrade pip ipython jupyter notebook debugpy + + +# Copy entire project to the workspace directory where it will be mounted at runtime +# NOTE: The build context must be the project root for this to work +# Use dynamic worktree name to match runtime mount path +COPY --chown=${USERNAME}:${USERNAME} . ${HOME_DIR}/Projects/${WORKTREE_NAME} + +# Install Python dependencies inside the venv with caching - split into separate commands +RUN --mount=type=cache,target=${HOME_DIR}/.cache/uv,uid=${USERID},gid=${USERID} \ + UV_CONCURRENT_DOWNLOADS=8 uv pip install \ + -e ${HOME_DIR}/Projects/${WORKTREE_NAME}/external_dependencies/unitree_sdk2_python + +# Unlike pip, uv downloads LFS files by default. There's a bug in uv that causes LFS files +# to fail to download (https://github.com/astral-sh/uv/issues/3312). So we need to set +# UV_GIT_LFS=1 to prevent uv from downloading LFS files. +# Install project dependencies (VLA extras only on x86) with caching +RUN --mount=type=cache,target=${HOME_DIR}/.cache/uv,uid=${USERID},gid=${USERID} \ + GIT_LFS_SKIP_SMUDGE=1 UV_CONCURRENT_DOWNLOADS=8 uv pip install -e "${HOME_DIR}/Projects/${WORKTREE_NAME}[dev]" + +# Clone and install robosuite with specific branch +RUN --mount=type=cache,target=${HOME_DIR}/.cache/uv,uid=${USERID},gid=${USERID} \ + git clone https://github.com/xieleo5/robosuite.git ${HOME_DIR}/robosuite && \ + cd ${HOME_DIR}/robosuite && \ + git checkout leo/support_g1_locomanip && \ + UV_CONCURRENT_DOWNLOADS=8 uv pip install -e . + +# Install gr00trobocasa +RUN --mount=type=cache,target=${HOME_DIR}/.cache/uv,uid=${USERID},gid=${USERID} \ + UV_CONCURRENT_DOWNLOADS=8 uv pip install -e ${HOME_DIR}/Projects/${WORKTREE_NAME}/gr00t_wbc/dexmg/gr00trobocasa + +# Configure bash environment with virtual environment and ROS2 setup +RUN echo "source ${HOME_DIR}/venv/bin/activate" >> ${HOME_DIR}/.bashrc && \ + echo "source /opt/ros/humble/setup.bash" >> ${HOME_DIR}/.bashrc && \ + echo "export ROS_LOCALHOST_ONLY=1" >> ${HOME_DIR}/.bashrc + +# Default command (can be overridden at runtime) +CMD ["/bin/bash"] diff --git a/docker/Dockerfile.deploy.base b/docker/Dockerfile.deploy.base new file mode 100644 index 0000000..360d2c5 --- /dev/null +++ b/docker/Dockerfile.deploy.base @@ -0,0 +1,155 @@ +# Multi-architecture Dockerfile for NVIDIA CUDA +# Supports linux/amd64 and linux/arm64 +FROM nvidia/cuda:12.4.0-runtime-ubuntu22.04 + +# Build info - ARGs need to be redeclared after FROM to use in RUN commands +ARG TARGETPLATFORM +ARG BUILDPLATFORM +RUN echo "Building for $TARGETPLATFORM on $BUILDPLATFORM" + +# Avoid prompts from apt +ENV DEBIAN_FRONTEND=noninteractive + +# Install minimal system dependencies +RUN apt-get update && \ + apt-get install -y \ + # Basic tools + build-essential \ + curl \ + gdb \ + git \ + git-lfs \ + net-tools \ + sudo \ + wget \ + iputils-ping \ + vim \ + unzip \ + # System services + udev \ + # Graphics and X11 + libgl1-mesa-dri \ + libgl1-mesa-glx \ + libglu1-mesa \ + mesa-utils \ + libxcb-cursor0 \ + x11-apps \ + xauth \ + # EGL and GPU access + libegl1 \ + libegl1-mesa \ + libegl1-mesa-dev \ + libgl1-mesa-dev \ + libgles2-mesa-dev \ + libglvnd-dev \ + mesa-common-dev \ + # XCB and Qt platform dependencies + libxcb-icccm4 \ + libxcb-image0 \ + libxcb-keysyms1 \ + libxcb-randr0 \ + libxcb-render-util0 \ + libxcb-shape0 \ + libxcb-xfixes0 \ + libxcb-xinerama0 \ + libxcb-xinput0 \ + libxcb-xkb1 \ + libxkbcommon-x11-0 \ + # D-Bus and system dependencies + libdbus-1-3 \ + # Other dependencies + libncurses5-dev \ + libudev-dev \ + libusb-1.0-0-dev \ + # Python 3.10 and pip + python3.10 \ + python3.10-venv \ + python3.10-distutils \ + python3-pip \ + expect \ + # ffmpeg and related libraries + ffmpeg \ + libavcodec-dev \ + libavformat-dev \ + libavdevice-dev \ + libavfilter-dev \ + libavutil-dev \ + libswresample-dev \ + libswscale-dev \ + # opencv + libgtk2.0-dev \ + # Clean up + && rm -rf /var/lib/apt/lists/* + +# --- Install ROS 2 Humble (following official instructions) --- +# Enable required repositories +RUN apt-get update && apt-get install -y software-properties-common \ + && add-apt-repository universe \ + # Add ROS 2 GPG key and repository + && apt-get install -y curl gnupg lsb-release \ + && curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" > /etc/apt/sources.list.d/ros2.list \ + # Upgrade system to avoid removal of critical packages (see ROS 2 docs) + && apt-get update \ + && apt-get upgrade -y \ + # Install ROS 2 Humble desktop + && apt-get install -y ros-humble-desktop \ + # (Optional) Install development tools + && apt-get install -y ros-dev-tools \ + # Install Eclipse Cyclone DDS RMW implementation + && apt-get install -y ros-humble-rmw-cyclonedds-cpp \ + # Clean up + && rm -rf /var/lib/apt/lists/* + +# Source ROS 2 setup in bashrc for all users +RUN echo 'source /opt/ros/humble/setup.bash' >> /etc/bash.bashrc + +# Clone, build, and install CycloneDDS 0.10.x +RUN git clone --branch releases/0.10.x https://github.com/eclipse-cyclonedds/cyclonedds /opt/cyclonedds && \ + mkdir -p /opt/cyclonedds/build /opt/cyclonedds/install && \ + cd /opt/cyclonedds/build && \ + cmake .. -DCMAKE_INSTALL_PREFIX=../install && \ + cmake --build . --target install && \ + # Clean up build files to reduce image size + rm -rf /opt/cyclonedds/build + +# Set CYCLONEDDS_HOME for all users +ENV CYCLONEDDS_HOME=/opt/cyclonedds/install + +# Install uv +RUN curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/opt/uv sh + +# Add uv to PATH +ENV PATH="/opt/uv:$PATH" +ENV UV_PYTHON=/opt/venv/bin/python + +# Fix dpkg state and install tmux +RUN dpkg --configure -a && apt-get update && apt-get install -y tmux && rm -rf /var/lib/apt/lists/* + +# Create NVIDIA ICD config for EGL if it doesn't exist +# Note: This might need platform-specific handling for ARM64 +RUN if [ ! -f /usr/share/glvnd/egl_vendor.d/10_nvidia.json ]; then \ + mkdir -p /usr/share/glvnd/egl_vendor.d && \ + printf '{\n "file_format_version" : "1.0.0",\n "ICD" : {\n "library_path" : "libEGL_nvidia.so.0"\n }\n}' | tee /usr/share/glvnd/egl_vendor.d/10_nvidia.json > /dev/null; \ + fi + +# Platform-specific configurations +RUN case "$TARGETPLATFORM" in \ + "linux/arm64") \ + echo "Configuring for ARM64 platform" && \ + # Add any ARM64-specific configurations here + echo "export GPU_FORCE_64BIT_PTR=1" >> /etc/environment \ + ;; \ + "linux/amd64") \ + echo "Configuring for AMD64 platform" && \ + # Add any AMD64-specific configurations here + echo "AMD64 platform configured" \ + ;; \ + *) \ + echo "Unknown platform: $TARGETPLATFORM" \ + ;; \ + esac + +# Add labels for better image management +LABEL org.opencontainers.image.title="Multi-Arch CUDA Runtime with ROS 2 Humble" +LABEL org.opencontainers.image.description="Multi-architecture Docker image with CUDA runtime and ROS 2 Humble" diff --git a/docker/build_deploy_base.sh b/docker/build_deploy_base.sh new file mode 100644 index 0000000..6c1ddb6 --- /dev/null +++ b/docker/build_deploy_base.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# Multi-architecture Docker build script +# Supports linux/amd64 + +set -e + +# Configuration +IMAGE_NAME="nvgear/ros-2" +TAG="${1:-latest}" +DOCKERFILE="docker/Dockerfile.deploy.base" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +echo -e "${GREEN}Building multi-architecture Docker image: ${IMAGE_NAME}:${TAG}${NC}" + +# Ensure we're using the multiarch builder +echo -e "${YELLOW}Setting up multiarch builder...${NC}" +sudo docker buildx use multiarch-builder 2>/dev/null || { + echo -e "${YELLOW}Creating multiarch builder...${NC}" + sudo docker buildx create --name multiarch-builder --use --bootstrap +} + +# Show supported platforms +echo -e "${YELLOW}Supported platforms:${NC}" +sudo docker buildx inspect --bootstrap | grep Platforms + +# Build for multiple architectures +echo -e "${GREEN}Starting multi-arch build...${NC}" +sudo docker buildx build \ + --platform linux/amd64 \ + --file "${DOCKERFILE}" \ + --tag "${IMAGE_NAME}:${TAG}" \ + --push \ + . + +# Alternative: Build and load locally (only works for single platform) +# docker buildx build \ +# --platform linux/amd64 \ +# --file "${DOCKERFILE}" \ +# --tag "${IMAGE_NAME}:${TAG}" \ +# --load \ +# . + +echo -e "${GREEN}Multi-arch build completed successfully!${NC}" +echo -e "${GREEN}Image: ${IMAGE_NAME}:${TAG}${NC}" +echo -e "${GREEN}Platforms: linux/amd64${NC}" + +# Verify the manifest +echo -e "${YELLOW}Verifying multi-arch manifest...${NC}" +sudo docker buildx imagetools inspect "${IMAGE_NAME}:${TAG}" \ No newline at end of file diff --git a/docker/entrypoint/bash.sh b/docker/entrypoint/bash.sh new file mode 100755 index 0000000..1142dd8 --- /dev/null +++ b/docker/entrypoint/bash.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -e # Exit on error + +echo "Dependencies installed successfully. Starting interactive bash shell..." +exec /bin/bash \ No newline at end of file diff --git a/docker/entrypoint/deploy.sh b/docker/entrypoint/deploy.sh new file mode 100755 index 0000000..216fbc2 --- /dev/null +++ b/docker/entrypoint/deploy.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -e # Exit on error + +# Run the deployment script +# Check for script existence before running +DEPLOY_SCRIPT="scripts/deploy_g1.py" +if [ -f "$DEPLOY_SCRIPT" ]; then + echo "Running deployment script at $DEPLOY_SCRIPT" + echo "Using python from $(which python)" + echo "Deploy args: $@" + exec python "$DEPLOY_SCRIPT" "$@" +else + echo "ERROR: Deployment script not found at $DEPLOY_SCRIPT" + echo "Current directory structure:" + find . -type f -name "*.py" | grep -i deploy + echo "Available script options:" + find . -type f -name "*.py" | sort + echo "Starting a bash shell for troubleshooting..." + exec /bin/bash +fi \ No newline at end of file diff --git a/docker/entrypoint/install_deps.sh b/docker/entrypoint/install_deps.sh new file mode 100755 index 0000000..cb602fb --- /dev/null +++ b/docker/entrypoint/install_deps.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -e + +# Source virtual environment and ROS2 +source ${HOME}/venv/bin/activate +source /opt/ros/humble/setup.bash +export ROS_LOCALHOST_ONLY=1 + +# Install external dependencies +echo "Current directory: $(pwd)" +echo "Installing dependencies..." + +# Install Unitree SDK and LeRobot +if [ -d "external_dependencies/unitree_sdk2_python" ]; then + cd external_dependencies/unitree_sdk2_python/ + uv pip install -e . --no-deps + cd ../.. +fi + +# Install project +if [ -f "pyproject.toml" ]; then + UV_GIT_LFS=1 uv pip install -e ".[dev]" +fi diff --git a/docker/image_name.txt b/docker/image_name.txt new file mode 100644 index 0000000..10b5635 --- /dev/null +++ b/docker/image_name.txt @@ -0,0 +1 @@ +nvcr.io/nvidian/gr00t_wbc:base diff --git a/docker/kill_all_containers.sh b/docker/kill_all_containers.sh new file mode 100755 index 0000000..1043070 --- /dev/null +++ b/docker/kill_all_containers.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +# Script to kill all running Docker containers +# Usage: ./kill_all_containers.sh [--force] + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Function to print colored output +print_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Check if Docker is running +if ! sudo docker info >/dev/null 2>&1; then + print_error "Docker is not running or not accessible. Please start Docker first." + exit 1 +fi + +# Get list of running containers +RUNNING_CONTAINERS=$(sudo docker ps -q) + +if [ -z "$RUNNING_CONTAINERS" ]; then + print_info "No running containers found." + exit 0 +fi + +# Count running containers +CONTAINER_COUNT=$(echo "$RUNNING_CONTAINERS" | wc -l | tr -d ' ') + +print_info "Found $CONTAINER_COUNT running container(s):" +sudo docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Names}}\t{{.Status}}" + +# Check for --force flag +FORCE_KILL=false +if [ "$1" = "--force" ]; then + FORCE_KILL=true + print_warning "Force mode enabled. Containers will be killed without confirmation." +fi + +# Ask for confirmation unless --force is used +if [ "$FORCE_KILL" = false ]; then + echo + read -p "Are you sure you want to kill all running containers? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + print_info "Operation cancelled." + exit 0 + fi +fi + +# Kill all running containers +print_info "Killing all running containers..." +if sudo docker kill $RUNNING_CONTAINERS; then + print_info "Successfully killed all running containers." +else + print_error "Failed to kill some containers. You may need to run with sudo or check Docker permissions." + exit 1 +fi + +# Optional: Remove stopped containers (commented out by default) +# Uncomment the following lines if you also want to remove the stopped containers +# print_info "Removing stopped containers..." +# sudo docker container prune -f + +print_info "Done!" \ No newline at end of file diff --git a/docker/kill_gr00t_wbc_processors.sh b/docker/kill_gr00t_wbc_processors.sh new file mode 100755 index 0000000..03d0819 --- /dev/null +++ b/docker/kill_gr00t_wbc_processors.sh @@ -0,0 +1,192 @@ +#!/bin/bash + +# kill_gr00t_wbc_processors.sh +# Kill gr00t_wbc processes in current container to prevent message passing conflicts + +# Note: Don't use 'set -e' as tmux/pgrep commands may return non-zero exit codes + +# Configuration +DRY_RUN=false +FORCE=false +QUIET=false +declare -A FOUND_PROCESSES + +# Default to verbose mode if no arguments +[[ $# -eq 0 ]] && { QUIET=false; DRY_RUN=false; } + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --dry-run) DRY_RUN=true ;; + --force) FORCE=true ;; + --verbose|-v) VERBOSE=true ;; + --help|-h) + echo "Usage: $0 [--dry-run] [--force] [--verbose] [--help]" + echo "Kill gr00t_wbc processes to prevent message passing conflicts" + exit 0 ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac + shift +done + +# Colors (only if not quiet) +if [[ "$QUIET" != true ]]; then + RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m' +else + RED=''; GREEN=''; YELLOW=''; BLUE=''; NC='' +fi + +# Show processes by pattern (for preview) +show_processes_by_pattern() { + local pattern="$1" desc="$2" + local pids=$(pgrep -f "$pattern" 2>/dev/null || true) + + [[ -z "$pids" ]] && return 0 + + echo -e "${YELLOW}$desc processes:${NC}" + + for pid in $pids; do + local cmd=$(ps -p $pid -o cmd= 2>/dev/null || echo "Process not found") + echo " PID $pid: $cmd" + done +} + +# Kill processes by pattern (silent killing) +kill_by_pattern() { + local pattern="$1" desc="$2" signal="${3:-TERM}" + local pids=$(pgrep -f "$pattern" 2>/dev/null || true) + + [[ -z "$pids" ]] && return 0 + + for pid in $pids; do + # Kill if not dry run + [[ "$DRY_RUN" != true ]] && kill -$signal $pid 2>/dev/null + done +} + +# Show tmux sessions (for preview) +show_tmux() { + local pattern="$1" + local sessions=$(tmux list-sessions 2>/dev/null | grep "$pattern" | cut -d: -f1 || true) + + [[ -z "$sessions" ]] && return 0 + + echo -e "${YELLOW}Tmux sessions:${NC}" + + for session in $sessions; do + echo " Session: $session" + done +} + +# Kill tmux sessions (silent killing) +kill_tmux() { + local pattern="$1" + local sessions=$(tmux list-sessions 2>/dev/null | grep "$pattern" | cut -d: -f1 || true) + + [[ -z "$sessions" ]] && return 0 + + for session in $sessions; do + [[ "$DRY_RUN" != true ]] && tmux kill-session -t "$session" 2>/dev/null + done +} + +# Show processes by port (for preview) +show_processes_by_port() { + local port="$1" desc="$2" + local pids=$(lsof -ti:$port 2>/dev/null || true) + + [[ -z "$pids" ]] && return 0 + + echo -e "${YELLOW}$desc (port $port):${NC}" + + for pid in $pids; do + local cmd=$(ps -p $pid -o cmd= 2>/dev/null || echo "Process not found") + echo " PID $pid: $cmd" + done +} + +# Kill processes by port (silent killing) +kill_by_port() { + local port="$1" desc="$2" + local pids=$(lsof -ti:$port 2>/dev/null || true) + + [[ -z "$pids" ]] && return 0 + + for pid in $pids; do + [[ "$DRY_RUN" != true ]] && kill -TERM $pid 2>/dev/null + done +} + +# Check if any processes exist +has_processes() { + # Check for processes + local has_tmux=$(tmux list-sessions 2>/dev/null | grep "g1_deployment" || true) + local has_control=$(pgrep -f "run_g1_control_loop.py" 2>/dev/null || true) + local has_teleop=$(pgrep -f "run_teleop_policy_loop.py" 2>/dev/null || true) + local has_camera=$(pgrep -f "camera_forwarder.py" 2>/dev/null || true) + local has_rqt=$(pgrep -f "rqt.*image_view" 2>/dev/null || true) + local has_port=$(lsof -ti:5555 2>/dev/null || true) + + [[ -n "$has_tmux" || -n "$has_control" || -n "$has_teleop" || -n "$has_camera" || -n "$has_rqt" || -n "$has_port" ]] +} + +# Main execution +main() { + # Check if any processes exist first + if ! has_processes; then + # No processes to kill, exit silently + exit 0 + fi + + # Show header and processes to be killed + if [[ "$QUIET" != true ]]; then + echo -e "${BLUE}=== gr00t_wbc Process Killer ===${NC}" + [[ "$DRY_RUN" == true ]] && echo -e "${BLUE}=== DRY RUN MODE ===${NC}" + + # Show what will be killed + show_tmux "g1_deployment" + show_processes_by_pattern "run_g1_control_loop.py" "G1 control loop" + show_processes_by_pattern "run_teleop_policy_loop.py" "Teleop policy" + show_processes_by_pattern "camera_forwarder.py" "Camera forwarder" + show_processes_by_pattern "rqt.*image_view" "RQT viewer" + show_processes_by_port "5555" "Inference server" + + # Ask for confirmation + if [[ "$FORCE" != true && "$DRY_RUN" != true ]]; then + echo + echo -e "${RED}WARNING: This will terminate the above gr00t_wbc processes!${NC}" + read -p "Continue? [Y/n]: " -n 1 -r + echo + # Default to Y - only abort if user explicitly types 'n' or 'N' + [[ $REPLY =~ ^[Nn]$ ]] && { echo "Aborted."; exit 0; } + fi + echo + fi + + # Kill processes (silently) + kill_tmux "g1_deployment" + kill_by_pattern "run_g1_control_loop.py" "G1 control loop" + kill_by_pattern "run_teleop_policy_loop.py" "Teleop policy" + kill_by_pattern "camera_forwarder.py" "Camera forwarder" + kill_by_pattern "rqt.*image_view" "RQT viewer" + kill_by_port "5555" "Inference server" + + # Force kill remaining (SIGKILL) + [[ "$DRY_RUN" != true ]] && { + sleep 1 + kill_by_pattern "run_g1_control_loop.py" "G1 control loop" "KILL" + kill_by_pattern "run_teleop_policy_loop.py" "Teleop policy" "KILL" + kill_by_pattern "camera_forwarder.py" "Camera forwarder" "KILL" + } + + # Summary (unless quiet) + [[ "$QUIET" != true ]] && { + if [[ "$DRY_RUN" == true ]]; then + echo -e "${BLUE}=== DRY RUN COMPLETE ===${NC}" + else + echo -e "${GREEN}All gr00t_wbc processes terminated${NC}" + fi + } +} + +main "$@" diff --git a/docker/publish.sh b/docker/publish.sh new file mode 100644 index 0000000..a9d909e --- /dev/null +++ b/docker/publish.sh @@ -0,0 +1,3 @@ +#!/bin/bash +image_name=$(cat image_name.txt) +docker push "$@" $image_name \ No newline at end of file diff --git a/docker/run_docker.sh b/docker/run_docker.sh new file mode 100755 index 0000000..cca4621 --- /dev/null +++ b/docker/run_docker.sh @@ -0,0 +1,454 @@ +#!/bin/bash + +# Docker run script for gr00t_wbc with branch-based container isolation +# +# Usage: +# ./docker/run_docker.sh [OPTIONS] +# +# Options: +# --build Build Docker image +# --clean Clean containers +# --deploy Run in deploy mode +# --install Pull prebuilt Docker image +# --push Push built image to Docker Hub +# --branch Use branch-specific container names +# +# Branch-based Container Isolation (when --branch flag is used): +# - Each git branch gets its own isolated containers +# - Container names include branch identifier (e.g., gr00t_wbc-deploy-user-main) +# - Works with git worktrees, separate clones, or nested repositories +# - Clean and build operations only affect the current branch + +# Exit on error +set -e + +# Default values +BUILD=false +CLEAN=false +DEPLOY=false +INSTALL=false +# Flag to push the built Docker image to Docker Hub +# This should be used when someone updates the Docker image dependencies +# because this image is used for CI/CD pipelines +# When true, the image will be tagged and pushed to docker.io/nvgear/gr00t_wbc:latest (lowercased in practice) +DOCKER_HUB_PUSH=false +# Flag to build the docker with root user +# This could cause some of your local files to be owned by root +# If you get error like "PermissionError: [Errno 13] Permission denied:" +# You can run `sudo chown -R $USER:$USER .` in local machine to fix it +ROOT=false +BRANCH_MODE=false +EXTRA_ARGS=() +PROJECT_NAME="gr00t_wbc" +PROJECT_SLUG=$(echo "$PROJECT_NAME" | tr '[:upper:]' '[:lower:]') +REMOTE_IMAGE="nvgear/${PROJECT_SLUG}:latest" + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --build) + BUILD=true + shift + ;; + --clean) + CLEAN=true + shift + ;; + --deploy) + DEPLOY=true + shift + ;; + --install) + INSTALL=true + shift + ;; + --push) + DOCKER_HUB_PUSH=true + shift + ;; + --root) + ROOT=true + shift + ;; + --branch) + BRANCH_MODE=true + shift + ;; + *) + # Collect all unknown arguments as extra args for the deployment script + EXTRA_ARGS+=("$1") + shift + ;; + esac +done + +if [ "$INSTALL" = true ] && [ "$BUILD" = true ]; then + echo "Cannot use --install and --build together. Choose one." + exit 1 +fi + + +# Function to get branch name for container naming +function get_branch_id { + # Check if we're in a git repository + if git rev-parse --is-inside-work-tree > /dev/null 2>&1; then + # Get current branch name (returns "HEAD" in detached state) + local branch_name=$(git rev-parse --abbrev-ref HEAD) + # Replace forward slashes with dashes for valid container names + echo "${branch_name//\//-}" + else + # Default: no branch identifier (not in git repo) + echo "" + fi +} + +# Architecture detection helpers +is_arm64() { [ "$(dpkg --print-architecture)" = "arm64" ]; } +is_amd64() { [ "$(dpkg --print-architecture)" = "amd64" ]; } + +# Get current user's username and UID +if [ "$ROOT" = true ]; then + USERNAME=root + USERID=0 + DOCKER_HOME_DIR=/root + CACHE_FROM=${PROJECT_SLUG}-deploy-cache-root +else + USERNAME=$(whoami) + USERID=$(id -u) + DOCKER_HOME_DIR=/home/${USERNAME} + CACHE_FROM=${PROJECT_SLUG}-deploy-cache +fi +# Get input group ID for device access +INPUT_GID=$(getent group input | cut -d: -f3) + +# Get script directory for path calculations +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# Function to get the actual project directory (worktree-aware) +function get_project_dir { + # For worktrees, use the actual worktree root path + if git rev-parse --is-inside-work-tree > /dev/null 2>&1; then + git rev-parse --show-toplevel + else + # Fallback to script-based detection + dirname "$SCRIPT_DIR" + fi +} + +# Get branch identifier +BRANCH_ID=$(get_branch_id) + +# Set project directory (needs to be after branch detection) +PROJECT_DIR="$(get_project_dir)" + +# Function to generate container name with optional branch support +function get_container_name { + local container_type="$1" + if [[ -n "$BRANCH_ID" ]] && [[ "$BRANCH_MODE" = true ]]; then + echo "${PROJECT_SLUG}-${container_type}-${USERNAME}-${BRANCH_ID}" + else + echo "${PROJECT_SLUG}-${container_type}-${USERNAME}" + fi +} + +# Set common variables used throughout the script +DEPLOY_CONTAINER=$(get_container_name "deploy") +BASH_CONTAINER=$(get_container_name "bash") +WORKTREE_NAME=$(basename "$PROJECT_DIR") + +# Debug output for branch detection +if [[ -n "$BRANCH_ID" ]] && [[ "$BRANCH_MODE" = true ]]; then + echo "Branch mode enabled - using branch: $BRANCH_ID" + echo "Project directory: $PROJECT_DIR" +elif [[ -n "$BRANCH_ID" ]]; then + echo "Branch mode disabled - using default containers" + echo "Project directory: $PROJECT_DIR" +else + echo "Running outside git repository" + echo "Project directory: $PROJECT_DIR" +fi + +# Get host's hostname and append -docker +HOSTNAME=$(hostname)-docker + +function clean_container { + echo "Cleaning up Docker containers..." + + # Stop containers + sudo docker stop $DEPLOY_CONTAINER 2>/dev/null || true + sudo docker stop $BASH_CONTAINER 2>/dev/null || true + # Remove containers + echo "Removing containers..." + sudo docker rm $DEPLOY_CONTAINER 2>/dev/null || true + sudo docker rm $BASH_CONTAINER 2>/dev/null || true + echo "Containers cleaned!" +} + + +# Function to install Docker Buildx if needed +function install_docker_buildx { + # Check if Docker Buildx is already installed + if sudo docker buildx version &> /dev/null; then + echo "Docker Buildx is already installed." + return 0 + fi + + echo "Installing Docker Buildx..." + + # Create directories and detect architecture + mkdir -p ~/.docker/cli-plugins/ && sudo mkdir -p /root/.docker/cli-plugins/ + ARCH=$(dpkg --print-architecture) + [[ "$ARCH" == "arm64" ]] && BUILDX_ARCH="linux-arm64" || BUILDX_ARCH="linux-amd64" + + # Get version (with fallback) + BUILDX_VERSION=$(curl -s https://api.github.com/repos/docker/buildx/releases/latest | grep tag_name | cut -d '"' -f 4) + BUILDX_VERSION=${BUILDX_VERSION:-v0.13.1} + + # Download and install for both user and root + curl -L "https://github.com/docker/buildx/releases/download/${BUILDX_VERSION}/buildx-${BUILDX_VERSION}.${BUILDX_ARCH}" -o ~/.docker/cli-plugins/docker-buildx + sudo cp ~/.docker/cli-plugins/docker-buildx /root/.docker/cli-plugins/docker-buildx + chmod +x ~/.docker/cli-plugins/docker-buildx && sudo chmod +x /root/.docker/cli-plugins/docker-buildx + + # Create builder + sudo docker buildx create --use --name mybuilder || true + sudo docker buildx inspect --bootstrap + + echo "Docker Buildx installation complete!" +} + +# Function to install NVIDIA Container Toolkit if needed +function install_nvidia_toolkit { + # Check if NVIDIA Container Toolkit is already installed + if command -v nvidia-container-toolkit &> /dev/null; then + echo "NVIDIA Container Toolkit is already installed." + return 0 + fi + + echo "Installing NVIDIA Container Toolkit..." + + # Add the package repositories + distribution=$(. /etc/os-release;echo $ID$VERSION_ID) + + # Check if GPG key exists and remove it if it does + if [ -f "/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg" ]; then + echo "Removing existing NVIDIA GPG key..." + sudo rm /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg + fi + + # Add new GPG key + curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg + + # Add repository + curl -s -L https://nvidia.github.io/nvidia-container-runtime/$distribution/nvidia-container-runtime.list | \ + sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \ + sudo tee /etc/apt/sources.list.d/nvidia-container-runtime.list + + # Install nvidia-container-toolkit and docker if needed + sudo apt-get update + sudo apt-get install -y nvidia-container-toolkit + + # Install docker if not already installed + if ! command -v docker &> /dev/null; then + sudo apt-get install -y docker.io + fi + + # Configure Docker to use the NVIDIA runtime + sudo nvidia-ctk runtime configure --runtime=docker + + # Restart the Docker daemon + sudo systemctl restart docker + + echo "NVIDIA Container Toolkit installation complete!" +} + + +# Function to build Docker image for current branch +function build_docker_image { + echo "Building Docker image: $DEPLOY_CONTAINER" + + sudo docker buildx build \ + --build-arg USERNAME=$USERNAME \ + --build-arg USERID=$USERID \ + --build-arg HOME_DIR=$DOCKER_HOME_DIR \ + --build-arg WORKTREE_NAME=$WORKTREE_NAME \ + --cache-from $CACHE_FROM \ + -t $DEPLOY_CONTAINER \ + -f docker/Dockerfile.deploy \ + --load \ + . + + # Tag for persistent cache + # sudo docker tag $DEPLOY_CONTAINER $CACHE_FROM + echo "Docker image build complete!" +} + +# Build function +function build_with_cleanup { + echo "Building Docker image..." + echo "Removing existing containers and images..." + clean_container + # Tag for persistent cache before deleting the image + sudo docker tag $DEPLOY_CONTAINER $CACHE_FROM 2>/dev/null || true + sudo docker rmi $DEPLOY_CONTAINER 2>/dev/null || true + echo "Images cleaned!" + + install_docker_buildx + install_nvidia_toolkit + build_docker_image +} + +function install_remote_image { + echo "Installing Docker image from remote registry: $REMOTE_IMAGE" + echo "Removing existing containers to ensure a clean install..." + clean_container + sudo docker pull "$REMOTE_IMAGE" + sudo docker tag "$REMOTE_IMAGE" "$DEPLOY_CONTAINER" + sudo docker tag "$REMOTE_IMAGE" "$CACHE_FROM" 2>/dev/null || true + echo "Docker image install complete!" +} + +# Clean up if requested +if [ "$CLEAN" = true ]; then + clean_container + exit 0 +fi + +# Build if requested +if [ "$BUILD" = true ]; then + build_with_cleanup +fi + +if [ "$INSTALL" = true ]; then + install_remote_image +fi + +if [ "$DOCKER_HUB_PUSH" = true ]; then + echo "Pushing Docker image to Docker Hub: docker.io/nvgear/${PROJECT_SLUG}:latest" + sudo docker tag $DEPLOY_CONTAINER docker.io/nvgear/${PROJECT_SLUG}:latest + sudo docker push docker.io/nvgear/${PROJECT_SLUG}:latest + echo "Docker image pushed to Docker Hub!" + exit 0 +fi + +# Setup X11 display forwarding +setup_x11() { + # Set display if missing and X server available + if [ -z "$DISPLAY" ] && command -v xset >/dev/null 2>&1 && xset q >/dev/null 2>&1; then + export DISPLAY=:1 + echo "No DISPLAY set, using :1" + fi + + # Enable X11 forwarding if possible + if [ -n "$DISPLAY" ] && command -v xhost >/dev/null 2>&1 && xhost +local:docker 2>/dev/null; then + echo "X11 forwarding enabled" + return 0 + else + echo "Headless environment - X11 disabled" + export DISPLAY="" + return 1 + fi +} + +X11_ENABLED=false +setup_x11 && X11_ENABLED=true + +# Mount entire /dev directory for dynamic device access (including hidraw for joycon) +# This allows JoyCon controllers to be detected even when connected after container launch +sudo chmod g+r+w /dev/input/* + +# Detect GPU setup and set appropriate environment variables +echo "Detecting GPU setup..." +GPU_ENV_VARS="" + +# Check if we have both integrated and discrete GPUs (hybrid/Optimus setup) +HAS_AMD_GPU=$(lspci | grep -i "vga\|3d\|display" | grep -i amd | wc -l) +HAS_INTEL_GPU=$(lspci | grep -i "vga\|3d\|display" | grep -i intel | wc -l) +HAS_NVIDIA_GPU=$(lspci | grep -i "vga\|3d\|display" | grep -i nvidia | wc -l) + +if [[ "$HAS_INTEL_GPU" -gt 0 ]] || [[ "$HAS_AMD_GPU" -gt 0 ]] && [[ "$HAS_NVIDIA_GPU" -gt 0 ]]; then + echo "Detected hybrid GPU setup (Intel/AMD integrated + NVIDIA discrete)" + echo "Setting NVIDIA Optimus environment variables for proper rendering offload..." + GPU_ENV_VARS="-e __NV_PRIME_RENDER_OFFLOAD=1 \ + -e __VK_LAYER_NV_optimus=NVIDIA_only" +else + GPU_ENV_VARS="" +fi + +# Set GPU runtime based on architecture +if is_arm64; then + echo "Detected ARM64 architecture (Jetson Orin), using device access instead of nvidia runtime..." + GPU_RUNTIME_ARGS="--device /dev/nvidia0 --device /dev/nvidiactl --device /dev/nvidia-modeset --device /dev/nvidia-uvm --device /dev/nvidia-uvm-tools" +else + GPU_RUNTIME_ARGS="--gpus all --runtime=nvidia" +fi + +# Common Docker run parameters +DOCKER_RUN_ARGS="--hostname $HOSTNAME \ + --user $USERNAME \ + --group-add $INPUT_GID \ + $GPU_RUNTIME_ARGS \ + --ipc=host \ + --network=host \ + --privileged \ + --device=/dev \ + $GPU_ENV_VARS \ + -p 5678:5678 \ + -e DISPLAY=$DISPLAY \ + -e NVIDIA_VISIBLE_DEVICES=all \ + -e NVIDIA_DRIVER_CAPABILITIES=graphics,compute,utility \ + -e __GLX_VENDOR_LIBRARY_NAME=nvidia \ + -e USERNAME=$USERNAME \ + -e GR00T_WBC_DIR="$DOCKER_HOME_DIR/Projects/$WORKTREE_NAME" \ + -v /dev/bus/usb:/dev/bus/usb \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + -v $HOME/.ssh:$DOCKER_HOME_DIR/.ssh \ + -v $HOME/.gear:$DOCKER_HOME_DIR/.gear \ + -v $HOME/.Xauthority:$DOCKER_HOME_DIR/.Xauthority \ + -v $PROJECT_DIR:$DOCKER_HOME_DIR/Projects/$(basename "$PROJECT_DIR") + --device /dev/snd \ + --group-add audio \ + -e PULSE_SERVER=unix:/run/user/$(id -u)/pulse/native \ + -v /run/user/$(id -u)/pulse/native:/run/user/$(id -u)/pulse/native \ + -v $HOME/.config/pulse/cookie:/home/$USERNAME/.config/pulse/cookie" + +# Check if RL mode first, then handle container logic +if [ "$DEPLOY" = true ]; then + # Deploy mode - use gr00t_wbc-deploy-${USERNAME} container + + # Always clean up old processes and create a new container + # Kill all gr00t_wbc processes across containers to prevent message passing conflicts + "$SCRIPT_DIR/kill_gr00t_wbc_processors.sh" + echo "Creating new deploy container..." + + # Clean up old processes and create a fresh deploy container + # Remove existing deploy container if it exists + if sudo docker ps -a --format '{{.Names}}' | grep -q "^$DEPLOY_CONTAINER$"; then + echo "Removing existing deploy container..." + sudo docker rm -f $DEPLOY_CONTAINER + fi + sudo docker run -it --rm $DOCKER_RUN_ARGS \ + -w $DOCKER_HOME_DIR/Projects/$WORKTREE_NAME \ + --name $DEPLOY_CONTAINER \ + $DEPLOY_CONTAINER \ + /bin/bash -ic 'exec "$0" "$@"' \ + "${DOCKER_HOME_DIR}/Projects/${WORKTREE_NAME}/docker/entrypoint/deploy.sh" \ + "${EXTRA_ARGS[@]}" +else + # Bash mode - use gr00t_wbc-bash-${USERNAME} container + if sudo docker ps -a --format '{{.Names}}' | grep -q "^$BASH_CONTAINER$"; then + echo "Bash container exists, starting it..." + sudo docker start $BASH_CONTAINER > /dev/null + sudo docker exec -it $BASH_CONTAINER /bin/bash + else + echo "Creating new bash container with auto-install gr00t_wbc..." + sudo docker run -it $DOCKER_RUN_ARGS \ + -w $DOCKER_HOME_DIR/Projects/$WORKTREE_NAME \ + --name $BASH_CONTAINER \ + $DEPLOY_CONTAINER \ + /bin/bash -ic 'exec "$0"' \ + "${DOCKER_HOME_DIR}/Projects/${WORKTREE_NAME}/docker/entrypoint/bash.sh" + fi +fi + +# Cleanup X11 permissions +$X11_ENABLED && xhost -local:docker 2>/dev/null diff --git a/external_dependencies/unitree_sdk2_python/.gitignore b/external_dependencies/unitree_sdk2_python/.gitignore new file mode 100644 index 0000000..001de9a --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/.gitignore @@ -0,0 +1,39 @@ +# Generated by MacOS +.DS_Store + +# Generated by Windows +Thumbs.db + +# Applications +*.app +*.exe +*.war + +# Large media files +*.mp4 +*.tiff +*.avi +*.flv +*.mov +*.wmv +*.jpg +*.png + +# VS Code +.vscode + +# other +*.egg-info +__pycache__ + +# IDEs +.idea + +# cache +.pytest_cache + +# JetBrains IDE +.idea/ + +# python +dist/ \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/LICENSE b/external_dependencies/unitree_sdk2_python/LICENSE new file mode 100644 index 0000000..42d2e64 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2016-2024 HangZhou YuShu TECHNOLOGY CO.,LTD. ("Unitree Robotics") +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/external_dependencies/unitree_sdk2_python/README zh.md b/external_dependencies/unitree_sdk2_python/README zh.md new file mode 100644 index 0000000..a7e6a8c --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/README zh.md @@ -0,0 +1,121 @@ +# unitree_sdk2_python +unitree_sdk2 python 接口 + +# 安装 +## 依赖 +- python>=3.8 +- cyclonedds==0.10.2 +- numpy +- opencv-python + +## 安装 unitree_sdk2_python +在终端中执行: +```bash +cd ~ +sudo apt install python3-pip +git clone https://github.com/unitreerobotics/unitree_sdk2_python.git +cd unitree_sdk2_python +pip3 install -e . +``` +## FAQ +##### 1. `pip3 install -e .` 遇到报错 +```bash +Could not locate cyclonedds. Try to set CYCLONEDDS_HOME or CMAKE_PREFIX_PATH +``` +该错误提示找不到 cyclonedds 路径。首先编译安装cyclonedds: +```bash +cd ~ +git clone https://github.com/eclipse-cyclonedds/cyclonedds -b releases/0.10.x +cd cyclonedds && mkdir build install && cd build +cmake .. -DCMAKE_INSTALL_PREFIX=../install +cmake --build . --target install +``` +进入 unitree_sdk2_python 目录,设置 `CYCLONEDDS_HOME` 为刚刚编译好的 cyclonedds 所在路径,再安装 unitree_sdk2_python +```bash +cd ~/unitree_sdk2_python +export CYCLONEDDS_HOME="~/cyclonedds/install" +pip3 install -e . +``` + +详细见: +https://pypi.org/project/cyclonedds/#installing-with-pre-built-binaries + +# 使用 +python sdk2 接口与 unitree_skd2的接口保持一致,通过请求响应或订阅发布topic实现机器人的状态获取和控制。相应的例程位于`/example`目录下。在运行例程前,需要根据文档 https://support.unitree.com/home/zh/developer/Quick_start 配置好机器人的网络连接。 +## DDS通讯 +在终端中执行: +```bash +python3 ./example/helloworld/publisher.py +``` +打开新的终端,执行: +```bash +python3 ./example/helloworld/subscriber.py +``` +可以看到终端输出的数据信息。`publisher.py` 和 `subscriber.py` 传输的数据定义在 `user_data.py` 中,用户可以根据需要自行定义需要传输的数据结构。 + +## 高层状态和控制 +高层接口的数据结构和控制方式与unitree_sdk2一致。具体可见:https://support.unitree.com/home/zh/developer/sports_services +### 高层状态 +终端中执行: +```bash +python3 ./example/high_level/read_highstate.py enp2s0 +``` +其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。 +### 高层控制 +终端中执行: +```bash +python3 ./example/high_level/sportmode_test.py enp2s0 +``` +其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。 +该例程提供了几种测试方法,可根据测试需要选择: +```python +test.StandUpDown() # 站立趴下 +# test.VelocityMove() # 速度控制 +# test.BalanceAttitude() # 姿态控制 +# test.TrajectoryFollow() # 轨迹跟踪 +# test.SpecialMotions() # 特殊动作 + +``` +## 底层状态和控制 +底层接口的数据结构和控制方式与unitree_sdk2一致。具体可见:https://support.unitree.com/home/zh/developer/Basic_services +### 底层状态 +终端中执行: +```bash +python3 ./example/low_level/lowlevel_control.py enp2s0 +``` +其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。程序会输出右前腿hip关节的状态、IMU和电池电压信息。 + +### 底层电机控制 +首先使用 app 关闭高层运动服务(sport_mode),否则会导致指令冲突。 +终端中执行: +```bash +python3 ./example/low_level/lowlevel_control.py enp2s0 +``` +其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。左后腿 hip 关节会保持在0角度 (安全起见,这里设置 kp=10, kd=1),左后腿 calf 关节将持续输出 1Nm 的转矩。 + +## 遥控器状态获取 +终端中执行: +```bash +python3 ./example/wireless_controller/wireless_controller.py enp2s0 +``` +其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。 +终端将输出每一个按键的状态。对于遥控器按键的定义和数据结构可见: https://support.unitree.com/home/zh/developer/Get_remote_control_status + +## 前置摄像头 +使用opencv获取前置摄像头(确保在有图形界面的系统下运行, 按 ESC 退出程序): +```bash +python3 ./example/front_camera/camera_opencv.py enp2s0 +``` +其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。 + +## 避障开关 +```bash +python3 ./example/obstacles_avoid_switch/obstacles_avoid_switch.py enp2s0 +``` +其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。机器人将循环开启和关闭避障功能。关于避障服务,详细见 https://support.unitree.com/home/zh/developer/ObstaclesAvoidClient + +## 灯光音量控制 +```bash +python3 ./example/vui_client/vui_client_example.py enp2s0 +``` +其中 `enp2s0` 为机器人所连接的网卡名称,请根据实际情况修改。机器人将循环调节音量和灯光亮度。该接口详细见 https://support.unitree.com/home/zh/developer/VuiClient diff --git a/external_dependencies/unitree_sdk2_python/README.md b/external_dependencies/unitree_sdk2_python/README.md new file mode 100644 index 0000000..fb799c2 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/README.md @@ -0,0 +1,118 @@ +# unitree_sdk2_python +Python interface for unitree sdk2 + +# Installation +## Dependencies +- Python >= 3.8 +- cyclonedds == 0.10.2 +- numpy +- opencv-python +## Install unitree_sdk2_python + +```bash +pip install unitree_sdk2py +``` + +### Installing from source +Execute the following commands in the terminal: +```bash +cd ~ +sudo apt install python3-pip +git clone https://github.com/unitreerobotics/unitree_sdk2_python.git +cd unitree_sdk2_python +pip3 install -e . +``` +## FAQ +##### 1. Error when `pip3 install -e .`: +```bash +Could not locate cyclonedds. Try to set CYCLONEDDS_HOME or CMAKE_PREFIX_PATH +``` +This error mentions that the cyclonedds path could not be found. First compile and install cyclonedds: + +```bash +cd ~ +git clone https://github.com/eclipse-cyclonedds/cyclonedds -b releases/0.10.x +cd cyclonedds && mkdir build install && cd build +cmake .. -DCMAKE_INSTALL_PREFIX=../install +cmake --build . --target install +``` +Enter the unitree_sdk2_python directory, set `CYCLONEDDS_HOME` to the path of the cyclonedds you just compiled, and then install unitree_sdk2_python. +```bash +cd ~/unitree_sdk2_python +export CYCLONEDDS_HOME="~/cyclonedds/install" +pip3 install -e . +``` +For details, see: https://pypi.org/project/cyclonedds/#installing-with-pre-built-binaries + +# Usage +The Python sdk2 interface maintains consistency with the unitree_sdk2 interface, achieving robot status acquisition and control through request-response or topic subscription/publishing. Example programs are located in the `/example` directory. Before running the examples, configure the robot's network connection as per the instructions in the document at https://support.unitree.com/home/en/developer/Quick_start. +## DDS Communication +In the terminal, execute: +```bash +python3 ./example/helloworld/publisher.py +``` +Open a new terminal and execute: +```bash +python3 ./example/helloworld/subscriber.py +``` +You will see the data output in the terminal. The data structure transmitted between `publisher.py` and `subscriber.py` is defined in `user_data.py`, and users can define the required data structure as needed. +## High-Level Status and Control +The high-level interface maintains consistency with unitree_sdk2 in terms of data structure and control methods. For detailed information, refer to https://support.unitree.com/home/en/developer/sports_services. +### High-Level Status +Execute the following command in the terminal: +```bash +python3 ./example/high_level/read_highstate.py enp2s0 +``` +Replace `enp2s0` with the name of the network interface to which the robot is connected,. +### High-Level Control +Execute the following command in the terminal: +```bash +python3 ./example/high_level/sportmode_test.py enp2s0 +``` +Replace `enp2s0` with the name of the network interface to which the robot is connected. This example program provides several test methods, and you can choose the required tests as follows: +```python +test.StandUpDown() # Stand up and lie down +# test.VelocityMove() # Velocity control +# test.BalanceAttitude() # Attitude control +# test.TrajectoryFollow() # Trajectory tracking +# test.SpecialMotions() # Special motions +``` +## Low-Level Status and Control +The low-level interface maintains consistency with unitree_sdk2 in terms of data structure and control methods. For detailed information, refer to https://support.unitree.com/home/en/developer/Basic_services. +### Low-Level Status +Execute the following command in the terminal: +```bash +python3 ./example/low_level/lowlevel_control.py enp2s0 +``` +Replace `enp2s0` with the name of the network interface to which the robot is connected. The program will output the state of the right front leg hip joint, IMU, and battery voltage. +### Low-Level Motor Control +First, use the app to turn off the high-level motion service (sport_mode) to prevent conflicting instructions. +Execute the following command in the terminal: +```bash +python3 ./example/low_level/lowlevel_control.py enp2s0 +``` +Replace `enp2s0` with the name of the network interface to which the robot is connected. The left hind leg hip joint will maintain a 0-degree position (for safety, set kp=10, kd=1), and the left hind leg calf joint will continuously output 1Nm of torque. +## Wireless Controller Status +Execute the following command in the terminal: +```bash +python3 ./example/wireless_controller/wireless_controller.py enp2s0 +``` +Replace `enp2s0` with the name of the network interface to which the robot is connected. The terminal will output the status of each key. For the definition and data structure of the remote control keys, refer to https://support.unitree.com/home/en/developer/Get_remote_control_status. +## Front Camera +Use OpenCV to obtain the front camera (ensure to run on a system with a graphical interface, and press ESC to exit the program): +```bash +python3 ./example/front_camera/camera_opencv.py enp2s0 +``` +Replace `enp2s0` with the name of the network interface to which the robot is connected. + +## Obstacle Avoidance Switch +```bash +python3 ./example/obstacles_avoid_switch/obstacles_avoid_switch.py enp2s0 +``` +Replace `enp2s0` with the name of the network interface to which the robot is connected. The robot will cycle obstacle avoidance on and off. For details on the obstacle avoidance service, see https://support.unitree.com/home/en/developer/ObstaclesAvoidClient + +## Light and volume control +```bash +python3 ./example/vui_client/vui_client_example.py enp2s0 +``` +Replace `enp2s0` with the name of the network interface to which the robot is connected.T he robot will cycle the volume and light brightness. The interface is detailed at https://support.unitree.com/home/en/developer/VuiClient \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/example/b2/camera/camera_opencv.py b/external_dependencies/unitree_sdk2_python/example/b2/camera/camera_opencv.py new file mode 100644 index 0000000..3fafcef --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/b2/camera/camera_opencv.py @@ -0,0 +1,51 @@ +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.b2.front_video.front_video_client import FrontVideoClient +from unitree_sdk2py.b2.back_video.back_video_client import BackVideoClient +import cv2 +import numpy as np +import sys + +def display_image(window_name, data): + # If data is a list, we need to convert it to a bytes object + if isinstance(data, list): + data = bytes(data) + + # Now convert to numpy image + image_data = np.frombuffer(data, dtype=np.uint8) + image = cv2.imdecode(image_data, cv2.IMREAD_COLOR) + if image is not None: + cv2.imshow(window_name, image) + +if __name__ == "__main__": + if len(sys.argv) > 1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + frontCameraClient = FrontVideoClient() # Create a front camera video client + frontCameraClient.SetTimeout(3.0) + frontCameraClient.Init() + + backCameraClient = BackVideoClient() # Create a back camera video client + backCameraClient.SetTimeout(3.0) + backCameraClient.Init() + + # Loop to continuously fetch images + while True: + # Get front camera image + front_code, front_data = frontCameraClient.GetImageSample() + if front_code == 0: + display_image("Front Camera", front_data) + + # Get back camera image + back_code, back_data = backCameraClient.GetImageSample() + if back_code == 0: + display_image("Back Camera", back_data) + + # Press ESC to stop + if cv2.waitKey(20) == 27: + break + + # Clean up windows + cv2.destroyAllWindows() + diff --git a/external_dependencies/unitree_sdk2_python/example/b2/camera/capture_image.py b/external_dependencies/unitree_sdk2_python/example/b2/camera/capture_image.py new file mode 100644 index 0000000..dcfaad6 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/b2/camera/capture_image.py @@ -0,0 +1,51 @@ +import time +import os +import sys + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.b2.front_video.front_video_client import FrontVideoClient +from unitree_sdk2py.b2.back_video.back_video_client import BackVideoClient + +if __name__ == "__main__": + if len(sys.argv) > 1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + # 创建前置相机客户端 + front_client = FrontVideoClient() + front_client.SetTimeout(3.0) + front_client.Init() + + # 创建后置相机客户端 + back_client = BackVideoClient() + back_client.SetTimeout(3.0) + back_client.Init() + + print("##################Get Front Camera Image###################") + # 获取前置相机图像 + front_code, front_data = front_client.GetImageSample() + + if front_code != 0: + print("Get front camera image error. Code:", front_code) + else: + front_image_name = "./front_img.jpg" + print("Front Image Saved as:", front_image_name) + + with open(front_image_name, "+wb") as f: + f.write(bytes(front_data)) + + print("##################Get Back Camera Image###################") + # 获取后置相机图像 + back_code, back_data = back_client.GetImageSample() + + if back_code != 0: + print("Get back camera image error. Code:", back_code) + else: + back_image_name = "./back_img.jpg" + print("Back Image Saved as:", back_image_name) + + with open(back_image_name, "+wb") as f: + f.write(bytes(back_data)) + + time.sleep(1) diff --git a/external_dependencies/unitree_sdk2_python/example/b2/high_level/b2_sport_client.py b/external_dependencies/unitree_sdk2_python/example/b2/high_level/b2_sport_client.py new file mode 100644 index 0000000..695d28e --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/b2/high_level/b2_sport_client.py @@ -0,0 +1,105 @@ +import time +import sys +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.b2.sport.sport_client import SportClient +import math +from dataclasses import dataclass + +@dataclass +class TestOption: + name: str + id: int + +option_list = [ + TestOption(name="damp", id=0), + TestOption(name="stand_up", id=1), + TestOption(name="stand_down", id=2), + TestOption(name="move forward", id=3), + TestOption(name="move lateral", id=4), + TestOption(name="move rotate", id=5), + TestOption(name="stop_move", id=6), + TestOption(name="switch_gait", id=7), + TestOption(name="switch_gait", id=8), + TestOption(name="recovery", id=9), + TestOption(name="balanced stand", id=10) +] + +class UserInterface: + def __init__(self): + self.test_option_ = None + + def convert_to_int(self, input_str): + try: + return int(input_str) + except ValueError: + return None + + def terminal_handle(self): + input_str = input("Enter id or name: \n") + + if input_str == "list": + self.test_option_.name = None + self.test_option_.id = None + for option in option_list: + print(f"{option.name}, id: {option.id}") + return + + for option in option_list: + if input_str == option.name or self.convert_to_int(input_str) == option.id: + self.test_option_.name = option.name + self.test_option_.id = option.id + print(f"Test: {self.test_option_.name}, test_id: {self.test_option_.id}") + return + + print("No matching test option found.") + +if __name__ == "__main__": + + if len(sys.argv) < 2: + print(f"Usage: python3 {sys.argv[0]} networkInterface") + sys.exit(-1) + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + ChannelFactoryInitialize(0, sys.argv[1]) + + test_option = TestOption(name=None, id=None) + user_interface = UserInterface() + user_interface.test_option_ = test_option + + sport_client = SportClient() + sport_client.SetTimeout(10.0) + sport_client.Init() + + print("Input \"list\" to list all test option ...") + + while True: + user_interface.terminal_handle() + + print(f"Updated Test Option: Name = {test_option.name}, ID = {test_option.id}") + + if test_option.id == 0: + sport_client.Damp() + elif test_option.id == 1: + sport_client.StandUp() + elif test_option.id == 2: + sport_client.StandDown() + elif test_option.id == 3: + sport_client.Move(0.3,0,0) + elif test_option.id == 4: + sport_client.Move(0,0.3,0) + elif test_option.id == 5: + sport_client.Move(0,0,0.5) + elif test_option.id == 6: + sport_client.StopMove() + elif test_option.id == 7: + sport_client.SwitchGait(0) + elif test_option.id == 8: + sport_client.SwitchGait(1) + elif test_option.id == 9: + sport_client.RecoveryStand() + elif test_option.id == 10: + sport_client.BalanceStand() + + time.sleep(1) diff --git a/external_dependencies/unitree_sdk2_python/example/b2/low_level/b2_stand_example.py b/external_dependencies/unitree_sdk2_python/example/b2/low_level/b2_stand_example.py new file mode 100644 index 0000000..a091630 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/b2/low_level/b2_stand_example.py @@ -0,0 +1,175 @@ +import time +import sys + +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowCmd_ +from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowState_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowCmd_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_ +from unitree_sdk2py.utils.thread import RecurrentThread +import unitree_legged_const as b2 +from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient +from unitree_sdk2py.b2.sport.sport_client import SportClient + +from unitree_sdk2py.utils.crc import CRC + +class Custom: + def __init__(self): + self.Kp = 1000.0 + self.Kd = 10.0 + self.time_consume = 0 + self.rate_count = 0 + self.sin_count = 0 + self.motiontime = 0 + self.dt = 0.002 + + self.low_cmd = unitree_go_msg_dds__LowCmd_() + self.low_state = None + + self.targetPos_1 = [0.0, 1.36, -2.65, 0.0, 1.36, -2.65, + -0.2, 1.36, -2.65, 0.2, 1.36, -2.65] + + self.targetPos_2 = [0.0, 0.67, -1.3, 0.0, 0.67, -1.3, + 0.0, 0.67, -1.3, 0.0, 0.67, -1.3] + + self.targetPos_3 = [-0.5, 1.36, -2.65, 0.5, 1.36, -2.65, + -0.5, 1.36, -2.65, 0.5, 1.36, -2.65] + + self.startPos = [0.0] * 12 + self.duration_1 = 500 + self.duration_2 = 900 + self.duration_3 = 1000 + self.duration_4 = 900 + self.percent_1 = 0 + self.percent_2 = 0 + self.percent_3 = 0 + self.percent_4 = 0 + + self.firstRun = True + self.done = False + + # thread handling + self.lowCmdWriteThreadPtr = None + + self.crc = CRC() + + def Init(self): + self.InitLowCmd() + + # create publisher # + self.lowcmd_publisher = ChannelPublisher("rt/lowcmd", LowCmd_) + self.lowcmd_publisher.Init() + + # create subscriber # + self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_) + self.lowstate_subscriber.Init(self.LowStateMessageHandler, 10) + + self.sc = SportClient() + self.sc.SetTimeout(5.0) + self.sc.Init() + + self.msc = MotionSwitcherClient() + self.msc.SetTimeout(5.0) + self.msc.Init() + + status, result = self.msc.CheckMode() + while result['name']: + self.sc.StandDown() + self.msc.ReleaseMode() + status, result = self.msc.CheckMode() + time.sleep(1) + + + def Start(self): + self.lowCmdWriteThreadPtr = RecurrentThread( + interval=0.002, target=self.LowCmdWrite, name="writebasiccmd" + ) + self.lowCmdWriteThreadPtr.Start() + + def InitLowCmd(self): + self.low_cmd.head[0] = 0xFE + self.low_cmd.head[1] = 0xEF + self.low_cmd.level_flag = 0xFF + self.low_cmd.gpio = 0 + for i in range(20): + self.low_cmd.motor_cmd[i].mode = 0x01 + self.low_cmd.motor_cmd[i].q= b2.PosStopF + self.low_cmd.motor_cmd[i].kp = 0 + self.low_cmd.motor_cmd[i].dq = b2.VelStopF + self.low_cmd.motor_cmd[i].kd = 0 + self.low_cmd.motor_cmd[i].tau = 0 + + def LowStateMessageHandler(self, msg: LowState_): + self.low_state = msg + + def LowCmdWrite(self): + + if self.firstRun: + for i in range(12): + self.startPos[i] = self.low_state.motor_state[i].q + self.firstRun = False + + self.percent_1 += 1.0 / self.duration_1 + self.percent_1 = min(self.percent_1, 1) + if self.percent_1 < 1: + for i in range(12): + self.low_cmd.motor_cmd[i].q = (1 - self.percent_1) * self.startPos[i] + self.percent_1 * self.targetPos_1[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if (self.percent_1 == 1) and (self.percent_2 <= 1): + self.percent_2 += 1.0 / self.duration_2 + self.percent_2 = min(self.percent_2, 1) + for i in range(12): + self.low_cmd.motor_cmd[i].q = (1 - self.percent_2) * self.targetPos_1[i] + self.percent_2 * self.targetPos_2[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 < 1): + self.percent_3 += 1.0 / self.duration_3 + self.percent_3 = min(self.percent_3, 1) + for i in range(12): + self.low_cmd.motor_cmd[i].q = self.targetPos_2[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 == 1) and (self.percent_4 <= 1): + self.percent_4 += 1.0 / self.duration_4 + self.percent_4 = min(self.percent_4, 1) + for i in range(12): + self.low_cmd.motor_cmd[i].q = (1 - self.percent_4) * self.targetPos_2[i] + self.percent_4 * self.targetPos_3[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + self.low_cmd.crc = self.crc.Crc(self.low_cmd) + self.lowcmd_publisher.Write(self.low_cmd) + +if __name__ == '__main__': + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + custom = Custom() + custom.Init() + custom.Start() + + while True: + if custom.percent_4 == 1.0: + time.sleep(1) + print("Done!") + sys.exit(-1) + time.sleep(1) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/example/b2/low_level/unitree_legged_const.py b/external_dependencies/unitree_sdk2_python/example/b2/low_level/unitree_legged_const.py new file mode 100644 index 0000000..153a051 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/b2/low_level/unitree_legged_const.py @@ -0,0 +1,20 @@ +LegID = { + "FR_0": 0, # Front right hip + "FR_1": 1, # Front right thigh + "FR_2": 2, # Front right calf + "FL_0": 3, + "FL_1": 4, + "FL_2": 5, + "RR_0": 6, + "RR_1": 7, + "RR_2": 8, + "RL_0": 9, + "RL_1": 10, + "RL_2": 11, +} + +HIGHLEVEL = 0xEE +LOWLEVEL = 0xFF +TRIGERLEVEL = 0xF0 +PosStopF = 2.146e9 +VelStopF = 16000.0 diff --git a/external_dependencies/unitree_sdk2_python/example/b2w/camera/camera_opencv.py b/external_dependencies/unitree_sdk2_python/example/b2w/camera/camera_opencv.py new file mode 100644 index 0000000..3fafcef --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/b2w/camera/camera_opencv.py @@ -0,0 +1,51 @@ +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.b2.front_video.front_video_client import FrontVideoClient +from unitree_sdk2py.b2.back_video.back_video_client import BackVideoClient +import cv2 +import numpy as np +import sys + +def display_image(window_name, data): + # If data is a list, we need to convert it to a bytes object + if isinstance(data, list): + data = bytes(data) + + # Now convert to numpy image + image_data = np.frombuffer(data, dtype=np.uint8) + image = cv2.imdecode(image_data, cv2.IMREAD_COLOR) + if image is not None: + cv2.imshow(window_name, image) + +if __name__ == "__main__": + if len(sys.argv) > 1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + frontCameraClient = FrontVideoClient() # Create a front camera video client + frontCameraClient.SetTimeout(3.0) + frontCameraClient.Init() + + backCameraClient = BackVideoClient() # Create a back camera video client + backCameraClient.SetTimeout(3.0) + backCameraClient.Init() + + # Loop to continuously fetch images + while True: + # Get front camera image + front_code, front_data = frontCameraClient.GetImageSample() + if front_code == 0: + display_image("Front Camera", front_data) + + # Get back camera image + back_code, back_data = backCameraClient.GetImageSample() + if back_code == 0: + display_image("Back Camera", back_data) + + # Press ESC to stop + if cv2.waitKey(20) == 27: + break + + # Clean up windows + cv2.destroyAllWindows() + diff --git a/external_dependencies/unitree_sdk2_python/example/b2w/camera/capture_image.py b/external_dependencies/unitree_sdk2_python/example/b2w/camera/capture_image.py new file mode 100644 index 0000000..dcfaad6 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/b2w/camera/capture_image.py @@ -0,0 +1,51 @@ +import time +import os +import sys + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.b2.front_video.front_video_client import FrontVideoClient +from unitree_sdk2py.b2.back_video.back_video_client import BackVideoClient + +if __name__ == "__main__": + if len(sys.argv) > 1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + # 创建前置相机客户端 + front_client = FrontVideoClient() + front_client.SetTimeout(3.0) + front_client.Init() + + # 创建后置相机客户端 + back_client = BackVideoClient() + back_client.SetTimeout(3.0) + back_client.Init() + + print("##################Get Front Camera Image###################") + # 获取前置相机图像 + front_code, front_data = front_client.GetImageSample() + + if front_code != 0: + print("Get front camera image error. Code:", front_code) + else: + front_image_name = "./front_img.jpg" + print("Front Image Saved as:", front_image_name) + + with open(front_image_name, "+wb") as f: + f.write(bytes(front_data)) + + print("##################Get Back Camera Image###################") + # 获取后置相机图像 + back_code, back_data = back_client.GetImageSample() + + if back_code != 0: + print("Get back camera image error. Code:", back_code) + else: + back_image_name = "./back_img.jpg" + print("Back Image Saved as:", back_image_name) + + with open(back_image_name, "+wb") as f: + f.write(bytes(back_data)) + + time.sleep(1) diff --git a/external_dependencies/unitree_sdk2_python/example/b2w/high_level/b2w_sport_client.py b/external_dependencies/unitree_sdk2_python/example/b2w/high_level/b2w_sport_client.py new file mode 100644 index 0000000..e324f75 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/b2w/high_level/b2w_sport_client.py @@ -0,0 +1,101 @@ +import time +import sys +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.b2.sport.sport_client import SportClient +import math +from dataclasses import dataclass + +@dataclass +class TestOption: + name: str + id: int + +option_list = [ + TestOption(name="damp", id=0), + TestOption(name="stand_up", id=1), + TestOption(name="stand_down", id=2), + TestOption(name="move forward", id=3), + TestOption(name="move lateral", id=4), + TestOption(name="move rotate", id=5), + TestOption(name="stop_move", id=6), + TestOption(name="switch_gait", id=7), + TestOption(name="switch_gait", id=8), + TestOption(name="recovery", id=9) +] + +class UserInterface: + def __init__(self): + self.test_option_ = None + + def convert_to_int(self, input_str): + try: + return int(input_str) + except ValueError: + return None + + def terminal_handle(self): + input_str = input("Enter id or name: \n") + + if input_str == "list": + self.test_option_.name = None + self.test_option_.id = None + for option in option_list: + print(f"name: {option.name}, id: {option.id}") + return + + for option in option_list: + if input_str == option.name or self.convert_to_int(input_str) == option.id: + self.test_option_.name = option.name + self.test_option_.id = option.id + print(f"Test: {self.test_option_.name}, test_id: {self.test_option_.id}") + return + + print("No matching test option found.") + +if __name__ == "__main__": + if len(sys.argv) < 2: + print(f"Usage: python3 {sys.argv[0]} networkInterface") + sys.exit(-1) + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + ChannelFactoryInitialize(0, sys.argv[1]) + + test_option = TestOption(name=None, id=None) + user_interface = UserInterface() + user_interface.test_option_ = test_option + + sport_client = SportClient() + sport_client.SetTimeout(10.0) + sport_client.Init() + + print("Input \"list\" to list all test option ...") + + while True: + user_interface.terminal_handle() + + print(f"Updated Test Option: Name = {test_option.name}, ID = {test_option.id}\n") + + if test_option.id == 0: + sport_client.Damp() + elif test_option.id == 1: + sport_client.StandUp() + elif test_option.id == 2: + sport_client.StandDown() + elif test_option.id == 3: + sport_client.Move(0.3,0,0) + elif test_option.id == 4: + sport_client.Move(0,0.3,0) + elif test_option.id == 5: + sport_client.Move(0,0,0.5) + elif test_option.id == 6: + sport_client.StopMove() + elif test_option.id == 7: + sport_client.SwitchGait(0) + elif test_option.id == 8: + sport_client.SwitchGait(1) + elif test_option.id == 9: + sport_client.RecoveryStand() + + time.sleep(1) diff --git a/external_dependencies/unitree_sdk2_python/example/b2w/low_level/b2w_stand_example.py b/external_dependencies/unitree_sdk2_python/example/b2w/low_level/b2w_stand_example.py new file mode 100644 index 0000000..b535059 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/b2w/low_level/b2w_stand_example.py @@ -0,0 +1,196 @@ +import time +import sys + +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowCmd_ +from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowState_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowCmd_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_ +from unitree_sdk2py.utils.crc import CRC +from unitree_sdk2py.utils.thread import RecurrentThread +import unitree_legged_const as b2w +from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient +from unitree_sdk2py.b2.sport.sport_client import SportClient + +class Custom: + def __init__(self): + self.Kp = 1000.0 + self.Kd = 10.0 + self.time_consume = 0 + self.rate_count = 0 + self.sin_count = 0 + self.motiontime = 0 + self.dt = 0.002 + + self.low_cmd = unitree_go_msg_dds__LowCmd_() + self.low_state = None + + self.targetPos_1 = [0.0, 1.36, -2.65, 0.0, 1.36, -2.65, + -0.2, 1.36, -2.65, 0.2, 1.36, -2.65] + + self.targetPos_2 = [0.0, 0.67, -1.3, 0.0, 0.67, -1.3, + 0.0, 0.67, -1.3, 0.0, 0.67, -1.3] + + self.targetPos_3 = [-0.65, 1.36, -2.65, 0.65, 1.36, -2.65, + -0.65, 1.36, -2.65, 0.65, 1.36, -2.65] + + self.startPos = [0.0] * 12 + self.duration_1 = 800 + self.duration_2 = 800 + self.duration_3 = 2000 + self.duration_4 = 1500 + self.percent_1 = 0 + self.percent_2 = 0 + self.percent_3 = 0 + self.percent_4 = 0 + + self.firstRun = True + self.done = False + + # thread handling + self.lowCmdWriteThreadPtr = None + + self.crc = CRC() + + def Init(self): + self.InitLowCmd() + + # create publisher # + self.lowcmd_publisher = ChannelPublisher("rt/lowcmd", LowCmd_) + self.lowcmd_publisher.Init() + + # create subscriber # + self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_) + self.lowstate_subscriber.Init(self.LowStateMessageHandler, 10) + + self.sc = SportClient() + self.sc.SetTimeout(5.0) + self.sc.Init() + + self.msc = MotionSwitcherClient() + self.msc.SetTimeout(5.0) + self.msc.Init() + + status, result = self.msc.CheckMode() + while result['name']: + self.sc.StandDown() + self.msc.ReleaseMode() + status, result = self.msc.CheckMode() + time.sleep(1) + + def Start(self): + self.lowCmdWriteThreadPtr = RecurrentThread( + name="writebasiccmd", interval=self.dt, target=self.LowCmdWrite, + ) + self.lowCmdWriteThreadPtr.Start() + + def InitLowCmd(self): + self.low_cmd.head[0] = 0xFE + self.low_cmd.head[1] = 0xEF + self.low_cmd.level_flag = 0xFF + self.low_cmd.gpio = 0 + for i in range(20): + self.low_cmd.motor_cmd[i].mode = 0x01 # (PMSM) mode + self.low_cmd.motor_cmd[i].q = b2w.PosStopF + self.low_cmd.motor_cmd[i].kp = 0 + self.low_cmd.motor_cmd[i].dq = b2w.VelStopF + self.low_cmd.motor_cmd[i].kd = 0 + self.low_cmd.motor_cmd[i].tau = 0 + + def LowStateMessageHandler(self, msg: LowState_): + self.low_state = msg + + def LowCmdWrite(self): + if self.firstRun: + for i in range(12): + self.startPos[i] = self.low_state.motor_state[i].q + self.firstRun = False + + self.percent_1 += 1.0 / self.duration_1 + self.percent_1 = min(self.percent_1, 1) + if self.percent_1 < 1: + for i in range(12): + self.low_cmd.motor_cmd[i].q = (1 - self.percent_1) * self.startPos[i] + self.percent_1 * self.targetPos_1[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if (self.percent_1 == 1) and (self.percent_2 <= 1): + self.percent_2 += 1.0 / self.duration_2 + self.percent_2 = min(self.percent_2, 1) + for i in range(12): + self.low_cmd.motor_cmd[i].q = (1 - self.percent_2) * self.targetPos_1[i] + self.percent_2 * self.targetPos_2[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 < 1): + self.percent_3 += 1.0 / self.duration_3 + self.percent_3 = min(self.percent_3, 1) + for i in range(12): + self.low_cmd.motor_cmd[i].q = self.targetPos_2[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if self.percent_3 < 0.4: + for i in range(12, 16): + self.low_cmd.motor_cmd[i].q = 0 + self.low_cmd.motor_cmd[i].kp = 0 + self.low_cmd.motor_cmd[i].dq = 3 + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if 0.4 <= self.percent_3 < 0.8: + for i in range(12, 16): + self.low_cmd.motor_cmd[i].q = 0 + self.low_cmd.motor_cmd[i].kp = 0 + self.low_cmd.motor_cmd[i].dq = -3 + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if self.percent_3 >= 0.8: + for i in range(12, 16): + self.low_cmd.motor_cmd[i].q = 0 + self.low_cmd.motor_cmd[i].kp = 0 + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 == 1) and (self.percent_4 <= 1): + self.percent_4 += 1.0 / self.duration_4 + self.percent_4 = min(self.percent_4, 1) + for i in range(12): + self.low_cmd.motor_cmd[i].q = (1 - self.percent_4) * self.targetPos_2[i] + self.percent_4 * self.targetPos_3[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + self.low_cmd.crc = self.crc.Crc(self.low_cmd) + self.lowcmd_publisher.Write(self.low_cmd) + +if __name__ == '__main__': + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + custom = Custom() + custom.Init() + custom.Start() + + while True: + if custom.percent_4 == 1.0: + time.sleep(1) + print("Done!") + sys.exit(-1) + time.sleep(1) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/example/b2w/low_level/unitree_legged_const.py b/external_dependencies/unitree_sdk2_python/example/b2w/low_level/unitree_legged_const.py new file mode 100644 index 0000000..7943e31 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/b2w/low_level/unitree_legged_const.py @@ -0,0 +1,24 @@ +LegID = { + "FR_0": 0, # Front right hip + "FR_1": 1, # Front right thigh + "FR_2": 2, # Front right calf + "FL_0": 3, + "FL_1": 4, + "FL_2": 5, + "RR_0": 6, + "RR_1": 7, + "RR_2": 8, + "RL_0": 9, + "RL_1": 10, + "RL_2": 11, + "FR_w": 12, # Front right wheel + "FL_w": 13, # Front left wheel + "RR_w": 14, # Rear right wheel + "RL_w": 15, # Rear left wheel +} + +HIGHLEVEL = 0xEE +LOWLEVEL = 0xFF +TRIGERLEVEL = 0xF0 +PosStopF = 2.146e9 +VelStopF = 16000.0 diff --git a/external_dependencies/unitree_sdk2_python/example/g1/audio/g1_audio_client_example.py b/external_dependencies/unitree_sdk2_python/example/g1/audio/g1_audio_client_example.py new file mode 100644 index 0000000..6e0cc25 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/g1/audio/g1_audio_client_example.py @@ -0,0 +1,44 @@ +import time +import sys +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.g1.audio.g1_audio_client import AudioClient +from unitree_sdk2py.g1.loco.g1_loco_client import LocoClient + +if __name__ == "__main__": + if len(sys.argv) < 2: + print(f"Usage: python3 {sys.argv[0]} networkInterface") + sys.exit(-1) + + ChannelFactoryInitialize(0, sys.argv[1]) + + audio_client = AudioClient() + audio_client.SetTimeout(10.0) + audio_client.Init() + + sport_client = LocoClient() + sport_client.SetTimeout(10.0) + sport_client.Init() + + ret = audio_client.GetVolume() + print("debug GetVolume: ",ret) + + audio_client.SetVolume(85) + + ret = audio_client.GetVolume() + print("debug GetVolume: ",ret) + + sport_client.WaveHand() + + audio_client.TtsMaker("大家好!我是宇树科技人形机器人。语音开发测试例程运行成功! 很高兴认识你!",0) + time.sleep(8) + audio_client.TtsMaker("接下来测试灯带开发例程!",0) + time.sleep(1) + audio_client.LedControl(255,0,0) + time.sleep(1) + audio_client.LedControl(0,255,0) + time.sleep(1) + audio_client.LedControl(0,0,255) + + time.sleep(3) + audio_client.TtsMaker("测试完毕,谢谢大家!",0) + diff --git a/external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_arm5_sdk_dds_example.py b/external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_arm5_sdk_dds_example.py new file mode 100644 index 0000000..6090807 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_arm5_sdk_dds_example.py @@ -0,0 +1,192 @@ +import time +import sys + +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowCmd_ +from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowState_ +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowCmd_ +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowState_ +from unitree_sdk2py.utils.crc import CRC +from unitree_sdk2py.utils.thread import RecurrentThread +from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient + +import numpy as np + +kPi = 3.141592654 +kPi_2 = 1.57079632 + +class G1JointIndex: + # Left leg + LeftHipPitch = 0 + LeftHipRoll = 1 + LeftHipYaw = 2 + LeftKnee = 3 + LeftAnklePitch = 4 + LeftAnkleB = 4 + LeftAnkleRoll = 5 + LeftAnkleA = 5 + + # Right leg + RightHipPitch = 6 + RightHipRoll = 7 + RightHipYaw = 8 + RightKnee = 9 + RightAnklePitch = 10 + RightAnkleB = 10 + RightAnkleRoll = 11 + RightAnkleA = 11 + + WaistYaw = 12 + WaistRoll = 13 # NOTE: INVALID for g1 23dof/29dof with waist locked + WaistA = 13 # NOTE: INVALID for g1 23dof/29dof with waist locked + WaistPitch = 14 # NOTE: INVALID for g1 23dof/29dof with waist locked + WaistB = 14 # NOTE: INVALID for g1 23dof/29dof with waist locked + + # Left arm + LeftShoulderPitch = 15 + LeftShoulderRoll = 16 + LeftShoulderYaw = 17 + LeftElbow = 18 + LeftWristRoll = 19 + LeftWristPitch = 20 # NOTE: INVALID for g1 23dof + LeftWristYaw = 21 # NOTE: INVALID for g1 23dof + + # Right arm + RightShoulderPitch = 22 + RightShoulderRoll = 23 + RightShoulderYaw = 24 + RightElbow = 25 + RightWristRoll = 26 + RightWristPitch = 27 # NOTE: INVALID for g1 23dof + RightWristYaw = 28 # NOTE: INVALID for g1 23dof + + kNotUsedJoint = 29 # NOTE: Weight + +class Custom: + def __init__(self): + self.time_ = 0.0 + self.control_dt_ = 0.02 + self.duration_ = 3.0 + self.counter_ = 0 + self.weight = 0. + self.weight_rate = 0.2 + self.kp = 60. + self.kd = 1.5 + self.dq = 0. + self.tau_ff = 0. + self.mode_machine_ = 0 + self.low_cmd = unitree_hg_msg_dds__LowCmd_() + self.low_state = None + self.first_update_low_state = False + self.crc = CRC() + self.done = False + + self.target_pos = [ + 0.0, kPi_2, 0.0, kPi_2, 0.0, + 0.0, -kPi_2, 0.0, kPi_2, 0.0, + 0.0, 0.0, 0.0 + ] + + self.arm_joints = [ + G1JointIndex.LeftShoulderPitch, G1JointIndex.LeftShoulderRoll, + G1JointIndex.LeftShoulderYaw, G1JointIndex.LeftElbow, + G1JointIndex.LeftWristRoll, + G1JointIndex.RightShoulderPitch, G1JointIndex.RightShoulderRoll, + G1JointIndex.RightShoulderYaw, G1JointIndex.RightElbow, + G1JointIndex.RightWristRoll, + G1JointIndex.WaistYaw, + G1JointIndex.WaistRoll, + G1JointIndex.WaistPitch + ] + + def Init(self): + # create publisher # + self.arm_sdk_publisher = ChannelPublisher("rt/arm_sdk", LowCmd_) + self.arm_sdk_publisher.Init() + + # create subscriber # + self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_) + self.lowstate_subscriber.Init(self.LowStateHandler, 10) + + def Start(self): + self.lowCmdWriteThreadPtr = RecurrentThread( + interval=self.control_dt_, target=self.LowCmdWrite, name="control" + ) + while self.first_update_low_state == False: + time.sleep(1) + + if self.first_update_low_state == True: + self.lowCmdWriteThreadPtr.Start() + + def LowStateHandler(self, msg: LowState_): + self.low_state = msg + + if self.first_update_low_state == False: + self.first_update_low_state = True + + def LowCmdWrite(self): + self.time_ += self.control_dt_ + + if self.time_ < self.duration_ : + # [Stage 1]: set robot to zero posture + self.low_cmd.motor_cmd[G1JointIndex.kNotUsedJoint].q = 1 # 1:Enable arm_sdk, 0:Disable arm_sdk + for i,joint in enumerate(self.arm_joints): + ratio = np.clip(self.time_ / self.duration_, 0.0, 1.0) + self.low_cmd.motor_cmd[joint].tau = 0. + self.low_cmd.motor_cmd[joint].q = (1.0 - ratio) * self.low_state.motor_state[joint].q + self.low_cmd.motor_cmd[joint].dq = 0. + self.low_cmd.motor_cmd[joint].kp = self.kp + self.low_cmd.motor_cmd[joint].kd = self.kd + + elif self.time_ < self.duration_ * 3 : + # [Stage 2]: lift arms up + for i,joint in enumerate(self.arm_joints): + ratio = np.clip((self.time_ - self.duration_) / (self.duration_ * 2), 0.0, 1.0) + self.low_cmd.motor_cmd[joint].tau = 0. + self.low_cmd.motor_cmd[joint].q = ratio * self.target_pos[i] + (1.0 - ratio) * self.low_state.motor_state[joint].q + self.low_cmd.motor_cmd[joint].dq = 0. + self.low_cmd.motor_cmd[joint].kp = self.kp + self.low_cmd.motor_cmd[joint].kd = self.kd + + elif self.time_ < self.duration_ * 6 : + # [Stage 3]: set robot back to zero posture + for i,joint in enumerate(self.arm_joints): + ratio = np.clip((self.time_ - self.duration_*3) / (self.duration_ * 3), 0.0, 1.0) + self.low_cmd.motor_cmd[joint].tau = 0. + self.low_cmd.motor_cmd[joint].q = (1.0 - ratio) * self.low_state.motor_state[joint].q + self.low_cmd.motor_cmd[joint].dq = 0. + self.low_cmd.motor_cmd[joint].kp = self.kp + self.low_cmd.motor_cmd[joint].kd = self.kd + + elif self.time_ < self.duration_ * 7 : + # [Stage 4]: release arm_sdk + for i,joint in enumerate(self.arm_joints): + ratio = np.clip((self.time_ - self.duration_*6) / (self.duration_), 0.0, 1.0) + self.low_cmd.motor_cmd[G1JointIndex.kNotUsedJoint].q = (1 - ratio) # 1:Enable arm_sdk, 0:Disable arm_sdk + + else: + self.done = True + + self.low_cmd.crc = self.crc.Crc(self.low_cmd) + self.arm_sdk_publisher.Write(self.low_cmd) + +if __name__ == '__main__': + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + custom = Custom() + custom.Init() + custom.Start() + + while True: + time.sleep(1) + if custom.done: + print("Done!") + sys.exit(-1) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_arm7_sdk_dds_example.py b/external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_arm7_sdk_dds_example.py new file mode 100644 index 0000000..6ced14f --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_arm7_sdk_dds_example.py @@ -0,0 +1,194 @@ +import time +import sys + +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowCmd_ +from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowState_ +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowCmd_ +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowState_ +from unitree_sdk2py.utils.crc import CRC +from unitree_sdk2py.utils.thread import RecurrentThread +from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient + +import numpy as np + +kPi = 3.141592654 +kPi_2 = 1.57079632 + +class G1JointIndex: + # Left leg + LeftHipPitch = 0 + LeftHipRoll = 1 + LeftHipYaw = 2 + LeftKnee = 3 + LeftAnklePitch = 4 + LeftAnkleB = 4 + LeftAnkleRoll = 5 + LeftAnkleA = 5 + + # Right leg + RightHipPitch = 6 + RightHipRoll = 7 + RightHipYaw = 8 + RightKnee = 9 + RightAnklePitch = 10 + RightAnkleB = 10 + RightAnkleRoll = 11 + RightAnkleA = 11 + + WaistYaw = 12 + WaistRoll = 13 # NOTE: INVALID for g1 23dof/29dof with waist locked + WaistA = 13 # NOTE: INVALID for g1 23dof/29dof with waist locked + WaistPitch = 14 # NOTE: INVALID for g1 23dof/29dof with waist locked + WaistB = 14 # NOTE: INVALID for g1 23dof/29dof with waist locked + + # Left arm + LeftShoulderPitch = 15 + LeftShoulderRoll = 16 + LeftShoulderYaw = 17 + LeftElbow = 18 + LeftWristRoll = 19 + LeftWristPitch = 20 # NOTE: INVALID for g1 23dof + LeftWristYaw = 21 # NOTE: INVALID for g1 23dof + + # Right arm + RightShoulderPitch = 22 + RightShoulderRoll = 23 + RightShoulderYaw = 24 + RightElbow = 25 + RightWristRoll = 26 + RightWristPitch = 27 # NOTE: INVALID for g1 23dof + RightWristYaw = 28 # NOTE: INVALID for g1 23dof + + kNotUsedJoint = 29 # NOTE: Weight + +class Custom: + def __init__(self): + self.time_ = 0.0 + self.control_dt_ = 0.02 + self.duration_ = 3.0 + self.counter_ = 0 + self.weight = 0. + self.weight_rate = 0.2 + self.kp = 60. + self.kd = 1.5 + self.dq = 0. + self.tau_ff = 0. + self.mode_machine_ = 0 + self.low_cmd = unitree_hg_msg_dds__LowCmd_() + self.low_state = None + self.first_update_low_state = False + self.crc = CRC() + self.done = False + + self.target_pos = [ + 0., kPi_2, 0., kPi_2, 0., 0., 0., + 0., -kPi_2, 0., kPi_2, 0., 0., 0., + 0, 0, 0 + ] + + self.arm_joints = [ + G1JointIndex.LeftShoulderPitch, G1JointIndex.LeftShoulderRoll, + G1JointIndex.LeftShoulderYaw, G1JointIndex.LeftElbow, + G1JointIndex.LeftWristRoll, G1JointIndex.LeftWristPitch, + G1JointIndex.LeftWristYaw, + G1JointIndex.RightShoulderPitch, G1JointIndex.RightShoulderRoll, + G1JointIndex.RightShoulderYaw, G1JointIndex.RightElbow, + G1JointIndex.RightWristRoll, G1JointIndex.RightWristPitch, + G1JointIndex.RightWristYaw, + G1JointIndex.WaistYaw, + G1JointIndex.WaistRoll, + G1JointIndex.WaistPitch + ] + + def Init(self): + # create publisher # + self.arm_sdk_publisher = ChannelPublisher("rt/arm_sdk", LowCmd_) + self.arm_sdk_publisher.Init() + + # create subscriber # + self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_) + self.lowstate_subscriber.Init(self.LowStateHandler, 10) + + def Start(self): + self.lowCmdWriteThreadPtr = RecurrentThread( + interval=self.control_dt_, target=self.LowCmdWrite, name="control" + ) + while self.first_update_low_state == False: + time.sleep(1) + + if self.first_update_low_state == True: + self.lowCmdWriteThreadPtr.Start() + + def LowStateHandler(self, msg: LowState_): + self.low_state = msg + + if self.first_update_low_state == False: + self.first_update_low_state = True + + def LowCmdWrite(self): + self.time_ += self.control_dt_ + + if self.time_ < self.duration_ : + # [Stage 1]: set robot to zero posture + self.low_cmd.motor_cmd[G1JointIndex.kNotUsedJoint].q = 1 # 1:Enable arm_sdk, 0:Disable arm_sdk + for i,joint in enumerate(self.arm_joints): + ratio = np.clip(self.time_ / self.duration_, 0.0, 1.0) + self.low_cmd.motor_cmd[joint].tau = 0. + self.low_cmd.motor_cmd[joint].q = (1.0 - ratio) * self.low_state.motor_state[joint].q + self.low_cmd.motor_cmd[joint].dq = 0. + self.low_cmd.motor_cmd[joint].kp = self.kp + self.low_cmd.motor_cmd[joint].kd = self.kd + + elif self.time_ < self.duration_ * 3 : + # [Stage 2]: lift arms up + for i,joint in enumerate(self.arm_joints): + ratio = np.clip((self.time_ - self.duration_) / (self.duration_ * 2), 0.0, 1.0) + self.low_cmd.motor_cmd[joint].tau = 0. + self.low_cmd.motor_cmd[joint].q = ratio * self.target_pos[i] + (1.0 - ratio) * self.low_state.motor_state[joint].q + self.low_cmd.motor_cmd[joint].dq = 0. + self.low_cmd.motor_cmd[joint].kp = self.kp + self.low_cmd.motor_cmd[joint].kd = self.kd + + elif self.time_ < self.duration_ * 6 : + # [Stage 3]: set robot back to zero posture + for i,joint in enumerate(self.arm_joints): + ratio = np.clip((self.time_ - self.duration_*3) / (self.duration_ * 3), 0.0, 1.0) + self.low_cmd.motor_cmd[joint].tau = 0. + self.low_cmd.motor_cmd[joint].q = (1.0 - ratio) * self.low_state.motor_state[joint].q + self.low_cmd.motor_cmd[joint].dq = 0. + self.low_cmd.motor_cmd[joint].kp = self.kp + self.low_cmd.motor_cmd[joint].kd = self.kd + + elif self.time_ < self.duration_ * 7 : + # [Stage 4]: release arm_sdk + for i,joint in enumerate(self.arm_joints): + ratio = np.clip((self.time_ - self.duration_*6) / (self.duration_), 0.0, 1.0) + self.low_cmd.motor_cmd[G1JointIndex.kNotUsedJoint].q = (1 - ratio) # 1:Enable arm_sdk, 0:Disable arm_sdk + + else: + self.done = True + + self.low_cmd.crc = self.crc.Crc(self.low_cmd) + self.arm_sdk_publisher.Write(self.low_cmd) + +if __name__ == '__main__': + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + custom = Custom() + custom.Init() + custom.Start() + + while True: + time.sleep(1) + if custom.done: + print("Done!") + sys.exit(-1) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_loco_client_example.py b/external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_loco_client_example.py new file mode 100644 index 0000000..55ee059 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/g1/high_level/g1_loco_client_example.py @@ -0,0 +1,117 @@ +import time +import sys +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_go_msg_dds__SportModeState_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import SportModeState_ +from unitree_sdk2py.g1.loco.g1_loco_client import LocoClient +import math +from dataclasses import dataclass + +@dataclass +class TestOption: + name: str + id: int + +option_list = [ + TestOption(name="damp", id=0), + TestOption(name="Squat2StandUp", id=1), + TestOption(name="StandUp2Squat", id=2), + TestOption(name="move forward", id=3), + TestOption(name="move lateral", id=4), + TestOption(name="move rotate", id=5), + TestOption(name="low stand", id=6), + TestOption(name="high stand", id=7), + TestOption(name="zero torque", id=8), + TestOption(name="wave hand1", id=9), # wave hand without turning around + TestOption(name="wave hand2", id=10), # wave hand and trun around + TestOption(name="shake hand", id=11), + TestOption(name="Lie2StandUp", id=12), +] + +class UserInterface: + def __init__(self): + self.test_option_ = None + + def convert_to_int(self, input_str): + try: + return int(input_str) + except ValueError: + return None + + def terminal_handle(self): + input_str = input("Enter id or name: \n") + + if input_str == "list": + self.test_option_.name = None + self.test_option_.id = None + for option in option_list: + print(f"{option.name}, id: {option.id}") + return + + for option in option_list: + if input_str == option.name or self.convert_to_int(input_str) == option.id: + self.test_option_.name = option.name + self.test_option_.id = option.id + print(f"Test: {self.test_option_.name}, test_id: {self.test_option_.id}") + return + + print("No matching test option found.") + +if __name__ == "__main__": + if len(sys.argv) < 2: + print(f"Usage: python3 {sys.argv[0]} networkInterface") + sys.exit(-1) + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + ChannelFactoryInitialize(0, sys.argv[1]) + + test_option = TestOption(name=None, id=None) + user_interface = UserInterface() + user_interface.test_option_ = test_option + + sport_client = LocoClient() + sport_client.SetTimeout(10.0) + sport_client.Init() + + print("Input \"list\" to list all test option ...") + while True: + user_interface.terminal_handle() + + print(f"Updated Test Option: Name = {test_option.name}, ID = {test_option.id}") + + if test_option.id == 0: + sport_client.Damp() + elif test_option.id == 1: + sport_client.Damp() + time.sleep(0.5) + sport_client.Squat2StandUp() + elif test_option.id == 2: + sport_client.StandUp2Squat() + elif test_option.id == 3: + sport_client.Move(0.3,0,0) + elif test_option.id == 4: + sport_client.Move(0,0.3,0) + elif test_option.id == 5: + sport_client.Move(0,0,0.3) + elif test_option.id == 6: + sport_client.LowStand() + elif test_option.id == 7: + sport_client.HighStand() + elif test_option.id == 8: + sport_client.ZeroTorque() + elif test_option.id == 9: + sport_client.WaveHand() + elif test_option.id == 10: + sport_client.WaveHand(True) + elif test_option.id == 11: + sport_client.ShakeHand() + time.sleep(3) + sport_client.ShakeHand() + elif test_option.id == 12: + sport_client.Damp() + time.sleep(0.5) + sport_client.Lie2StandUp() # When using the Lie2StandUp function, ensure that the robot faces up and the ground is hard, flat and rough. + + time.sleep(1) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/example/g1/low_level/g1_low_level_example.py b/external_dependencies/unitree_sdk2_python/example/g1/low_level/g1_low_level_example.py new file mode 100644 index 0000000..382e863 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/g1/low_level/g1_low_level_example.py @@ -0,0 +1,205 @@ +import time +import sys + +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowCmd_ +from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowState_ +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowCmd_ +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowState_ +from unitree_sdk2py.utils.crc import CRC +from unitree_sdk2py.utils.thread import RecurrentThread +from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient + +import numpy as np + +G1_NUM_MOTOR = 29 + +Kp = [ + 60, 60, 60, 100, 40, 40, # legs + 60, 60, 60, 100, 40, 40, # legs + 60, 40, 40, # waist + 40, 40, 40, 40, 40, 40, 40, # arms + 40, 40, 40, 40, 40, 40, 40 # arms +] + +Kd = [ + 1, 1, 1, 2, 1, 1, # legs + 1, 1, 1, 2, 1, 1, # legs + 1, 1, 1, # waist + 1, 1, 1, 1, 1, 1, 1, # arms + 1, 1, 1, 1, 1, 1, 1 # arms +] + +class G1JointIndex: + LeftHipPitch = 0 + LeftHipRoll = 1 + LeftHipYaw = 2 + LeftKnee = 3 + LeftAnklePitch = 4 + LeftAnkleB = 4 + LeftAnkleRoll = 5 + LeftAnkleA = 5 + RightHipPitch = 6 + RightHipRoll = 7 + RightHipYaw = 8 + RightKnee = 9 + RightAnklePitch = 10 + RightAnkleB = 10 + RightAnkleRoll = 11 + RightAnkleA = 11 + WaistYaw = 12 + WaistRoll = 13 # NOTE: INVALID for g1 23dof/29dof with waist locked + WaistA = 13 # NOTE: INVALID for g1 23dof/29dof with waist locked + WaistPitch = 14 # NOTE: INVALID for g1 23dof/29dof with waist locked + WaistB = 14 # NOTE: INVALID for g1 23dof/29dof with waist locked + LeftShoulderPitch = 15 + LeftShoulderRoll = 16 + LeftShoulderYaw = 17 + LeftElbow = 18 + LeftWristRoll = 19 + LeftWristPitch = 20 # NOTE: INVALID for g1 23dof + LeftWristYaw = 21 # NOTE: INVALID for g1 23dof + RightShoulderPitch = 22 + RightShoulderRoll = 23 + RightShoulderYaw = 24 + RightElbow = 25 + RightWristRoll = 26 + RightWristPitch = 27 # NOTE: INVALID for g1 23dof + RightWristYaw = 28 # NOTE: INVALID for g1 23dof + + +class Mode: + PR = 0 # Series Control for Pitch/Roll Joints + AB = 1 # Parallel Control for A/B Joints + +class Custom: + def __init__(self): + self.time_ = 0.0 + self.control_dt_ = 0.002 # [2ms] + self.duration_ = 3.0 # [3 s] + self.counter_ = 0 + self.mode_pr_ = Mode.PR + self.mode_machine_ = 0 + self.low_cmd = unitree_hg_msg_dds__LowCmd_() + self.low_state = None + self.update_mode_machine_ = False + self.crc = CRC() + + def Init(self): + self.msc = MotionSwitcherClient() + self.msc.SetTimeout(5.0) + self.msc.Init() + + status, result = self.msc.CheckMode() + while result['name']: + self.msc.ReleaseMode() + status, result = self.msc.CheckMode() + time.sleep(1) + + # create publisher # + self.lowcmd_publisher_ = ChannelPublisher("rt/lowcmd", LowCmd_) + self.lowcmd_publisher_.Init() + + # create subscriber # + self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_) + self.lowstate_subscriber.Init(self.LowStateHandler, 10) + + def Start(self): + self.lowCmdWriteThreadPtr = RecurrentThread( + interval=self.control_dt_, target=self.LowCmdWrite, name="control" + ) + while self.update_mode_machine_ == False: + time.sleep(1) + + if self.update_mode_machine_ == True: + self.lowCmdWriteThreadPtr.Start() + + def LowStateHandler(self, msg: LowState_): + self.low_state = msg + + if self.update_mode_machine_ == False: + self.mode_machine_ = self.low_state.mode_machine + self.update_mode_machine_ = True + + self.counter_ +=1 + if (self.counter_ % 500 == 0) : + self.counter_ = 0 + print(self.low_state.imu_state.rpy) + + def LowCmdWrite(self): + self.time_ += self.control_dt_ + + if self.time_ < self.duration_ : + # [Stage 1]: set robot to zero posture + for i in range(G1_NUM_MOTOR): + ratio = np.clip(self.time_ / self.duration_, 0.0, 1.0) + self.low_cmd.mode_pr = Mode.PR + self.low_cmd.mode_machine = self.mode_machine_ + self.low_cmd.motor_cmd[i].mode = 1 # 1:Enable, 0:Disable + self.low_cmd.motor_cmd[i].tau = 0. + self.low_cmd.motor_cmd[i].q = (1.0 - ratio) * self.low_state.motor_state[i].q + self.low_cmd.motor_cmd[i].dq = 0. + self.low_cmd.motor_cmd[i].kp = Kp[i] + self.low_cmd.motor_cmd[i].kd = Kd[i] + + elif self.time_ < self.duration_ * 2 : + # [Stage 2]: swing ankle using PR mode + max_P = np.pi * 30.0 / 180.0 + max_R = np.pi * 10.0 / 180.0 + t = self.time_ - self.duration_ + L_P_des = max_P * np.sin(2.0 * np.pi * t) + L_R_des = max_R * np.sin(2.0 * np.pi * t) + R_P_des = max_P * np.sin(2.0 * np.pi * t) + R_R_des = -max_R * np.sin(2.0 * np.pi * t) + + self.low_cmd.mode_pr = Mode.PR + self.low_cmd.mode_machine = self.mode_machine_ + self.low_cmd.motor_cmd[G1JointIndex.LeftAnklePitch].q = L_P_des + self.low_cmd.motor_cmd[G1JointIndex.LeftAnkleRoll].q = L_R_des + self.low_cmd.motor_cmd[G1JointIndex.RightAnklePitch].q = R_P_des + self.low_cmd.motor_cmd[G1JointIndex.RightAnkleRoll].q = R_R_des + + else : + # [Stage 3]: swing ankle using AB mode + max_A = np.pi * 30.0 / 180.0 + max_B = np.pi * 10.0 / 180.0 + t = self.time_ - self.duration_ * 2 + L_A_des = max_A * np.sin(2.0 * np.pi * t) + L_B_des = max_B * np.sin(2.0 * np.pi * t + np.pi) + R_A_des = -max_A * np.sin(2.0 * np.pi * t) + R_B_des = -max_B * np.sin(2.0 * np.pi * t + np.pi) + + self.low_cmd.mode_pr = Mode.AB + self.low_cmd.mode_machine = self.mode_machine_ + self.low_cmd.motor_cmd[G1JointIndex.LeftAnkleA].q = L_A_des + self.low_cmd.motor_cmd[G1JointIndex.LeftAnkleB].q = L_B_des + self.low_cmd.motor_cmd[G1JointIndex.RightAnkleA].q = R_A_des + self.low_cmd.motor_cmd[G1JointIndex.RightAnkleB].q = R_B_des + + max_WristYaw = np.pi * 30.0 / 180.0 + L_WristYaw_des = max_WristYaw * np.sin(2.0 * np.pi * t) + R_WristYaw_des = max_WristYaw * np.sin(2.0 * np.pi * t) + self.low_cmd.motor_cmd[G1JointIndex.LeftWristRoll].q = L_WristYaw_des + self.low_cmd.motor_cmd[G1JointIndex.RightWristRoll].q = R_WristYaw_des + + + self.low_cmd.crc = self.crc.Crc(self.low_cmd) + self.lowcmd_publisher_.Write(self.low_cmd) + +if __name__ == '__main__': + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + custom = Custom() + custom.Init() + custom.Start() + + while True: + time.sleep(1) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/example/g1/low_level/g1_move_hands_example.py b/external_dependencies/unitree_sdk2_python/example/g1/low_level/g1_move_hands_example.py new file mode 100644 index 0000000..4b43fd2 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/g1/low_level/g1_move_hands_example.py @@ -0,0 +1,225 @@ +import time +import sys + +import numpy as np + +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import HandCmd_, HandState_ +from unitree_sdk2py.idl.default import unitree_hg_msg_dds__HandCmd_ +from unitree_sdk2py.utils.crc import CRC +from unitree_sdk2py.utils.thread import RecurrentThread + +MOTOR_NUM_HAND = 7 + +Kp = [0.5] * MOTOR_NUM_HAND +Kd = [0.1] * MOTOR_NUM_HAND + +Kp[0] = 2.0 + +maxTorqueLimits_left = [1.05, 1.05, 1.75, 0.0, 0.0, 0.0, 0.0] +minTorqueLimits_left = [-1.05, -0.72, 0.0, -1.57, -1.75, -1.57, -1.75] +maxTorqueLimits_right = [1.05, 0.74, 0.0, 1.57, 1.75, 1.57, 1.75] +minTorqueLimits_right = [-1.05, -1.05, -1.75, 0.0, 0.0, 0.0, 0.0] + + +def make_hand_mode(motor_index): + status = 0x01 + timeout = 0x01 + mode = (motor_index & 0x0F) + mode |= (status << 4) # bits [4..6] + mode |= (timeout << 7) # bit 7 + return mode + + +class HandControl: + def __init__(self, network_interface="enp36s0f1"): + ChannelFactoryInitialize(0, network_interface) + + self.left_cmd_pub = ChannelPublisher("rt/dex3/left/cmd", HandCmd_) + self.right_cmd_pub = ChannelPublisher("rt/dex3/right/cmd", HandCmd_) + + self.left_state_sub = ChannelSubscriber("rt/dex3/left/state", HandState_) + self.right_state_sub = ChannelSubscriber("rt/dex3/right/state", HandState_) + + self.left_cmd_pub.Init() + self.right_cmd_pub.Init() + + self.left_state_sub.Init(self.left_state_handler, 10) + self.right_state_sub.Init(self.right_state_handler, 10) + + self.left_state = None + self.right_state = None + + self.crc = CRC() + + # Control loop timing + self.control_dt = 0.01 # 10 ms + self.time_ = 0.0 + self.stage_time = 3.0 # 3s per stage + + # To let the code run once we start: + self.run_flag = False + + self.counter = 0 + + def left_state_handler(self, msg: HandState_): + self.left_state = msg + + # self.counter +=1 + # if (self.counter % 1000 == 0) : + # self.counter = 0 + # print('Left hand state:') + # for i in range(MOTOR_NUM_HAND): + # print(180/np.pi*self.left_state.motor_state[i].q) + + def right_state_handler(self, msg: HandState_): + self.right_state = msg + + self.counter += 1 + if (self.counter % 1000 == 0): + self.counter = 0 + print('Right hand state:') + for i in range(MOTOR_NUM_HAND): + print(180 / np.pi * self.right_state.motor_state[i].q) + + def start(self): + """ + Kick off the main control loop thread. + """ + self.run_flag = True + self.control_thread = RecurrentThread(interval=self.control_dt, + target=self.hand_control_loop, + name="HandControlLoop") + self.control_thread.Start() + + def hand_control_loop(self): + """ + This gets called at a fixed rate (every self.control_dt seconds). + We'll demonstrate 3 stages of motion: + 1) Move from current position to 'zero' (or some nominal) in 3s + 2) Sinusoidal motion in stage 2 (3s) + 3) Another motion in stage 3 (final) + """ + if not self.run_flag: + return + + self.time_ += self.control_dt + t = self.time_ + cmd_left = unitree_hg_msg_dds__HandCmd_() + cmd_right = unitree_hg_msg_dds__HandCmd_() + + # cmd_left.motor_cmd.resize(MOTOR_NUM_HAND) + # cmd_right.motor_cmd.resize(MOTOR_NUM_HAND) + + # Prepare stage times + stage1_end = self.stage_time + stage2_end = self.stage_time * 2.0 + + # We'll fetch current positions for left and right if available + # so we can blend from actual state to zero. + # If we haven't gotten a state yet, default to 0. + left_q_now = [0.0] * MOTOR_NUM_HAND + right_q_now = [0.0] * MOTOR_NUM_HAND + + if self.left_state is not None: + for i in range(MOTOR_NUM_HAND): + left_q_now[i] = self.left_state.motor_state[i].q + + if self.right_state is not None: + for i in range(MOTOR_NUM_HAND): + right_q_now[i] = self.right_state.motor_state[i].q + + left_q_desired = np.deg2rad([50.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) + + right_q_desired = np.deg2rad([50.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) + + # Decide the desired position for each stage: + if t < stage1_end: + # Stage 1: Move from the current joint positions to zero in [0..3s] + ratio = np.clip(t / stage1_end, 0.0, 1.0) + # Simple linear blend: final = (1-ratio)*initial + ratio*0 + left_q_des = np.zeros(MOTOR_NUM_HAND) + right_q_des = np.zeros(MOTOR_NUM_HAND) + for i in range(MOTOR_NUM_HAND): + left_q_des[i] = (1.0 - ratio) * (left_q_now[i] - left_q_desired[i]) + left_q_desired[i] + right_q_des[i] = (1.0 - ratio) * (right_q_now[i] - right_q_desired[i]) + right_q_desired[i] + + else: + # Stage 2: Some sinusoidal wave + # We'll wave only the first 2 or 3 motors, just as a demo + dt2 = t - stage1_end + freq = 1.0 # 1 Hz + amp = 0.3 # ~ 0.3 rad amplitude + + left_q_des = left_q_desired + right_q_des = right_q_desired + + # E.g. wave motor 0 and 1: + left_q_des[0] += amp * np.sin(2 * np.pi * freq * dt2) + left_q_des[1] += amp * (1 - np.cos(2 * np.pi * freq * dt2)) + left_q_des[2] += amp * (1 - np.cos(2 * np.pi * freq * dt2)) + right_q_des[0] += amp * np.sin(2 * np.pi * freq * dt2) + right_q_des[1] += amp * (np.cos(2 * np.pi * freq * dt2) - 1) + right_q_des[2] += amp * (np.cos(2 * np.pi * freq * dt2) - 1) + + freqA = 2.0 + freqB = 2.0 + ampA = 0.2 + ampB = 0.4 + + left_q_des[3] += ampA * (np.cos(2 * np.pi * freqA * dt2) - 1) + left_q_des[4] += ampB * (np.cos(2 * np.pi * freqB * dt2) - 1) + left_q_des[5] += ampA * (np.cos(2 * np.pi * freqA * dt2) - 1) + left_q_des[6] += ampB * (np.cos(2 * np.pi * freqB * dt2) - 1) + right_q_des[3] += ampA * (1 - np.cos(2 * np.pi * freqA * dt2)) + right_q_des[4] += ampB * (1 - np.cos(2 * np.pi * freqB * dt2)) + right_q_des[5] += ampA * (1 - np.cos(2 * np.pi * freqA * dt2)) + right_q_des[6] += ampB * (1 - np.cos(2 * np.pi * freqB * dt2)) + + # Fill in the commands + for i in range(MOTOR_NUM_HAND): + # Build the bitfield mode (see your C++ example) + mode_val = make_hand_mode(i) + + # Left + cmd_left.motor_cmd[i].mode = mode_val + cmd_left.motor_cmd[i].q = left_q_des[i] + cmd_left.motor_cmd[i].dq = 0.0 + cmd_left.motor_cmd[i].tau = 0.0 + cmd_left.motor_cmd[i].kp = Kp[i] + cmd_left.motor_cmd[i].kd = Kd[i] + + # Right + cmd_right.motor_cmd[i].mode = mode_val + cmd_right.motor_cmd[i].q = right_q_des[i] + cmd_right.motor_cmd[i].dq = 0.0 + cmd_right.motor_cmd[i].tau = 0.0 + cmd_right.motor_cmd[i].kp = Kp[i] + cmd_right.motor_cmd[i].kd = Kd[i] + + # Compute CRC if your firmware requires it + # cmd_left.crc = self.crc.Crc(cmd_left) + # cmd_right.crc = self.crc.Crc(cmd_right) + + # Publish + self.left_cmd_pub.Write(cmd_left) + self.right_cmd_pub.Write(cmd_right) + + +if __name__ == "__main__": + print("WARNING: Make sure your robot’s hands can move freely before running.") + input("Press Enter to continue...") + + # Optionally pass a specific interface name, e.g. "enp37s0f0" or "eth0" + if len(sys.argv) > 1: + net_if = sys.argv[1] + else: + net_if = "enp36s0f1" + + # Create and start + hand_control = HandControl(network_interface=net_if) + hand_control.start() + + # Just wait + while True: + time.sleep(1) diff --git a/external_dependencies/unitree_sdk2_python/example/g1/readme.md b/external_dependencies/unitree_sdk2_python/example/g1/readme.md new file mode 100644 index 0000000..9fb7900 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/g1/readme.md @@ -0,0 +1,5 @@ +This example is a test of Unitree G1/H1-2 robot. + +**Note:** +idl/unitree_go is used for Unitree Go2/B2/H1/B2w/Go2w robots +idl/unitree_hg is used for Unitree G1/H1-2 robots diff --git a/external_dependencies/unitree_sdk2_python/example/go2/front_camera/camera_opencv.py b/external_dependencies/unitree_sdk2_python/example/go2/front_camera/camera_opencv.py new file mode 100644 index 0000000..9d98839 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/go2/front_camera/camera_opencv.py @@ -0,0 +1,41 @@ +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.go2.video.video_client import VideoClient +import cv2 +import numpy as np +import sys + + +if __name__ == "__main__": + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + client = VideoClient() # Create a video client + client.SetTimeout(3.0) + client.Init() + + code, data = client.GetImageSample() + + # Request normal when code==0 + while code == 0: + # Get Image data from Go2 robot + code, data = client.GetImageSample() + + # Convert to numpy image + image_data = np.frombuffer(bytes(data), dtype=np.uint8) + image = cv2.imdecode(image_data, cv2.IMREAD_COLOR) + + # Display image + cv2.imshow("front_camera", image) + # Press ESC to stop + if cv2.waitKey(20) == 27: + break + + if code != 0: + print("Get image sample error. code:", code) + else: + # Capture an image + cv2.imwrite("front_image.jpg", image) + + cv2.destroyWindow("front_camera") diff --git a/external_dependencies/unitree_sdk2_python/example/go2/front_camera/capture_image.py b/external_dependencies/unitree_sdk2_python/example/go2/front_camera/capture_image.py new file mode 100644 index 0000000..1e85643 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/go2/front_camera/capture_image.py @@ -0,0 +1,30 @@ +import time +import os +import sys + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.go2.video.video_client import VideoClient + +if __name__ == "__main__": + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + client = VideoClient() + client.SetTimeout(3.0) + client.Init() + + print("##################GetImageSample###################") + code, data = client.GetImageSample() + + if code != 0: + print("get image sample error. code:", code) + else: + imageName = "./img.jpg" + print("ImageName:", imageName) + + with open(imageName, "+wb") as f: + f.write(bytes(data)) + + time.sleep(1) diff --git a/external_dependencies/unitree_sdk2_python/example/go2/high_level/go2_sport_client.py b/external_dependencies/unitree_sdk2_python/example/go2/high_level/go2_sport_client.py new file mode 100644 index 0000000..b14d79d --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/go2/high_level/go2_sport_client.py @@ -0,0 +1,170 @@ +import time +import sys +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_go_msg_dds__SportModeState_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import SportModeState_ +from unitree_sdk2py.go2.sport.sport_client import ( + SportClient, + PathPoint, + SPORT_PATH_POINT_SIZE, +) +import math +from dataclasses import dataclass + +@dataclass +class TestOption: + name: str + id: int + +option_list = [ + TestOption(name="damp", id=0), + TestOption(name="stand_up", id=1), + TestOption(name="stand_down", id=2), + TestOption(name="move forward", id=3), + TestOption(name="move lateral", id=4), + TestOption(name="move rotate", id=5), + TestOption(name="stop_move", id=6), + TestOption(name="switch_gait", id=7), + TestOption(name="switch_gait", id=8), + TestOption(name="balanced stand", id=9), + TestOption(name="recovery", id=10), + TestOption(name="recovery", id=10), + TestOption(name="left flip", id=11), + TestOption(name="back flip", id=12), + TestOption(name="free walk", id=13), + TestOption(name="free bound", id=14), + TestOption(name="free avoid", id=15), + TestOption(name="walk stair", id=16), + TestOption(name="walk upright", id=17), + TestOption(name="cross step", id=18), + TestOption(name="free jump", id=19) +] + +class UserInterface: + def __init__(self): + self.test_option_ = None + + def convert_to_int(self, input_str): + try: + return int(input_str) + except ValueError: + return None + + def terminal_handle(self): + input_str = input("Enter id or name: \n") + + if input_str == "list": + self.test_option_.name = None + self.test_option_.id = None + for option in option_list: + print(f"{option.name}, id: {option.id}") + return + + for option in option_list: + if input_str == option.name or self.convert_to_int(input_str) == option.id: + self.test_option_.name = option.name + self.test_option_.id = option.id + print(f"Test: {self.test_option_.name}, test_id: {self.test_option_.id}") + return + + print("No matching test option found.") + +if __name__ == "__main__": + + if len(sys.argv) < 2: + print(f"Usage: python3 {sys.argv[0]} networkInterface") + sys.exit(-1) + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + ChannelFactoryInitialize(0, sys.argv[1]) + + test_option = TestOption(name=None, id=None) + user_interface = UserInterface() + user_interface.test_option_ = test_option + + sport_client = SportClient() + sport_client.SetTimeout(10.0) + sport_client.Init() + while True: + + user_interface.terminal_handle() + + print(f"Updated Test Option: Name = {test_option.name}, ID = {test_option.id}\n") + + if test_option.id == 0: + sport_client.Damp() + elif test_option.id == 1: + sport_client.StandUp() + elif test_option.id == 2: + sport_client.StandDown() + elif test_option.id == 3: + sport_client.Move(0.3,0,0) + elif test_option.id == 4: + sport_client.Move(0,0.3,0) + elif test_option.id == 5: + sport_client.Move(0,0,0.5) + elif test_option.id == 6: + sport_client.StopMove() + elif test_option.id == 7: + sport_client.SwitchGait(0) + elif test_option.id == 8: + sport_client.SwitchGait(1) + elif test_option.id == 9: + sport_client.BalanceStand() + elif test_option.id == 10: + sport_client.RecoveryStand() + elif test_option.id == 11: + ret = sport_client.LeftFlip() + print("ret: ",ret) + elif test_option.id == 12: + ret = sport_client.BackFlip() + print("ret: ",ret) + elif test_option.id == 13: + ret = sport_client.FreeWalk(True) + print("ret: ",ret) + elif test_option.id == 14: + ret = sport_client.FreeBound(True) + print("ret: ",ret) + time.sleep(2) + ret = sport_client.FreeBound(False) + print("ret: ",ret) + elif test_option.id == 14: + ret = sport_client.FreeBound(True) + print("ret: ",ret) + time.sleep(2) + ret = sport_client.FreeBound(False) + print("ret: ",ret) + elif test_option.id == 15: + ret = sport_client.FreeAvoid(True) + print("ret: ",ret) + time.sleep(2) + ret = sport_client.FreeAvoid(False) + print("ret: ",ret) + elif test_option.id == 16: + ret = sport_client.WalkStair(True) + print("ret: ",ret) + time.sleep(10) + ret = sport_client.WalkStair(False) + print("ret: ",ret) + elif test_option.id == 17: + ret = sport_client.WalkUpright(True) + print("ret: ",ret) + time.sleep(4) + ret = sport_client.WalkUpright(False) + print("ret: ",ret) + elif test_option.id == 18: + ret = sport_client.CrossStep(True) + print("ret: ",ret) + time.sleep(4) + ret = sport_client.CrossStep(False) + print("ret: ",ret) + elif test_option.id == 19: + ret = sport_client.FreeJump(True) + print("ret: ",ret) + time.sleep(4) + ret = sport_client.FreeJump(False) + print("ret: ",ret) + + time.sleep(1) diff --git a/external_dependencies/unitree_sdk2_python/example/go2/high_level/go2_utlidar_switch.py b/external_dependencies/unitree_sdk2_python/example/go2/high_level/go2_utlidar_switch.py new file mode 100644 index 0000000..09e0ce9 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/go2/high_level/go2_utlidar_switch.py @@ -0,0 +1,39 @@ +import time +import sys + +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.std_msgs.msg.dds_ import String_ +from unitree_sdk2py.idl.default import std_msgs_msg_dds__String_ + +class Custom: + def __init__(self): + # create publisher # + self.publisher = ChannelPublisher("rt/utlidar/switch", String_) + self.publisher.Init() + self.low_cmd = std_msgs_msg_dds__String_() + + def go2_utlidar_switch(self,status): + if status == "OFF": + self.low_cmd.data = "OFF" + elif status == "ON": + self.low_cmd.data = "ON" + + self.publisher.Write(self.low_cmd) + + +if __name__ == '__main__': + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + custom = Custom() + custom.go2_utlidar_switch("OFF") + + + diff --git a/external_dependencies/unitree_sdk2_python/example/go2/low_level/go2_stand_example.py b/external_dependencies/unitree_sdk2_python/example/go2/low_level/go2_stand_example.py new file mode 100644 index 0000000..f2241ce --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/go2/low_level/go2_stand_example.py @@ -0,0 +1,176 @@ +import time +import sys + +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowCmd_ +from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowState_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowCmd_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_ +from unitree_sdk2py.utils.crc import CRC +from unitree_sdk2py.utils.thread import RecurrentThread +import unitree_legged_const as go2 +from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient +from unitree_sdk2py.go2.sport.sport_client import SportClient + +class Custom: + def __init__(self): + self.Kp = 60.0 + self.Kd = 5.0 + self.time_consume = 0 + self.rate_count = 0 + self.sin_count = 0 + self.motiontime = 0 + self.dt = 0.002 # 0.001~0.01 + + self.low_cmd = unitree_go_msg_dds__LowCmd_() + self.low_state = None + + self._targetPos_1 = [0.0, 1.36, -2.65, 0.0, 1.36, -2.65, + -0.2, 1.36, -2.65, 0.2, 1.36, -2.65] + self._targetPos_2 = [0.0, 0.67, -1.3, 0.0, 0.67, -1.3, + 0.0, 0.67, -1.3, 0.0, 0.67, -1.3] + self._targetPos_3 = [-0.35, 1.36, -2.65, 0.35, 1.36, -2.65, + -0.5, 1.36, -2.65, 0.5, 1.36, -2.65] + + self.startPos = [0.0] * 12 + self.duration_1 = 500 + self.duration_2 = 500 + self.duration_3 = 1000 + self.duration_4 = 900 + self.percent_1 = 0 + self.percent_2 = 0 + self.percent_3 = 0 + self.percent_4 = 0 + + self.firstRun = True + self.done = False + + # thread handling + self.lowCmdWriteThreadPtr = None + + self.crc = CRC() + + # Public methods + def Init(self): + self.InitLowCmd() + + # create publisher # + self.lowcmd_publisher = ChannelPublisher("rt/lowcmd", LowCmd_) + self.lowcmd_publisher.Init() + + # create subscriber # + self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_) + self.lowstate_subscriber.Init(self.LowStateMessageHandler, 10) + + self.sc = SportClient() + self.sc.SetTimeout(5.0) + self.sc.Init() + + self.msc = MotionSwitcherClient() + self.msc.SetTimeout(5.0) + self.msc.Init() + + status, result = self.msc.CheckMode() + while result['name']: + self.sc.StandDown() + self.msc.ReleaseMode() + status, result = self.msc.CheckMode() + time.sleep(1) + + def Start(self): + self.lowCmdWriteThreadPtr = RecurrentThread( + interval=0.002, target=self.LowCmdWrite, name="writebasiccmd" + ) + self.lowCmdWriteThreadPtr.Start() + + # Private methods + def InitLowCmd(self): + self.low_cmd.head[0]=0xFE + self.low_cmd.head[1]=0xEF + self.low_cmd.level_flag = 0xFF + self.low_cmd.gpio = 0 + for i in range(20): + self.low_cmd.motor_cmd[i].mode = 0x01 # (PMSM) mode + self.low_cmd.motor_cmd[i].q= go2.PosStopF + self.low_cmd.motor_cmd[i].kp = 0 + self.low_cmd.motor_cmd[i].dq = go2.VelStopF + self.low_cmd.motor_cmd[i].kd = 0 + self.low_cmd.motor_cmd[i].tau = 0 + + def LowStateMessageHandler(self, msg: LowState_): + self.low_state = msg + # print("FR_0 motor state: ", msg.motor_state[go2.LegID["FR_0"]]) + # print("IMU state: ", msg.imu_state) + # print("Battery state: voltage: ", msg.power_v, "current: ", msg.power_a) + + def LowCmdWrite(self): + + if self.firstRun: + for i in range(12): + self.startPos[i] = self.low_state.motor_state[i].q + self.firstRun = False + + self.percent_1 += 1.0 / self.duration_1 + self.percent_1 = min(self.percent_1, 1) + if self.percent_1 < 1: + for i in range(12): + self.low_cmd.motor_cmd[i].q = (1 - self.percent_1) * self.startPos[i] + self.percent_1 * self._targetPos_1[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if (self.percent_1 == 1) and (self.percent_2 <= 1): + self.percent_2 += 1.0 / self.duration_2 + self.percent_2 = min(self.percent_2, 1) + for i in range(12): + self.low_cmd.motor_cmd[i].q = (1 - self.percent_2) * self._targetPos_1[i] + self.percent_2 * self._targetPos_2[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 < 1): + self.percent_3 += 1.0 / self.duration_3 + self.percent_3 = min(self.percent_3, 1) + for i in range(12): + self.low_cmd.motor_cmd[i].q = self._targetPos_2[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 == 1) and (self.percent_4 <= 1): + self.percent_4 += 1.0 / self.duration_4 + self.percent_4 = min(self.percent_4, 1) + for i in range(12): + self.low_cmd.motor_cmd[i].q = (1 - self.percent_4) * self._targetPos_2[i] + self.percent_4 * self._targetPos_3[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + self.low_cmd.crc = self.crc.Crc(self.low_cmd) + self.lowcmd_publisher.Write(self.low_cmd) + +if __name__ == '__main__': + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + custom = Custom() + custom.Init() + custom.Start() + + while True: + if custom.percent_4 == 1.0: + time.sleep(1) + print("Done!") + sys.exit(-1) + time.sleep(1) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/example/go2/low_level/unitree_legged_const.py b/external_dependencies/unitree_sdk2_python/example/go2/low_level/unitree_legged_const.py new file mode 100644 index 0000000..153a051 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/go2/low_level/unitree_legged_const.py @@ -0,0 +1,20 @@ +LegID = { + "FR_0": 0, # Front right hip + "FR_1": 1, # Front right thigh + "FR_2": 2, # Front right calf + "FL_0": 3, + "FL_1": 4, + "FL_2": 5, + "RR_0": 6, + "RR_1": 7, + "RR_2": 8, + "RL_0": 9, + "RL_1": 10, + "RL_2": 11, +} + +HIGHLEVEL = 0xEE +LOWLEVEL = 0xFF +TRIGERLEVEL = 0xF0 +PosStopF = 2.146e9 +VelStopF = 16000.0 diff --git a/external_dependencies/unitree_sdk2_python/example/go2w/high_level/go2w_sport_client.py b/external_dependencies/unitree_sdk2_python/example/go2w/high_level/go2w_sport_client.py new file mode 100644 index 0000000..824a882 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/go2w/high_level/go2w_sport_client.py @@ -0,0 +1,99 @@ +import time +import sys +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_go_msg_dds__SportModeState_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import SportModeState_ +from unitree_sdk2py.go2.sport.sport_client import SportClient +import math +from dataclasses import dataclass + +@dataclass +class TestOption: + name: str + id: int + +option_list = [ + TestOption(name="damp", id=0), + TestOption(name="stand_up", id=1), + TestOption(name="stand_down", id=2), + TestOption(name="move", id=3), + TestOption(name="stop_move", id=4), + TestOption(name="speed_level", id=5), + TestOption(name="switch_gait", id=6), + TestOption(name="get_state", id=7), + TestOption(name="recovery", id=8), + TestOption(name="balance", id=9) +] + +class UserInterface: + def __init__(self): + self.test_option_ = None + + def convert_to_int(self, input_str): + try: + return int(input_str) + except ValueError: + return None + + def terminal_handle(self): + input_str = input("Enter id or name: \n") + + if input_str == "list": + self.test_option_.name = None + self.test_option_.id = None + for option in option_list: + print(f"{option.name}, id: {option.id}") + return + + for option in option_list: + if input_str == option.name or self.convert_to_int(input_str) == option.id: + self.test_option_.name = option.name + self.test_option_.id = option.id + print(f"Test: {self.test_option_.name}, test_id: {self.test_option_.id}") + return + + print("No matching test option found.") + +if __name__ == "__main__": + if len(sys.argv) < 2: + print(f"Usage: python3 {sys.argv[0]} networkInterface") + sys.exit(-1) + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + ChannelFactoryInitialize(0, sys.argv[1]) + + test_option = TestOption(name=None, id=None) + user_interface = UserInterface() + user_interface.test_option_ = test_option + + sport_client = SportClient() + sport_client.SetTimeout(10.0) + sport_client.Init() + + while True: + user_interface.terminal_handle() + + print(f"Updated Test Option: Name = {test_option.name}, ID = {test_option.id}\n") + + if test_option.id == 0: + sport_client.Damp() + elif test_option.id == 1: + sport_client.StandUp() + elif test_option.id == 2: + sport_client.StandDown() + elif test_option.id == 3: + sport_client.Move(0.5,0,0) + elif test_option.id == 4: + sport_client.StopMove() + elif test_option.id == 5: + sport_client.SpeedLevel(1) + elif test_option.id == 6: + sport_client.SwitchGait(1) + elif test_option.id == 8: + sport_client.RecoveryStand() + elif test_option.id == 9: + sport_client.BalanceStand() + + time.sleep(1) diff --git a/external_dependencies/unitree_sdk2_python/example/go2w/low_level/go2w_stand_example.py b/external_dependencies/unitree_sdk2_python/example/go2w/low_level/go2w_stand_example.py new file mode 100644 index 0000000..e3a54de --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/go2w/low_level/go2w_stand_example.py @@ -0,0 +1,196 @@ +import time +import sys + +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowCmd_ +from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowState_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowCmd_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_ +from unitree_sdk2py.utils.crc import CRC +from unitree_sdk2py.utils.thread import RecurrentThread +import unitree_legged_const as go2w +from unitree_sdk2py.go2.robot_state.robot_state_client import RobotStateClient +from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient +from unitree_sdk2py.go2.sport.sport_client import SportClient + +class Custom: + def __init__(self): + self.Kp = 70.0 + self.Kd = 5.0 + self.time_consume = 0 + self.rate_count = 0 + self.sin_count = 0 + self.motiontime = 0 + self.dt = 0.002 + + self.low_cmd = unitree_go_msg_dds__LowCmd_() + self.low_state = None + + self.targetPos_1 = [0.0, 1.36, -2.65, 0.0, 1.36, -2.65, + -0.2, 1.36, -2.65, 0.2, 1.36, -2.65] + + self.targetPos_2 = [0.0, 0.67, -1.3, 0.0, 0.67, -1.3, + 0.0, 0.67, -1.3, 0.0, 0.67, -1.3] + + self.targetPos_3 = [-0.35, 1.36, -2.65, 0.35, 1.36, -2.65, + -0.5, 1.36, -2.65, 0.5, 1.36, -2.65] + + self.startPos = [0.0] * 12 + self.duration_1 = 500 + self.duration_2 = 500 + self.duration_3 = 2000 + self.duration_4 = 900 + self.percent_1 = 0 + self.percent_2 = 0 + self.percent_3 = 0 + self.percent_4 = 0 + + self.firstRun = True + self.done = False + + # thread handling + self.lowCmdWriteThreadPtr = None + + self.crc = CRC() + + # Public methods + def Init(self): + self.InitLowCmd() + + # create publisher # + self.lowcmd_publisher = ChannelPublisher("rt/lowcmd", LowCmd_) + self.lowcmd_publisher.Init() + + # create subscriber # + self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_) + self.lowstate_subscriber.Init(self.LowStateMessageHandler, 10) + + self.sc = SportClient() + self.sc.SetTimeout(5.0) + self.sc.Init() + + self.msc = MotionSwitcherClient() + self.msc.SetTimeout(5.0) + self.msc.Init() + + status, result = self.msc.CheckMode() + while result['name']: + self.sc.StandUp() + self.sc.StandDown() + self.msc.ReleaseMode() + status, result = self.msc.CheckMode() + time.sleep(1) + + def Start(self): + self.lowCmdWriteThreadPtr = RecurrentThread( + name="writebasiccmd", interval=0.002, target=self.LowCmdWrite, + ) + self.lowCmdWriteThreadPtr.Start() + + def InitLowCmd(self): + self.low_cmd.head[0] = 0xFE + self.low_cmd.head[1] = 0xEF + self.low_cmd.level_flag = 0xFF + self.low_cmd.gpio = 0 + for i in range(20): + self.low_cmd.motor_cmd[i].mode = 0x01 + self.low_cmd.motor_cmd[i].q= go2w.PosStopF + self.low_cmd.motor_cmd[i].kp = 0 + self.low_cmd.motor_cmd[i].dq = go2w.VelStopF + self.low_cmd.motor_cmd[i].kd = 0 + self.low_cmd.motor_cmd[i].tau = 0 + + def LowStateMessageHandler(self, msg: LowState_): + self.low_state = msg + + def LowCmdWrite(self): + if self.firstRun: + for i in range(12): + self.startPos[i] = self.low_state.motor_state[i].q + self.firstRun = False + + self.percent_1 += 1.0 / self.duration_1 + self.percent_1 = min(self.percent_1, 1) + if self.percent_1 < 1: + for i in range(12): + self.low_cmd.motor_cmd[i].q = (1 - self.percent_1) * self.startPos[i] + self.percent_1 * self.targetPos_1[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if (self.percent_1 == 1) and (self.percent_2 <= 1): + self.percent_2 += 1.0 / self.duration_2 + self.percent_2 = min(self.percent_2, 1) + for i in range(12): + self.low_cmd.motor_cmd[i].q = (1 - self.percent_2) * self.targetPos_1[i] + self.percent_2 * self.targetPos_2[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 < 1): + self.percent_3 += 1.0 / self.duration_3 + self.percent_3 = min(self.percent_3, 1) + for i in range(12): + self.low_cmd.motor_cmd[i].q = self.targetPos_2[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if self.percent_3 < 0.4: + for i in range(12, 16): + self.low_cmd.motor_cmd[i].q = 0 + self.low_cmd.motor_cmd[i].kp = 0.0 + self.low_cmd.motor_cmd[i].dq = 3 + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if 0.4 <= self.percent_3 < 0.8: + for i in range(12, 16): + self.low_cmd.motor_cmd[i].q = 0 + self.low_cmd.motor_cmd[i].kp = 0 + self.low_cmd.motor_cmd[i].dq = -3 + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if self.percent_3 >= 0.8: + for i in range(12, 16): + self.low_cmd.motor_cmd[i].q = 0 + self.low_cmd.motor_cmd[i].kp = 0 + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + if (self.percent_1 == 1) and (self.percent_2 == 1) and (self.percent_3 == 1) and (self.percent_4 <= 1): + self.percent_4 += 1.0 / self.duration_4 + self.percent_4 = min(self.percent_4, 1) + for i in range(12): + self.low_cmd.motor_cmd[i].q = (1 - self.percent_4) * self.targetPos_2[i] + self.percent_4 * self.targetPos_3[i] + self.low_cmd.motor_cmd[i].dq = 0 + self.low_cmd.motor_cmd[i].kp = self.Kp + self.low_cmd.motor_cmd[i].kd = self.Kd + self.low_cmd.motor_cmd[i].tau = 0 + + self.low_cmd.crc = self.crc.Crc(self.low_cmd) + self.lowcmd_publisher.Write(self.low_cmd) + +if __name__ == '__main__': + + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + custom = Custom() + custom.Init() + custom.Start() + + while True: + if custom.percent_4 == 1.0: + time.sleep(1) + print("Done!") + sys.exit(-1) + time.sleep(1) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/example/go2w/low_level/unitree_legged_const.py b/external_dependencies/unitree_sdk2_python/example/go2w/low_level/unitree_legged_const.py new file mode 100644 index 0000000..7943e31 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/go2w/low_level/unitree_legged_const.py @@ -0,0 +1,24 @@ +LegID = { + "FR_0": 0, # Front right hip + "FR_1": 1, # Front right thigh + "FR_2": 2, # Front right calf + "FL_0": 3, + "FL_1": 4, + "FL_2": 5, + "RR_0": 6, + "RR_1": 7, + "RR_2": 8, + "RL_0": 9, + "RL_1": 10, + "RL_2": 11, + "FR_w": 12, # Front right wheel + "FL_w": 13, # Front left wheel + "RR_w": 14, # Rear right wheel + "RL_w": 15, # Rear left wheel +} + +HIGHLEVEL = 0xEE +LOWLEVEL = 0xFF +TRIGERLEVEL = 0xF0 +PosStopF = 2.146e9 +VelStopF = 16000.0 diff --git a/external_dependencies/unitree_sdk2_python/example/h1/high_level/h1_loco_client_example.py b/external_dependencies/unitree_sdk2_python/example/h1/high_level/h1_loco_client_example.py new file mode 100644 index 0000000..4432d8a --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/h1/high_level/h1_loco_client_example.py @@ -0,0 +1,96 @@ +import time +import sys +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_go_msg_dds__SportModeState_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import SportModeState_ +from unitree_sdk2py.h1.loco.h1_loco_client import LocoClient +import math +from dataclasses import dataclass + +@dataclass +class TestOption: + name: str + id: int + +option_list = [ + TestOption(name="damp", id=0), + TestOption(name="stand_up", id=1), + TestOption(name="move forward", id=3), + TestOption(name="move lateral", id=4), + TestOption(name="move rotate", id=5), + TestOption(name="low stand", id=6), + TestOption(name="high stand", id=7), + TestOption(name="zero torque", id=8) +] + +class UserInterface: + def __init__(self): + self.test_option_ = None + + def convert_to_int(self, input_str): + try: + return int(input_str) + except ValueError: + return None + + def terminal_handle(self): + input_str = input("Enter id or name: \n") + + if input_str == "list": + self.test_option_.name = None + self.test_option_.id = None + for option in option_list: + print(f"{option.name}, id: {option.id}") + return + + for option in option_list: + if input_str == option.name or self.convert_to_int(input_str) == option.id: + self.test_option_.name = option.name + self.test_option_.id = option.id + print(f"Test: {self.test_option_.name}, test_id: {self.test_option_.id}") + return + + print("No matching test option found.") + +if __name__ == "__main__": + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + test_option = TestOption(name=None, id=None) + user_interface = UserInterface() + user_interface.test_option_ = test_option + + sport_client = LocoClient() + sport_client.SetTimeout(10.0) + sport_client.Init() + + while True: + + user_interface.terminal_handle() + + print(f"Updated Test Option: Name = {test_option.name}, ID = {test_option.id}\n") + + if test_option.id == 0: + sport_client.Damp() + elif test_option.id == 1: + sport_client.StandUp() + elif test_option.id == 3: + sport_client.Move(0.3,0,0) + elif test_option.id == 4: + sport_client.Move(0,0.3,0) + elif test_option.id == 5: + sport_client.Move(0,0,0.3) + elif test_option.id == 6: + sport_client.LowStand() + elif test_option.id == 7: + sport_client.HighStand() + elif test_option.id == 8: + sport_client.ZeroTorque() + + time.sleep(1) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/example/h1/low_level/h1_low_level_example.py b/external_dependencies/unitree_sdk2_python/example/h1/low_level/h1_low_level_example.py new file mode 100644 index 0000000..51a9111 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/h1/low_level/h1_low_level_example.py @@ -0,0 +1,167 @@ +import time +import sys + +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowCmd_ +from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowState_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowCmd_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_ +from unitree_sdk2py.utils.crc import CRC +from unitree_sdk2py.utils.thread import RecurrentThread +from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient +import unitree_legged_const as h1 +import numpy as np + +H1_NUM_MOTOR = 20 + +class H1JointIndex: + # Right leg + kRightHipYaw = 8 + kRightHipRoll = 0 + kRightHipPitch = 1 + kRightKnee = 2 + kRightAnkle = 11 + # Left leg + kLeftHipYaw = 7 + kLeftHipRoll = 3 + kLeftHipPitch = 4 + kLeftKnee = 5 + kLeftAnkle = 10 + + kWaistYaw = 6 + + kNotUsedJoint = 9 + + # Right arm + kRightShoulderPitch = 12 + kRightShoulderRoll = 13 + kRightShoulderYaw = 14 + kRightElbow = 15 + # Left arm + kLeftShoulderPitch = 16 + kLeftShoulderRoll = 17 + kLeftShoulderYaw = 18 + kLeftElbow = 19 + +class Custom: + def __init__(self): + self.time_ = 0.0 + self.control_dt_ = 0.01 + self.duration_ = 10.0 + self.counter_ = 0 + self.kp_low_ = 60.0 + self.kp_high_ = 200.0 + self.kd_low_ = 1.5 + self.kd_high_ = 5.0 + self.low_cmd = unitree_go_msg_dds__LowCmd_() + self.InitLowCmd() + self.low_state = None + self.crc = CRC() + + def Init(self): + # # create publisher # + self.lowcmd_publisher_ = ChannelPublisher("rt/lowcmd", LowCmd_) + self.lowcmd_publisher_.Init() + + # # create subscriber # + self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_) + self.lowstate_subscriber.Init(self.LowStateHandler, 10) + + self.msc = MotionSwitcherClient() + self.msc.SetTimeout(5.0) + self.msc.Init() + + status, result = self.msc.CheckMode() + while result['name']: + self.msc.ReleaseMode() + status, result = self.msc.CheckMode() + time.sleep(1) + + self.report_rpy_ptr_ = RecurrentThread( + interval=0.1, target=self.ReportRPY, name="report_rpy" + ) + + self.report_rpy_ptr_.Start() + + def is_weak_motor(self,motor_index): + return motor_index in { + H1JointIndex.kLeftAnkle, + H1JointIndex.kRightAnkle, + H1JointIndex.kRightShoulderPitch, + H1JointIndex.kRightShoulderRoll, + H1JointIndex.kRightShoulderYaw, + H1JointIndex.kRightElbow, + H1JointIndex.kLeftShoulderPitch, + H1JointIndex.kLeftShoulderRoll, + H1JointIndex.kLeftShoulderYaw, + H1JointIndex.kLeftElbow, + } + + def InitLowCmd(self): + self.low_cmd.head[0] = 0xFE + self.low_cmd.head[1] = 0xEF + self.low_cmd.level_flag = 0xFF + self.low_cmd.gpio = 0 + for i in range(H1_NUM_MOTOR): + if self.is_weak_motor(i): + self.low_cmd.motor_cmd[i].mode = 0x01 + else: + self.low_cmd.motor_cmd[i].mode = 0x0A + self.low_cmd.motor_cmd[i].q= h1.PosStopF + self.low_cmd.motor_cmd[i].kp = 0 + self.low_cmd.motor_cmd[i].dq = h1.VelStopF + self.low_cmd.motor_cmd[i].kd = 0 + self.low_cmd.motor_cmd[i].tau = 0 + + def Start(self): + self.lowCmdWriteThreadPtr = RecurrentThread( + interval=self.control_dt_, target=self.LowCmdWrite, name="control" + ) + self.lowCmdWriteThreadPtr.Start() + + def LowStateHandler(self, msg: LowState_): + self.low_state = msg + + def ReportRPY(self): + print("rpy: [",self.low_state.imu_state.rpy[0],", " + ,self.low_state.imu_state.rpy[1],", " + ,self.low_state.imu_state.rpy[2],"]" + ) + + def LowCmdWrite(self): + self.time_ += self.control_dt_ + self.time_ = np.clip(self.time_ , 0.0, self.duration_) + + # set robot to zero posture + for i in range(H1_NUM_MOTOR): + ratio = self.time_ / self.duration_ + self.low_cmd.motor_cmd[i].tau = 0. + self.low_cmd.motor_cmd[i].q = (1.0 - ratio) * self.low_state.motor_state[i].q + self.low_cmd.motor_cmd[i].dq = 0. + self.low_cmd.motor_cmd[i].kp = self.kp_low_ if self.is_weak_motor(i) else self.kp_high_ + self.low_cmd.motor_cmd[i].kd = self.kd_low_ if self.is_weak_motor(i) else self.kd_high_ + + self.low_cmd.crc = self.crc.Crc(self.low_cmd) + self.lowcmd_publisher_.Write(self.low_cmd) + +if __name__ == '__main__': + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + custom = Custom() + custom.Init() + custom.Start() + + while True: + if custom.time_ == custom.duration_: + time.sleep(1) + print("Done!") + sys.exit(-1) + time.sleep(1) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/example/h1/low_level/unitree_legged_const.py b/external_dependencies/unitree_sdk2_python/example/h1/low_level/unitree_legged_const.py new file mode 100644 index 0000000..c9112ea --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/h1/low_level/unitree_legged_const.py @@ -0,0 +1,5 @@ +HIGHLEVEL = 0xEE +LOWLEVEL = 0xFF +TRIGERLEVEL = 0xF0 +PosStopF = 2.146e9 +VelStopF = 16000.0 diff --git a/external_dependencies/unitree_sdk2_python/example/h1_2/low_level/h1_2_low_level_example.py b/external_dependencies/unitree_sdk2_python/example/h1_2/low_level/h1_2_low_level_example.py new file mode 100644 index 0000000..0959b32 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/h1_2/low_level/h1_2_low_level_example.py @@ -0,0 +1,201 @@ +import time +import sys + +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowCmd_ +from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowState_ +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowCmd_ +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowState_ +from unitree_sdk2py.utils.crc import CRC +from unitree_sdk2py.utils.thread import RecurrentThread +from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient + +import numpy as np + +H1_2_NUM_MOTOR = 27 + +class H1_2_JointIndex: + # legs + LeftHipYaw = 0 + LeftHipPitch = 1 + LeftHipRoll = 2 + LeftKnee = 3 + LeftAnklePitch = 4 + LeftAnkleB = 4 + LeftAnkleRoll = 5 + LeftAnkleA = 5 + RightHipYaw = 6 + RightHipPitch = 7 + RightHipRoll = 8 + RightKnee = 9 + RightAnklePitch = 10 + RightAnkleB = 10 + RightAnkleRoll = 11 + RightAnkleA = 11 + # torso + WaistYaw = 12 + # arms + LeftShoulderPitch = 13 + LeftShoulderRoll = 14 + LeftShoulderYaw = 15 + LeftElbow = 16 + LeftWristRoll = 17 + LeftWristPitch = 18 + LeftWristYaw = 19 + RightShoulderPitch = 20 + RightShoulderRoll = 21 + RightShoulderYaw = 22 + RightElbow = 23 + RightWristRoll = 24 + RightWristPitch = 25 + RightWristYaw = 26 + + +class Mode: + PR = 0 # Series Control for Pitch/Roll Joints + AB = 1 # Parallel Control for A/B Joints + +class Custom: + def __init__(self): + self.time_ = 0.0 + self.control_dt_ = 0.002 # [2ms] + self.duration_ = 3.0 # [3 s] + self.counter_ = 0 + self.mode_pr_ = Mode.PR + self.mode_machine_ = 0 + self.low_cmd = unitree_hg_msg_dds__LowCmd_() + self.low_state = None + self.update_mode_machine_ = False + self.crc = CRC() + + def Init(self): + self.msc = MotionSwitcherClient() + self.msc.SetTimeout(5.0) + self.msc.Init() + + status, result = self.msc.CheckMode() + while result['name']: + self.msc.ReleaseMode() + status, result = self.msc.CheckMode() + time.sleep(1) + + # create publisher # + self.lowcmd_publisher_ = ChannelPublisher("rt/lowcmd", LowCmd_) + self.lowcmd_publisher_.Init() + + # create subscriber # + self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_) + self.lowstate_subscriber.Init(self.LowStateHandler, 10) + + def Start(self): + self.lowCmdWriteThreadPtr = RecurrentThread( + interval=self.control_dt_, target=self.LowCmdWrite, name="control" + ) + while self.update_mode_machine_ == False: + time.sleep(1) + + if self.update_mode_machine_ == True: + self.lowCmdWriteThreadPtr.Start() + + def LowStateHandler(self, msg: LowState_): + self.low_state = msg + + if self.update_mode_machine_ == False: + self.mode_machine_ = self.low_state.mode_machine + self.update_mode_machine_ = True + + self.counter_ +=1 + if (self.counter_ % 500 == 0) : + self.counter_ = 0 + print(self.low_state.imu_state.rpy) + + def LowCmdWrite(self): + self.time_ += self.control_dt_ + self.low_cmd.mode_pr = Mode.PR + self.low_cmd.mode_machine = self.mode_machine_ + for i in range(H1_2_NUM_MOTOR): + ratio = np.clip(self.time_ / self.duration_, 0.0, 1.0) + self.low_cmd.motor_cmd[i].mode = 1 # 1:Enable, 0:Disable + self.low_cmd.motor_cmd[i].tau = 0.0 + self.low_cmd.motor_cmd[i].q = 0.0 + self.low_cmd.motor_cmd[i].dq = 0.0 + self.low_cmd.motor_cmd[i].kp = 100.0 if i < 13 else 50.0 + self.low_cmd.motor_cmd[i].kd = 1.0 + + if self.time_ < self.duration_ : + # [Stage 1]: set robot to zero posture + for i in range(H1_2_NUM_MOTOR): + ratio = np.clip(self.time_ / self.duration_, 0.0, 1.0) + self.low_cmd.mode_pr = Mode.PR + self.low_cmd.mode_machine = self.mode_machine_ + self.low_cmd.motor_cmd[i].mode = 1 # 1:Enable, 0:Disable + self.low_cmd.motor_cmd[i].tau = 0. + self.low_cmd.motor_cmd[i].q = (1.0 - ratio) * self.low_state.motor_state[i].q + self.low_cmd.motor_cmd[i].dq = 0. + self.low_cmd.motor_cmd[i].kp = 100.0 if i < 13 else 50.0 + self.low_cmd.motor_cmd[i].kd = 1.0 + else : + # [Stage 2]: swing ankle using PR mode + max_P = 0.25 + max_R = 0.25 + t = self.time_ - self.duration_ + L_P_des = max_P * np.cos(2.0 * np.pi * t) + L_R_des = max_R * np.sin(2.0 * np.pi * t) + R_P_des = max_P * np.cos(2.0 * np.pi * t) + R_R_des = -max_R * np.sin(2.0 * np.pi * t) + + Kp_Pitch = 80 + Kd_Pitch = 1 + Kp_Roll = 80 + Kd_Roll = 1 + + self.low_cmd.mode_pr = Mode.PR + self.low_cmd.mode_machine = self.mode_machine_ + self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnklePitch].q = L_P_des + self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnklePitch].dq = 0 + self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnklePitch].kp = Kp_Pitch + self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnklePitch].kd = Kd_Pitch + self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnkleRoll].q = L_R_des + self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnkleRoll].dq = 0 + self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnkleRoll].kp = Kp_Roll + self.low_cmd.motor_cmd[H1_2_JointIndex.LeftAnkleRoll].kd = Kd_Roll + self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnklePitch].q = R_P_des + self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnklePitch].dq = 0 + self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnklePitch].kp = Kp_Pitch + self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnklePitch].kd = Kd_Pitch + self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnkleRoll].q = R_R_des + self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnkleRoll].dq = 0 + self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnkleRoll].kp = Kp_Roll + self.low_cmd.motor_cmd[H1_2_JointIndex.RightAnkleRoll].kd = Kd_Roll + + max_wrist_roll_angle = 0.5; # [rad] + WristRoll_des = max_wrist_roll_angle * np.sin(2.0 * np.pi * t) + self.low_cmd.motor_cmd[H1_2_JointIndex.LeftWristRoll].q = WristRoll_des + self.low_cmd.motor_cmd[H1_2_JointIndex.LeftWristRoll].dq = 0 + self.low_cmd.motor_cmd[H1_2_JointIndex.LeftWristRoll].kp = 50 + self.low_cmd.motor_cmd[H1_2_JointIndex.LeftWristRoll].kd = 1 + self.low_cmd.motor_cmd[H1_2_JointIndex.RightWristRoll].q = WristRoll_des + self.low_cmd.motor_cmd[H1_2_JointIndex.RightWristRoll].dq = 0 + self.low_cmd.motor_cmd[H1_2_JointIndex.RightWristRoll].kp = 50 + self.low_cmd.motor_cmd[H1_2_JointIndex.RightWristRoll].kd = 1 + + self.low_cmd.crc = self.crc.Crc(self.low_cmd) + self.lowcmd_publisher_.Write(self.low_cmd) + +if __name__ == '__main__': + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + custom = Custom() + custom.Init() + custom.Start() + + while True: + time.sleep(1) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/example/helloworld/publisher.py b/external_dependencies/unitree_sdk2_python/example/helloworld/publisher.py new file mode 100644 index 0000000..cfe4f1f --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/helloworld/publisher.py @@ -0,0 +1,28 @@ +import time + +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize +from user_data import * + + +if __name__ == "__main__": + ChannelFactoryInitialize() + + # Create a publisher to publish the data defined in UserData class + pub = ChannelPublisher("topic", UserData) + pub.Init() + + for i in range(30): + # Create a Userdata message + msg = UserData(" ", 0) + msg.string_data = "Hello world" + msg.float_data = time.time() + + # Publish message + if pub.Write(msg, 0.5): + print("Publish success. msg:", msg) + else: + print("Waitting for subscriber.") + + time.sleep(1) + + pub.Close() diff --git a/external_dependencies/unitree_sdk2_python/example/helloworld/subscriber.py b/external_dependencies/unitree_sdk2_python/example/helloworld/subscriber.py new file mode 100644 index 0000000..a7d0b63 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/helloworld/subscriber.py @@ -0,0 +1,20 @@ +import time + +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from user_data import * + + +if __name__ == "__main__": + ChannelFactoryInitialize() + # Create a subscriber to subscribe the data defined in UserData class + sub = ChannelSubscriber("topic", UserData) + sub.Init() + + while True: + msg = sub.Read() + if msg is not None: + print("Subscribe success. msg:", msg) + else: + print("No data subscribed.") + break + sub.Close() diff --git a/external_dependencies/unitree_sdk2_python/example/helloworld/user_data.py b/external_dependencies/unitree_sdk2_python/example/helloworld/user_data.py new file mode 100644 index 0000000..9696ce3 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/helloworld/user_data.py @@ -0,0 +1,9 @@ +from dataclasses import dataclass +from cyclonedds.idl import IdlStruct + + +# This class defines user data consisting of a float data and a string data +@dataclass +class UserData(IdlStruct, typename="UserData"): + string_data: str + float_data: float diff --git a/external_dependencies/unitree_sdk2_python/example/motionSwitcher/motion_switcher_example.py b/external_dependencies/unitree_sdk2_python/example/motionSwitcher/motion_switcher_example.py new file mode 100644 index 0000000..4f39f72 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/motionSwitcher/motion_switcher_example.py @@ -0,0 +1,36 @@ +import time +import sys + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient + + +class Custom: + def __init__(self): + self.msc = MotionSwitcherClient() + self.msc.SetTimeout(5.0) + self.msc.Init() + + def selectMode(self,name): + ret = self.msc.SelectMode(name) + return ret + + +if __name__ == '__main__': + + print("WARNING: Please ensure there are no obstacles around the robot while running this example.") + input("Press Enter to continue...") + + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + custom = Custom() + selectMode = "ai" + # selectMode = "normal" + # selectMode = "advanced" + # selectMode = "ai-w" # for wheeled robot + ret = custom.selectMode(selectMode) + print("ret: ",ret) + diff --git a/external_dependencies/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_move.py b/external_dependencies/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_move.py new file mode 100644 index 0000000..284f8ea --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_move.py @@ -0,0 +1,35 @@ +import time +import sys + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.go2.obstacles_avoid.obstacles_avoid_client import ObstaclesAvoidClient + +if __name__ == "__main__": + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + try: + client = ObstaclesAvoidClient() + client.SetTimeout(3.0) + client.Init() + + while not client.SwitchGet()[1]: + client.SwitchSet(True) + time.sleep(0.1) + + print("obstacles avoid switch on") + + client.UseRemoteCommandFromApi(True) + time.sleep(0.5) + client.Move(0.5, 0.0, 0.0) + time.sleep(1.0) # move 1s + client.Move(0.0, 0.0, 0.0) + client.UseRemoteCommandFromApi(False) + + except KeyboardInterrupt: + client.Move(0.0, 0.0, 0.0) + client.UseRemoteCommandFromApi(False) + print("exit!!") + \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_switch.py b/external_dependencies/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_switch.py new file mode 100644 index 0000000..eb345c0 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_switch.py @@ -0,0 +1,94 @@ +import time +import sys + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.go2.obstacles_avoid.obstacles_avoid_client import ObstaclesAvoidClient + +if __name__ == "__main__": + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + client = ObstaclesAvoidClient() + client.SetTimeout(3.0) + client.Init() + + while True: + print("##################GetServerApiVersion###################") + code, serverAPiVersion = client.GetServerApiVersion() + if code != 0: + print("get server api error. code:", code) + else: + print("get server api version:", serverAPiVersion) + + if serverAPiVersion != client.GetApiVersion(): + print("api version not equal.") + + time.sleep(3) + + print("##################SwitchGet###################") + code, enable = client.SwitchGet() + if code != 0: + print("switch get error. code:", code) + else: + print("switch get success. enable:", enable) + + time.sleep(3) + + print("##################SwitchSet (on)###################") + code = client.SwitchSet(True) + if code != 0: + print("switch set error. code:", code) + else: + print("switch set success.") + + time.sleep(3) + + print("##################SwitchGet###################") + code, enable1 = client.SwitchGet() + if code != 0: + print("switch get error. code:", code) + else: + print("switch get success. enable:", enable1) + + time.sleep(3) + + print("##################SwitchSet (off)###################") + code = client.SwitchSet(False) + if code != 0: + print("switch set error. code:", code) + else: + print("switch set success.") + + time.sleep(3) + + print("##################SwitchGet###################") + code, enable1 = client.SwitchGet() + if code != 0: + print("switch get error. code:", code) + else: + print("switch get success. enable:", enable1) + + time.sleep(3) + + + print("##################SwitchSet (enable)###################") + + code = client.SwitchSet(enable) + if code != 0: + print("switch set error. code:", code) + else: + print("switch set success. enable:", enable) + + time.sleep(3) + + print("##################SwitchGet###################") + code, enable = client.SwitchGet() + if code != 0: + print("switch get error. code:", code) + else: + print("switch get success. enable:", enable) + + time.sleep(3) + \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/example/vui_client/vui_client_example.py b/external_dependencies/unitree_sdk2_python/example/vui_client/vui_client_example.py new file mode 100644 index 0000000..a5e6932 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/vui_client/vui_client_example.py @@ -0,0 +1,77 @@ +import time +import sys + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.go2.vui.vui_client import VuiClient + +if __name__ == "__main__": + if len(sys.argv)>1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + client = VuiClient() + client.SetTimeout(3.0) + client.Init() + + for i in range(1, 11): + print("#################GetBrightness####################") + code, level = client.GetBrightness() + + if code != 0: + print("get brightness error. code:", code) + else: + print("get brightness success. level:", level) + + time.sleep(1) + + print("#################SetBrightness####################") + + code = client.SetBrightness(i) + + if code != 0: + print("set brightness error. code:", code) + else: + print("set brightness success. level:", i) + + time.sleep(1) + + print("#################SetBrightness 0####################") + + code = client.SetBrightness(0) + + if code != 0: + print("set brightness error. code:", code) + else: + print("set brightness 0 success.") + + for i in range(1, 11): + print("#################GetVolume####################") + code, level = client.GetVolume() + + if code != 0: + print("get volume error. code:", code) + else: + print("get volume success. level:", level) + + time.sleep(1) + + print("#################SetVolume####################") + + code = client.SetVolume(i) + + if code != 0: + print("set volume error. code:", code) + else: + print("set volume success. level:", i) + + time.sleep(1) + + print("#################SetVolume 0####################") + + code = client.SetVolume(0) + + if code != 0: + print("set volume error. code:", code) + else: + print("set volume 0 success.") diff --git a/external_dependencies/unitree_sdk2_python/example/wireless_controller/wireless_controller.py b/external_dependencies/unitree_sdk2_python/example/wireless_controller/wireless_controller.py new file mode 100644 index 0000000..995487f --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/example/wireless_controller/wireless_controller.py @@ -0,0 +1,131 @@ +import time +import sys +import struct + +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize + +# Uncomment the following two lines when using Go2、Go2-W、B2、B2-W、H1 robot +# from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowState_ +# from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_ + +# Uncomment the following two lines when using G1、H1-2 robot +from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowState_ +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowState_ + +class unitreeRemoteController: + def __init__(self): + # key + self.Lx = 0 + self.Rx = 0 + self.Ry = 0 + self.Ly = 0 + + # button + self.L1 = 0 + self.L2 = 0 + self.R1 = 0 + self.R2 = 0 + self.A = 0 + self.B = 0 + self.X = 0 + self.Y = 0 + self.Up = 0 + self.Down = 0 + self.Left = 0 + self.Right = 0 + self.Select = 0 + self.F1 = 0 + self.F3 = 0 + self.Start = 0 + + def parse_botton(self,data1,data2): + self.R1 = (data1 >> 0) & 1 + self.L1 = (data1 >> 1) & 1 + self.Start = (data1 >> 2) & 1 + self.Select = (data1 >> 3) & 1 + self.R2 = (data1 >> 4) & 1 + self.L2 = (data1 >> 5) & 1 + self.F1 = (data1 >> 6) & 1 + self.F3 = (data1 >> 7) & 1 + self.A = (data2 >> 0) & 1 + self.B = (data2 >> 1) & 1 + self.X = (data2 >> 2) & 1 + self.Y = (data2 >> 3) & 1 + self.Up = (data2 >> 4) & 1 + self.Right = (data2 >> 5) & 1 + self.Down = (data2 >> 6) & 1 + self.Left = (data2 >> 7) & 1 + + def parse_key(self,data): + lx_offset = 4 + self.Lx = struct.unpack('1: + ChannelFactoryInitialize(0, sys.argv[1]) + else: + ChannelFactoryInitialize(0) + + custom = Custom() + custom.Init() + + while True: + time.sleep(1) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/pyproject.toml b/external_dependencies/unitree_sdk2_python/pyproject.toml new file mode 100644 index 0000000..07de284 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/setup.py b/external_dependencies/unitree_sdk2_python/setup.py new file mode 100644 index 0000000..54d8cac --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/setup.py @@ -0,0 +1,21 @@ +from setuptools import setup, find_packages + +setup(name='unitree_sdk2py', + version='1.0.1', + author='UnitreeRobotics', + author_email='unitree@unitree.com', + long_description=open('README.md').read(), + long_description_content_type="text/markdown", + license="BSD-3-Clause", + packages=find_packages(include=['unitree_sdk2py','unitree_sdk2py.*']), + description='Unitree robot sdk version 2 for python', + project_urls={ + "Source Code": "https://github.com/unitreerobotics/unitree_sdk2_python", + }, + python_requires='>=3.8', + install_requires=[ + "cyclonedds==0.10.2", + "numpy", + "opencv-python", + ], + ) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/__init__.py new file mode 100644 index 0000000..f071aeb --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/__init__.py @@ -0,0 +1,10 @@ +from . import idl, utils, core, rpc, go2, b2 + +__all__ = [ + "idl" + "utils" + "core", + "rpc", + "go2", + "b2", +] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_api.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_api.py new file mode 100644 index 0000000..16f566b --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_api.py @@ -0,0 +1,16 @@ +""" +" service name +""" +ROBOT_BACK_VIDEO_SERVICE_NAME = "back_videohub" + + +""" +" service api version +""" +ROBOT_BACK_VIDEO_API_VERSION = "1.0.0.0" + + +""" +" api id +""" +ROBOT_BACK_VIDEO_API_ID_GETIMAGESAMPLE = 1001 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_client.py new file mode 100644 index 0000000..e19c97f --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_client.py @@ -0,0 +1,23 @@ +import json + +from ...rpc.client import Client +from .back_video_api import * + + +""" +" class FrontVideoClient +""" +class BackVideoClient(Client): + def __init__(self): + super().__init__(ROBOT_BACK_VIDEO_SERVICE_NAME, False) + + + def Init(self): + # set api version + self._SetApiVerson(ROBOT_BACK_VIDEO_API_VERSION) + # regist api + self._RegistApi(ROBOT_BACK_VIDEO_API_ID_GETIMAGESAMPLE, 0) + + # 1001 + def GetImageSample(self): + return self._CallBinary(ROBOT_BACK_VIDEO_API_ID_GETIMAGESAMPLE, []) diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_api.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_api.py new file mode 100644 index 0000000..6f4717a --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_api.py @@ -0,0 +1,16 @@ +""" +" service name +""" +ROBOT_FRONT_VIDEO_SERVICE_NAME = "front_videohub" + + +""" +" service api version +""" +ROBOT_FRONT_VIDEO_API_VERSION = "1.0.0.0" + + +""" +" api id +""" +ROBOT_FRONT_VIDEO_API_ID_GETIMAGESAMPLE = 1001 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_client.py new file mode 100644 index 0000000..5ba065a --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_client.py @@ -0,0 +1,23 @@ +import json + +from ...rpc.client import Client +from .front_video_api import * + + +""" +" class FrontVideoClient +""" +class FrontVideoClient(Client): + def __init__(self): + super().__init__(ROBOT_FRONT_VIDEO_SERVICE_NAME, False) + + + def Init(self): + # set api version + self._SetApiVerson(ROBOT_FRONT_VIDEO_API_VERSION) + # regist api + self._RegistApi(ROBOT_FRONT_VIDEO_API_ID_GETIMAGESAMPLE, 0) + + # 1001 + def GetImageSample(self): + return self._CallBinary(ROBOT_FRONT_VIDEO_API_ID_GETIMAGESAMPLE, []) diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_api.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_api.py new file mode 100644 index 0000000..fde54df --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_api.py @@ -0,0 +1,25 @@ +""" +" service name +""" +ROBOT_STATE_SERVICE_NAME = "robot_state" + + +""" +" service api version +""" +ROBOT_STATE_API_VERSION = "1.0.0.1" + + +""" +" api id +""" +ROBOT_STATE_API_ID_SERVICE_SWITCH = 1001 +ROBOT_STATE_API_ID_REPORT_FREQ = 1002 +ROBOT_STATE_API_ID_SERVICE_LIST = 1003 + + +""" +" error code +""" +ROBOT_STATE_ERR_SERVICE_SWITCH = 5201 +ROBOT_STATE_ERR_SERVICE_PROTECTED = 5202 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_client.py new file mode 100644 index 0000000..5b2e74d --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_client.py @@ -0,0 +1,84 @@ +import json + +from ...rpc.client import Client +from ...rpc.client_internal import * +from .robot_state_api import * + + +""" +" class ServiceState +""" +class ServiceState: + def __init__(self, name: str = None, status: int = None, protect: bool = None): + self.name = name + self.status = status + self.protect = protect + +""" +" class RobotStateClient +""" +class RobotStateClient(Client): + def __init__(self): + super().__init__(ROBOT_STATE_SERVICE_NAME, False) + + def Init(self): + # set api version + self._SetApiVerson(ROBOT_STATE_API_VERSION) + # regist api + self._RegistApi(ROBOT_STATE_API_ID_SERVICE_SWITCH, 0) + self._RegistApi(ROBOT_STATE_API_ID_REPORT_FREQ, 0) + self._RegistApi(ROBOT_STATE_API_ID_SERVICE_LIST, 0) + + def ServiceList(self): + p = {} + parameter = json.dumps(p) + + code, data = self._Call(ROBOT_STATE_API_ID_SERVICE_LIST, parameter) + + if code != 0: + return code, None + + lst = [] + + d = json.loads(data) + for t in d: + s = ServiceState() + s.name = t["name"] + s.status = t["status"] + s.protect = t["protect"] + lst.append(s) + + return code, lst + + + def ServiceSwitch(self, name: str, switch: bool): + p = {} + p["name"] = name + p["switch"] = int(switch) + parameter = json.dumps(p) + + code, data = self._Call(ROBOT_STATE_API_ID_SERVICE_SWITCH, parameter) + + if code != 0: + return code + + d = json.loads(data) + + status = d["status"] + + if status == 5: + return ROBOT_STATE_ERR_SERVICE_PROTECTED + + if status != 0 and status != 1: + return ROBOT_STATE_ERR_SERVICE_SWITCH + + return code + + def SetReportFreq(self, interval: int, duration: int): + p = {} + p["interval"] = interval + p["duration"] = duration + parameter = json.dumps(p) + + code, data = self._Call(ROBOT_STATE_API_ID_REPORT_FREQ, p) + return code diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_api.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_api.py new file mode 100644 index 0000000..72520b6 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_api.py @@ -0,0 +1,58 @@ +""" +" service name +""" +SPORT_SERVICE_NAME = "sport" + + +""" +" service api version +""" +SPORT_API_VERSION = "1.0.0.1" + + +""" +" api id +""" +SPORT_API_ID_DAMP = 1001; +SPORT_API_ID_BALANCESTAND = 1002; +SPORT_API_ID_STOPMOVE = 1003; +SPORT_API_ID_STANDUP = 1004; +SPORT_API_ID_STANDDOWN = 1005; +SPORT_API_ID_RECOVERYSTAND = 1006; +SPORT_API_ID_EULER = 1007; +SPORT_API_ID_MOVE = 1008; +SPORT_API_ID_SIT = 1009; +SPORT_API_ID_RISESIT = 1010; +SPORT_API_ID_SWITCHGAIT = 1011; +SPORT_API_ID_TRIGGER = 1012; +SPORT_API_ID_BODYHEIGHT = 1013; +SPORT_API_ID_FOOTRAISEHEIGHT = 1014; +SPORT_API_ID_SPEEDLEVEL = 1015; +SPORT_API_ID_HELLO = 1016; +SPORT_API_ID_STRETCH = 1017; +SPORT_API_ID_TRAJECTORYFOLLOW = 1018; +SPORT_API_ID_CONTINUOUSGAIT = 1019; +SPORT_API_ID_CONTENT = 1020; +SPORT_API_ID_WALLOW = 1021; +SPORT_API_ID_DANCE1 = 1022; +SPORT_API_ID_DANCE2 = 1023; +SPORT_API_ID_GETBODYHEIGHT = 1024; +SPORT_API_ID_GETFOOTRAISEHEIGHT = 1025; +SPORT_API_ID_GETSPEEDLEVEL = 1026; +SPORT_API_ID_SWITCHJOYSTICK = 1027; +SPORT_API_ID_POSE = 1028; +SPORT_API_ID_FRONTJUMP = 1031; +SPORT_API_ID_ECONOMICGAIT = 1035; +SPORT_API_ID_MOVETOPOS = 1036; +SPORT_API_ID_SWITCHEULERMODE = 1037; +SPORT_API_ID_SWITCHMOVEMODE = 1038; + + +""" +" error code +""" +# client side +SPORT_ERR_CLIENT_POINT_PATH = 4101 +# server side +SPORT_ERR_SERVER_OVERTIME = 4201 +SPORT_ERR_SERVER_NOT_INIT = 4202 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_client.py new file mode 100644 index 0000000..9ca34a3 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_client.py @@ -0,0 +1,241 @@ +import json + +from ...rpc.client import Client +from .sport_api import * + +""" +" SPORT_PATH_POINT_SIZE +""" +SPORT_PATH_POINT_SIZE = 30 + + +""" +" class PathPoint +""" +class PathPoint: + def __init__(self, timeFromStart: float, x: float, y: float, yaw: float, vx: float, vy: float, vyaw: float): + self.timeFromStart = timeFromStart + self.x = x + self.y = y + self.yaw = yaw + self.vx = vx + self.vy = vy + self.vyaw = vyaw + + +""" +" class SportClient +""" +class SportClient(Client): + def __init__(self, enableLease: bool = False): + super().__init__(SPORT_SERVICE_NAME, enableLease) + + + def Init(self): + # set api version + self._SetApiVerson(SPORT_API_VERSION) + + # regist api + self._RegistApi(SPORT_API_ID_DAMP, 0) + self._RegistApi(SPORT_API_ID_BALANCESTAND, 0) + self._RegistApi(SPORT_API_ID_STOPMOVE, 0) + self._RegistApi(SPORT_API_ID_STANDUP, 0) + self._RegistApi(SPORT_API_ID_STANDDOWN, 0) + self._RegistApi(SPORT_API_ID_RECOVERYSTAND, 0) + self._RegistApi(SPORT_API_ID_EULER, 0) + self._RegistApi(SPORT_API_ID_MOVE, 0) + self._RegistApi(SPORT_API_ID_SIT, 0) + self._RegistApi(SPORT_API_ID_SWITCHGAIT, 0) + self._RegistApi(SPORT_API_ID_BODYHEIGHT, 0) + self._RegistApi(SPORT_API_ID_FOOTRAISEHEIGHT, 0) + self._RegistApi(SPORT_API_ID_SPEEDLEVEL, 0) + self._RegistApi(SPORT_API_ID_TRAJECTORYFOLLOW, 0) + self._RegistApi(SPORT_API_ID_CONTINUOUSGAIT, 0) + self._RegistApi(SPORT_API_ID_MOVETOPOS, 0) + self._RegistApi(SPORT_API_ID_FRONTJUMP, 0) + self._RegistApi(SPORT_API_ID_ECONOMICGAIT, 0) + self._RegistApi(SPORT_API_ID_POSE, 0) + self._RegistApi(SPORT_API_ID_SWITCHEULERMODE, 0) + self._RegistApi(SPORT_API_ID_SWITCHMOVEMODE, 0) + + # 1001 + def Damp(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_DAMP, parameter) + return code + + # 1002 + def BalanceStand(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_BALANCESTAND, parameter) + return code + + # 1003 + def StopMove(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_STOPMOVE, parameter) + return code + + # 1004 + def StandUp(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_STANDUP, parameter) + return code + + # 1005 + def StandDown(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_STANDDOWN, parameter) + return code + + # 1006 + def RecoveryStand(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_RECOVERYSTAND, parameter) + return code + + # 1007 + def Euler(self, roll: float, pitch: float, yaw: float): + p = {} + p["x"] = roll + p["y"] = pitch + p["z"] = yaw + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_EULER, parameter) + return code + + # 1008 + def Move(self, vx: float, vy: float, vyaw: float): + p = {} + p["x"] = vx + p["y"] = vy + p["z"] = vyaw + parameter = json.dumps(p) + code = self._CallNoReply(SPORT_API_ID_MOVE, parameter) + return code + + # 1009 + def Sit(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_SIT, parameter) + return code + + # 1011 + def SwitchGait(self, t: int): + p = {} + p["data"] = t + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_SWITCHGAIT, parameter) + return code + + # 1013 + def BodyHeight(self, height: float): + p = {} + p["data"] = height + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_BODYHEIGHT, parameter) + return code + + # 1014 + def FootRaiseHeight(self, height: float): + p = {} + p["data"] = height + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_FOOTRAISEHEIGHT, parameter) + return code + + # 1015 + def SpeedLevel(self, level: int): + p = {} + p["data"] = level + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_SPEEDLEVEL, parameter) + return code + + # 1018 + def TrajectoryFollow(self, path: list): + l = len(path) + if l != SPORT_PATH_POINT_SIZE: + return SPORT_ERR_CLIENT_POINT_PATH + + path_p = [] + for i in range(l): + point = path[i] + p = {} + p["t_from_start"] = point.timeFromStart + p["x"] = point.x + p["y"] = point.y + p["yaw"] = point.yaw + p["vx"] = point.vx + p["vy"] = point.vy + p["vyaw"] = point.vyaw + path_p.append(p) + + parameter = json.dumps(path_p) + code = self._CallNoReply(SPORT_API_ID_TRAJECTORYFOLLOW, parameter) + return code + + # 1019 + def ContinuousGait(self, flag: int): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_CONTINUOUSGAIT, parameter) + return code + + # 1036 + def MoveToPos(self, x: float, y: float, yaw: float): + p = {} + p["x"] = x + p["y"] = y + p["yaw"] = yaw + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_MOVETOPOS, parameter) + return code + + # 1031 + def FrontJump(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_FRONTJUMP, parameter) + return code + + + # 1035 + def EconomicGait(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_ECONOMICGAIT, parameter) + return code + + # 1028 + def Pose(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_POSE, parameter) + return code + + # 1037 + def SwitchEulerMode(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_SWITCHEULERMODE, parameter) + return code + + # 1038 + def SwitchMoveMode(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_SWITCHMOVEMODE, parameter) + return code diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_api.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_api.py new file mode 100644 index 0000000..d5dcd34 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_api.py @@ -0,0 +1,21 @@ +""" +" service name +""" +VUI_SERVICE_NAME = "vui" + + +""" +" service api version +""" +VUI_API_VERSION = "1.0.0.1" + + +""" +" api id +""" +VUI_API_ID_SETSWITCH = 1001 +VUI_API_ID_GETSWITCH = 1002 +VUI_API_ID_SETVOLUME = 1003 +VUI_API_ID_GETVOLUME = 1004 +VUI_API_ID_SETBRIGHTNESS = 1005 +VUI_API_ID_GETBRIGHTNESS = 1006 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_client.py new file mode 100644 index 0000000..234f285 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_client.py @@ -0,0 +1,86 @@ +import json + +from ...rpc.client import Client +from .vui_api import * + + +""" +" class VideoClient +""" +class VuiClient(Client): + def __init__(self): + super().__init__(VUI_SERVICE_NAME, False) + + def Init(self): + # set api version + self._SetApiVerson(VUI_API_VERSION) + # regist api + self._RegistApi(VUI_API_ID_SETSWITCH, 0) + self._RegistApi(VUI_API_ID_GETSWITCH, 0) + self._RegistApi(VUI_API_ID_SETVOLUME, 0) + self._RegistApi(VUI_API_ID_GETVOLUME, 0) + self._RegistApi(VUI_API_ID_SETBRIGHTNESS, 0) + self._RegistApi(VUI_API_ID_GETBRIGHTNESS, 0) + + # 1001 + def SetSwitch(self, enable: int): + p = {} + p["enable"] = enable + parameter = json.dumps(p) + + code, data = self._Call(VUI_API_ID_SETSWITCH, parameter) + return code + + # 1002 + def GetSwitch(self): + p = {} + parameter = json.dumps(p) + + code, data = self._Call(VUI_API_ID_GETSWITCH, parameter) + if code == 0: + d = json.loads(data) + return code, d["enable"] + else: + return code, None + + # 1003 + def SetVolume(self, level: int): + p = {} + p["volume"] = level + parameter = json.dumps(p) + + code, data = self._Call(VUI_API_ID_SETVOLUME, parameter) + return code + + # 1006 + def GetVolume(self): + p = {} + parameter = json.dumps(p) + + code, data = self._Call(VUI_API_ID_GETVOLUME, parameter) + if code == 0: + d = json.loads(data) + return code, d["volume"] + else: + return code, None + + # 1005 + def SetBrightness(self, level: int): + p = {} + p["brightness"] = level + parameter = json.dumps(p) + + code, data = self._Call(VUI_API_ID_SETBRIGHTNESS, parameter) + return code + + # 1006 + def GetBrightness(self): + p = {} + parameter = json.dumps(p) + + code, data = self._Call(VUI_API_ID_GETBRIGHTNESS, parameter) + if code == 0: + d = json.loads(data) + return code, d["brightness"] + else: + return code, None \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_api.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_api.py new file mode 100644 index 0000000..ddb9a42 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_api.py @@ -0,0 +1,29 @@ +""" +" service name +""" +MOTION_SWITCHER_SERVICE_NAME = "motion_switcher" + + +""" +" service api version +""" +MOTION_SWITCHER_API_VERSION = "1.0.0.1" + + +""" +" api id +""" +MOTION_SWITCHER_API_ID_CHECK_MODE = 1001 +MOTION_SWITCHER_API_ID_SELECT_MODE = 1002 +MOTION_SWITCHER_API_ID_RELEASE_MODE = 1003 +MOTION_SWITCHER_API_ID_SET_SILENT = 1004 +MOTION_SWITCHER_API_ID_GET_SILENT = 1005 + +# """ +# " error code +# """ +# # client side +# SPORT_ERR_CLIENT_POINT_PATH = 4101 +# # server side +# SPORT_ERR_SERVER_OVERTIME = 4201 +# SPORT_ERR_SERVER_NOT_INIT = 4202 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_client.py new file mode 100644 index 0000000..1ad411a --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_client.py @@ -0,0 +1,51 @@ +import json + +from ...rpc.client import Client +from .motion_switcher_api import * + +""" +" class MotionSwitcherClient +""" +class MotionSwitcherClient(Client): + def __init__(self): + super().__init__(MOTION_SWITCHER_SERVICE_NAME, False) + + + def Init(self): + # set api version + self._SetApiVerson(MOTION_SWITCHER_API_VERSION) + + # regist api + self._RegistApi(MOTION_SWITCHER_API_ID_CHECK_MODE, 0) + self._RegistApi(MOTION_SWITCHER_API_ID_SELECT_MODE, 0) + self._RegistApi(MOTION_SWITCHER_API_ID_RELEASE_MODE, 0) + self._RegistApi(MOTION_SWITCHER_API_ID_SET_SILENT, 0) + self._RegistApi(MOTION_SWITCHER_API_ID_GET_SILENT, 0) + + # 1001 + def CheckMode(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(MOTION_SWITCHER_API_ID_CHECK_MODE, parameter) + if code == 0: + return code, json.loads(data) + else: + return code, None + + # 1002 + def SelectMode(self, nameOrAlias): + p = {} + p["name"] = nameOrAlias + parameter = json.dumps(p) + code, data = self._Call(MOTION_SWITCHER_API_ID_SELECT_MODE, parameter) + + return code, None + + # 1003 + def ReleaseMode(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(MOTION_SWITCHER_API_ID_RELEASE_MODE, parameter) + + return code, None + \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel.py new file mode 100644 index 0000000..daacc63 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel.py @@ -0,0 +1,290 @@ +import time +from typing import Any, Callable +from threading import Thread, Event + +from cyclonedds.domain import Domain, DomainParticipant +from cyclonedds.internal import dds_c_t +from cyclonedds.pub import DataWriter +from cyclonedds.sub import DataReader +from cyclonedds.topic import Topic +from cyclonedds.qos import Qos +from cyclonedds.core import DDSException, Listener +from cyclonedds.util import duration +from cyclonedds.internal import dds_c_t, InvalidSample + +# for channel config +from .channel_config import ChannelConfigAutoDetermine, ChannelConfigHasInterface + +# for singleton +from ..utils.singleton import Singleton +from ..utils.bqueue import BQueue + + +""" +" class ChannelReader +""" + +""" +" class Channel +""" +class Channel: + + """ + " internal class __Reader + """ + class __Reader: + def __init__(self): + self.__reader = None + self.__handler = None + self.__queue = None + self.__queueEnable = False + self.__threadEvent = None + self.__threadReader = None + + def Init(self, participant: DomainParticipant, topic: Topic, qos: Qos = None, handler: Callable = None, queueLen: int = 0): + if handler is None: + self.__reader = DataReader(participant, topic, qos) + else: + self.__handler = handler + if queueLen > 0: + self.__queueEnable = True + self.__queue = BQueue(queueLen) + self.__threadEvent = Event() + self.__threadReader = Thread(target=self.__ChannelReaderThreadFunc, name="ch_reader", daemon=True) + self.__threadReader.start() + self.__reader = DataReader(participant, topic, qos, Listener(on_data_available=self.__OnDataAvailable)) + + def Read(self, timeout: float = None): + sample = None + try: + if timeout is None: + sample = self.__reader.take_one() + else: + sample = self.__reader.take_one(timeout=duration(seconds=timeout)) + except DDSException as e: + print("[Reader] catch DDSException msg:", e.msg) + except TimeoutError as e: + print("[Reader] take sample timeout") + except: + print("[Reader] take sample error") + + return sample + + def Close(self): + if self.__reader is not None: + del self.__reader + + if self.__queueEnable: + self.__threadEvent.set() + self.__queue.Interrupt() + self.__queue.Clear() + self.__threadReader.join() + + def __OnDataAvailable(self, reader: DataReader): + samples = [] + try: + samples = reader.take(1) + except DDSException as e: + print("[Reader] catch DDSException error. msg:", e.msg) + return + except TimeoutError as e: + print("[Reader] take sample timeout") + return + except: + print("[Reader] take sample error") + return + + if samples is None: + return + + # check invalid sample + sample = samples[0] + if isinstance(sample, InvalidSample): + return + + # do sample + if self.__queueEnable: + self.__queue.Put(sample) + else: + self.__handler(sample) + + def __ChannelReaderThreadFunc(self): + while not self.__threadEvent.is_set(): + sample = self.__queue.Get() + if sample is not None: + self.__handler(sample) + + """ + " internal class __Writer + """ + class __Writer: + def __init__(self): + self.__writer = None + self.__publication_matched_count = 0 + + def Init(self, participant: DomainParticipant, topic: Topic, qos: Qos = None): + self.__writer = DataWriter(participant, topic, qos, Listener(on_publication_matched=self.__OnPublicationMatched)) + time.sleep(0.2) + + def Write(self, sample: Any, timeout: float = None): + waitsec = 0.0 if timeout is None else timeout + + # check publication_matched_count + while waitsec > 0.0 and self.__publication_matched_count == 0: + time.sleep(0.1) + waitsec = waitsec - 0.1 + # print(time.time()) + + # check waitsec + if timeout is not None and waitsec <= 0.0: + return False + + try: + self.__writer.write(sample) + except DDSException as e: + print("[Writer] catch DDSException error. msg:", e.msg) + return False + except Exception as e: + print("[Writer] write sample error. msg:", e.args()) + return False + + return True + + def Close(self): + if self.__writer is not None: + del self.__writer + + def __OnPublicationMatched(self, writer: DataWriter, status: dds_c_t.publication_matched_status): + self.__publication_matched_count = status.current_count + + + # channel __init__ + def __init__(self, participant: DomainParticipant, name: str, type: Any, qos: Qos = None): + self.__reader = self.__Reader() + self.__writer = self.__Writer() + self.__participant = participant + self.__topic = Topic(self.__participant, name, type, qos) + + def SetWriter(self, qos: Qos = None): + self.__writer.Init(self.__participant, self.__topic, qos) + + def SetReader(self, qos: Qos = None, handler: Callable = None, queueLen: int = 0): + self.__reader.Init(self.__participant, self.__topic, qos, handler, queueLen) + + def Write(self, sample: Any, timeout: float = None): + return self.__writer.Write(sample, timeout) + + def Read(self, timeout: float = None): + return self.__reader.Read(timeout) + + def CloseReader(self): + self.__reader.Close() + + def CloseWriter(self): + self.__writer.Close() + + +""" +" class ChannelFactory +""" +class ChannelFactory(Singleton): + __domain = None + __participant = None + __qos = None + + def __init__(self): + super().__init__() + + def Init(self, id: int, networkInterface: str = None, qos: Qos = None): + config = None + # choose config + if networkInterface is None: + config = ChannelConfigAutoDetermine + else: + config = ChannelConfigHasInterface.replace('$__IF_NAME__$', networkInterface) + + try: + self.__domain = Domain(id, config) + except DDSException as e: + print("[ChannelFactory] create domain error. msg:", e.msg) + return False + except: + print("[ChannelFactory] create domain error.") + return False + + try: + self.__participant = DomainParticipant(id) + except DDSException as e: + print("[ChannelFactory] create domain participant error. msg:", e.msg) + return False + except: + print("[ChannelFactory] create domain participant error") + return False + + self.__qos = qos + + return True + + def CreateChannel(self, name: str, type: Any): + return Channel(self.__participant, name, type, self.__qos) + + def CreateSendChannel(self, name: str, type: Any): + channel = self.CreateChannel(name, type) + channel.SetWriter(None) + return channel + + def CreateRecvChannel(self, name: str, type: Any, handler: Callable = None, queueLen: int = 0): + channel = self.CreateChannel(name, type) + channel.SetReader(None, handler, queueLen) + return channel + + +""" +" class ChannelPublisher +""" +class ChannelPublisher: + def __init__(self, name: str, type: Any): + factory = ChannelFactory() + self.__channel = factory.CreateChannel(name, type) + self.__inited = False + + def Init(self): + if not self.__inited: + self.__channel.SetWriter(None) + self.__inited = True + + def Close(self): + self.__channel.CloseWriter() + self.__inited = False + + def Write(self, sample: Any, timeout: float = None): + return self.__channel.Write(sample, timeout) + +""" +" class ChannelSubscriber +""" +class ChannelSubscriber: + def __init__(self, name: str, type: Any): + factory = ChannelFactory() + self.__channel = factory.CreateChannel(name, type) + self.__inited = False + + def Init(self, handler: Callable = None, queueLen: int = 0): + if not self.__inited: + self.__channel.SetReader(None, handler, queueLen) + self.__inited = True + + def Close(self): + self.__channel.CloseReader() + self.__inited = False + + def Read(self, timeout: float = None): + return self.__channel.Read(timeout) + +""" +" function ChannelFactoryInitialize. used to intialize channel everenment. +""" +def ChannelFactoryInitialize(id: int = 0, networkInterface: str = None): + factory = ChannelFactory() + if not factory.Init(id, networkInterface): + raise Exception("channel factory init error.") diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel_config.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel_config.py new file mode 100644 index 0000000..19a67a4 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel_config.py @@ -0,0 +1,25 @@ +ChannelConfigHasInterface = ''' + + + + + + + + + config + /tmp/cdds.LOG + + + ''' + +ChannelConfigAutoDetermine = ''' + + + + + + + + + ''' diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel_name.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel_name.py new file mode 100644 index 0000000..722e408 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/core/channel_name.py @@ -0,0 +1,34 @@ +from enum import Enum + +""" +" Enum ChannelType +""" +class ChannelType(Enum): + SEND = 0 + RECV = 1 + +""" +" function GetClientChannelName +""" +def GetClientChannelName(serviceName: str, channelType: ChannelType): + name = "rt/api/" + serviceName + + if channelType == ChannelType.SEND: + name += "/request" + else: + name += "/response" + + return name + +""" +" function GetClientChannelName +""" +def GetServerChannelName(serviceName: str, channelType: ChannelType): + name = "rt/api/" + serviceName + + if channelType == ChannelType.SEND: + name += "/response" + else: + name += "/request" + + return name \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_api.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_api.py new file mode 100644 index 0000000..9aba780 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_api.py @@ -0,0 +1,24 @@ +""" +" service name +""" +AUDIO_SERVICE_NAME = "voice" + +""" +" service api version +""" +AUDIO_API_VERSION = "1.0.0.0" + +""" +" api id +""" +ROBOT_API_ID_AUDIO_TTS = 1001 +ROBOT_API_ID_AUDIO_ASR = 1002 +ROBOT_API_ID_AUDIO_START_PLAY = 1003 +ROBOT_API_ID_AUDIO_STOP_PLAY = 1004 +ROBOT_API_ID_AUDIO_GET_VOLUME = 1005 +ROBOT_API_ID_AUDIO_SET_VOLUME = 1006 +ROBOT_API_ID_AUDIO_SET_RGB_LED = 1010 + +""" +" error code +""" \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_client.py new file mode 100644 index 0000000..d8d0bc3 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_client.py @@ -0,0 +1,62 @@ +import json + +from ...rpc.client import Client +from .g1_audio_api import * + +""" +" class SportClient +""" +class AudioClient(Client): + def __init__(self): + super().__init__(AUDIO_SERVICE_NAME, False) + self.tts_index = 0 + + def Init(self): + # set api version + self._SetApiVerson(AUDIO_API_VERSION) + + # regist api + self._RegistApi(ROBOT_API_ID_AUDIO_TTS, 0) + self._RegistApi(ROBOT_API_ID_AUDIO_ASR, 0) + self._RegistApi(ROBOT_API_ID_AUDIO_START_PLAY, 0) + self._RegistApi(ROBOT_API_ID_AUDIO_STOP_PLAY, 0) + self._RegistApi(ROBOT_API_ID_AUDIO_GET_VOLUME, 0) + self._RegistApi(ROBOT_API_ID_AUDIO_SET_VOLUME, 0) + self._RegistApi(ROBOT_API_ID_AUDIO_SET_RGB_LED, 0) + + ## API Call ## + def TtsMaker(self, text: str, speaker_id: int): + self.tts_index += self.tts_index + p = {} + p["index"] = self.tts_index + p["text"] = text + p["speaker_id"] = speaker_id + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_AUDIO_TTS, parameter) + return code + + def GetVolume(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_AUDIO_GET_VOLUME, parameter) + if code == 0: + return code, json.loads(data) + else: + return code, None + + def SetVolume(self, volume: int): + p = {} + p["volume"] = volume + # p["name"] = 'volume' + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_AUDIO_SET_VOLUME, parameter) + return code + + def LedControl(self, R: int, G: int, B: int): + p = {} + p["R"] = R + p["G"] = G + p["B"] = B + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_AUDIO_SET_RGB_LED, parameter) + return code diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_api.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_api.py new file mode 100644 index 0000000..937de19 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_api.py @@ -0,0 +1,32 @@ +""" +" service name +""" +LOCO_SERVICE_NAME = "loco" + + +""" +" service api version +""" +LOCO_API_VERSION = "1.0.0.0" + + +""" +" api id +""" +ROBOT_API_ID_LOCO_GET_FSM_ID = 7001 +ROBOT_API_ID_LOCO_GET_FSM_MODE = 7002 +ROBOT_API_ID_LOCO_GET_BALANCE_MODE = 7003 +ROBOT_API_ID_LOCO_GET_SWING_HEIGHT = 7004 +ROBOT_API_ID_LOCO_GET_STAND_HEIGHT = 7005 +ROBOT_API_ID_LOCO_GET_PHASE = 7006 # deprecated + +ROBOT_API_ID_LOCO_SET_FSM_ID = 7101 +ROBOT_API_ID_LOCO_SET_BALANCE_MODE = 7102 +ROBOT_API_ID_LOCO_SET_SWING_HEIGHT = 7103 +ROBOT_API_ID_LOCO_SET_STAND_HEIGHT = 7104 +ROBOT_API_ID_LOCO_SET_VELOCITY = 7105 +ROBOT_API_ID_LOCO_SET_ARM_TASK = 7106 + +""" +" error code +""" \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_client.py new file mode 100644 index 0000000..9d3a632 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_client.py @@ -0,0 +1,127 @@ +import json + +from ...rpc.client import Client +from .g1_loco_api import * + +""" +" class SportClient +""" +class LocoClient(Client): + def __init__(self): + super().__init__(LOCO_SERVICE_NAME, False) + self.first_shake_hand_stage_ = -1 + + def Init(self): + # set api version + self._SetApiVerson(LOCO_API_VERSION) + + # regist api + self._RegistApi(ROBOT_API_ID_LOCO_GET_FSM_ID, 0) + self._RegistApi(ROBOT_API_ID_LOCO_GET_FSM_MODE, 0) + self._RegistApi(ROBOT_API_ID_LOCO_GET_BALANCE_MODE, 0) + self._RegistApi(ROBOT_API_ID_LOCO_GET_SWING_HEIGHT, 0) + self._RegistApi(ROBOT_API_ID_LOCO_GET_STAND_HEIGHT, 0) + self._RegistApi(ROBOT_API_ID_LOCO_GET_PHASE, 0) # deprecated + + self._RegistApi(ROBOT_API_ID_LOCO_SET_FSM_ID, 0) + self._RegistApi(ROBOT_API_ID_LOCO_SET_BALANCE_MODE, 0) + self._RegistApi(ROBOT_API_ID_LOCO_SET_SWING_HEIGHT, 0) + self._RegistApi(ROBOT_API_ID_LOCO_SET_STAND_HEIGHT, 0) + self._RegistApi(ROBOT_API_ID_LOCO_SET_VELOCITY, 0) + self._RegistApi(ROBOT_API_ID_LOCO_SET_ARM_TASK, 0) + + # 7101 + def SetFsmId(self, fsm_id: int): + p = {} + p["data"] = fsm_id + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_SET_FSM_ID, parameter) + return code + + # 7102 + def SetBalanceMode(self, balance_mode: int): + p = {} + p["data"] = balance_mode + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_SET_BALANCE_MODE, parameter) + return code + + # 7104 + def SetStandHeight(self, stand_height: float): + p = {} + p["data"] = stand_height + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_SET_STAND_HEIGHT, parameter) + return code + + # 7105 + def SetVelocity(self, vx: float, vy: float, omega: float, duration: float = 1.0): + p = {} + velocity = [vx,vy,omega] + p["velocity"] = velocity + p["duration"] = duration + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_SET_VELOCITY, parameter) + return code + + # 7106 + def SetTaskId(self, task_id: float): + p = {} + p["data"] = task_id + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_SET_ARM_TASK, parameter) + return code + + def Damp(self): + self.SetFsmId(1) + + def Start(self): + self.SetFsmId(200) + + def Squat2StandUp(self): + self.SetFsmId(706) + + def Lie2StandUp(self): + self.SetFsmId(702) + + def Sit(self): + self.SetFsmId(3) + + def StandUp2Squat(self): + self.SetFsmId(706) + + def ZeroTorque(self): + self.SetFsmId(0) + + def StopMove(self): + self.SetVelocity(0., 0., 0.) + + def HighStand(self): + UINT32_MAX = (1 << 32) - 1 + self.SetStandHeight(UINT32_MAX) + + def LowStand(self): + UINT32_MIN = 0 + self.SetStandHeight(UINT32_MIN) + + def Move(self, vx: float, vy: float, vyaw: float, continous_move: bool = False): + duration = 864000.0 if continous_move else 1 + self.SetVelocity(vx, vy, vyaw, duration) + + def BalanceStand(self, balance_mode: int): + self.SetBalanceMode(balance_mode) + + def WaveHand(self, turn_flag: bool = False): + self.SetTaskId(1 if turn_flag else 0) + + def ShakeHand(self, stage: int = -1): + if stage == 0: + self.first_shake_hand_stage_ = False + self.SetTaskId(2) + elif stage == 1: + self.first_shake_hand_stage_ = True + self.SetTaskId(3) + else: + self.first_shake_hand_stage_ = not self.first_shake_hand_stage_ + return self.SetTaskId(3 if self.first_shake_hand_stage_ else 2) + \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_api.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_api.py new file mode 100644 index 0000000..ad9dc06 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_api.py @@ -0,0 +1,19 @@ +""" +" service name +""" +OBSTACLES_AVOID_SERVICE_NAME = "obstacles_avoid" + + +""" +" service api version +""" +OBSTACLES_AVOID_API_VERSION = "1.0.0.2" + + +""" +" api id +""" +OBSTACLES_AVOID_API_ID_SWITCH_SET = 1001 +OBSTACLES_AVOID_API_ID_SWITCH_GET = 1002 +OBSTACLES_AVOID_API_ID_MOVE = 1003 +OBSTACLES_AVOID_API_ID_USE_REMOTE_COMMAND_FROM_API = 1004 \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_client.py new file mode 100644 index 0000000..4d7b62b --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_client.py @@ -0,0 +1,60 @@ +import json + +from ...rpc.client import Client +from .obstacles_avoid_api import * + + +""" +" class ObstaclesAvoidClient +""" +class ObstaclesAvoidClient(Client): + def __init__(self): + super().__init__(OBSTACLES_AVOID_SERVICE_NAME, False) + + def Init(self): + # set api version + self._SetApiVerson(OBSTACLES_AVOID_API_VERSION) + # regist api + self._RegistApi(OBSTACLES_AVOID_API_ID_SWITCH_SET, 0) + self._RegistApi(OBSTACLES_AVOID_API_ID_SWITCH_GET, 0) + self._RegistApi(OBSTACLES_AVOID_API_ID_MOVE, 0) + self._RegistApi(OBSTACLES_AVOID_API_ID_USE_REMOTE_COMMAND_FROM_API, 0) + + # 1001 + def SwitchSet(self, on: bool): + p = {} + p["enable"] = on + parameter = json.dumps(p) + + code, data = self._Call(OBSTACLES_AVOID_API_ID_SWITCH_SET, parameter) + return code + + # 1002 + def SwitchGet(self): + p = {} + parameter = json.dumps(p) + + code, data = self._Call(OBSTACLES_AVOID_API_ID_SWITCH_GET, parameter) + if code == 0: + d = json.loads(data) + return code, d["enable"] + else: + return code, None + + # 1003 + def Move(self, vx: float, vy: float, vyaw: float): + p = {} + p["x"] = vx + p["y"] = vy + p["yaw"] = vyaw + p["mode"] = 0 + parameter = json.dumps(p) + code = self._CallNoReply(OBSTACLES_AVOID_API_ID_MOVE, parameter) + return code + + def UseRemoteCommandFromApi(self, isRemoteCommandsFromApi: bool): + p = {} + p["is_remote_commands_from_api"] = isRemoteCommandsFromApi + parameter = json.dumps(p) + code, data = self._Call(OBSTACLES_AVOID_API_ID_USE_REMOTE_COMMAND_FROM_API, parameter) + return code \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_api.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_api.py new file mode 100644 index 0000000..fde54df --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_api.py @@ -0,0 +1,25 @@ +""" +" service name +""" +ROBOT_STATE_SERVICE_NAME = "robot_state" + + +""" +" service api version +""" +ROBOT_STATE_API_VERSION = "1.0.0.1" + + +""" +" api id +""" +ROBOT_STATE_API_ID_SERVICE_SWITCH = 1001 +ROBOT_STATE_API_ID_REPORT_FREQ = 1002 +ROBOT_STATE_API_ID_SERVICE_LIST = 1003 + + +""" +" error code +""" +ROBOT_STATE_ERR_SERVICE_SWITCH = 5201 +ROBOT_STATE_ERR_SERVICE_PROTECTED = 5202 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_client.py new file mode 100644 index 0000000..097a891 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_client.py @@ -0,0 +1,84 @@ +import json + +from ...rpc.client import Client +from ...rpc.internal import * +from .robot_state_api import * + + +""" +" class ServiceState +""" +class ServiceState: + def __init__(self, name: str = None, status: int = None, protect: bool = None): + self.name = name + self.status = status + self.protect = protect + +""" +" class RobotStateClient +""" +class RobotStateClient(Client): + def __init__(self): + super().__init__(ROBOT_STATE_SERVICE_NAME, False) + + def Init(self): + # set api version + self._SetApiVerson(ROBOT_STATE_API_VERSION) + # regist api + self._RegistApi(ROBOT_STATE_API_ID_SERVICE_SWITCH, 0) + self._RegistApi(ROBOT_STATE_API_ID_REPORT_FREQ, 0) + self._RegistApi(ROBOT_STATE_API_ID_SERVICE_LIST, 0) + + def ServiceList(self): + p = {} + parameter = json.dumps(p) + + code, data = self._Call(ROBOT_STATE_API_ID_SERVICE_LIST, parameter) + + if code != 0: + return code, None + + lst = [] + + d = json.loads(data) + for t in d: + s = ServiceState() + s.name = t["name"] + s.status = t["status"] + s.protect = t["protect"] + lst.append(s) + + return code, lst + + + def ServiceSwitch(self, name: str, switch: bool): + p = {} + p["name"] = name + p["switch"] = int(switch) + parameter = json.dumps(p) + + code, data = self._Call(ROBOT_STATE_API_ID_SERVICE_SWITCH, parameter) + + if code != 0: + return code + + d = json.loads(data) + + status = d["status"] + + if status == 5: + return ROBOT_STATE_ERR_SERVICE_PROTECTED + + if status != 0 and status != 1: + return ROBOT_STATE_ERR_SERVICE_SWITCH + + return code + + def SetReportFreq(self, interval: int, duration: int): + p = {} + p["interval"] = interval + p["duration"] = duration + parameter = json.dumps(p) + + code, data = self._Call(ROBOT_STATE_API_ID_REPORT_FREQ, p) + return code diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_api.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_api.py new file mode 100644 index 0000000..bfca62a --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_api.py @@ -0,0 +1,74 @@ +""" +" service name +""" +SPORT_SERVICE_NAME = "sport" + + +""" +" service api version +""" +SPORT_API_VERSION = "1.0.0.1" + + +""" +" api id +""" +SPORT_API_ID_DAMP = 1001 +SPORT_API_ID_BALANCESTAND = 1002 +SPORT_API_ID_STOPMOVE = 1003 +SPORT_API_ID_STANDUP = 1004 +SPORT_API_ID_STANDDOWN = 1005 +SPORT_API_ID_RECOVERYSTAND = 1006 +SPORT_API_ID_EULER = 1007 +SPORT_API_ID_MOVE = 1008 +SPORT_API_ID_SIT = 1009 +SPORT_API_ID_RISESIT = 1010 +SPORT_API_ID_SWITCHGAIT = 1011 +SPORT_API_ID_TRIGGER = 1012 +SPORT_API_ID_BODYHEIGHT = 1013 +SPORT_API_ID_FOOTRAISEHEIGHT = 1014 +SPORT_API_ID_SPEEDLEVEL = 1015 +SPORT_API_ID_HELLO = 1016 +SPORT_API_ID_STRETCH = 1017 +SPORT_API_ID_TRAJECTORYFOLLOW = 1018 +SPORT_API_ID_CONTINUOUSGAIT = 1019 +SPORT_API_ID_CONTENT = 1020 +SPORT_API_ID_WALLOW = 1021 +SPORT_API_ID_DANCE1 = 1022 +SPORT_API_ID_DANCE2 = 1023 +SPORT_API_ID_GETBODYHEIGHT = 1024 +SPORT_API_ID_GETFOOTRAISEHEIGHT = 1025 +SPORT_API_ID_GETSPEEDLEVEL = 1026 +SPORT_API_ID_SWITCHJOYSTICK = 1027 +SPORT_API_ID_POSE = 1028 +SPORT_API_ID_SCRAPE = 1029 +SPORT_API_ID_FRONTFLIP = 1030 +SPORT_API_ID_FRONTJUMP = 1031 +SPORT_API_ID_FRONTPOUNCE = 1032 +SPORT_API_ID_WIGGLEHIPS = 1033 +SPORT_API_ID_GETSTATE = 1034 +SPORT_API_ID_ECONOMICGAIT = 1035 +SPORT_API_ID_HEART = 1036 +ROBOT_SPORT_API_ID_DANCE3 = 1037 +ROBOT_SPORT_API_ID_DANCE4 = 1038 +ROBOT_SPORT_API_ID_HOPSPINLEFT = 1039 +ROBOT_SPORT_API_ID_HOPSPINRIGHT = 1040 + +ROBOT_SPORT_API_ID_LEFTFLIP = 1042 +ROBOT_SPORT_API_ID_BACKFLIP = 1044 +ROBOT_SPORT_API_ID_FREEWALK = 1045 +ROBOT_SPORT_API_ID_FREEBOUND = 1046 +ROBOT_SPORT_API_ID_FREEJUMP = 1047 +ROBOT_SPORT_API_ID_FREEAVOID = 1048 +ROBOT_SPORT_API_ID_WALKSTAIR = 1049 +ROBOT_SPORT_API_ID_WALKUPRIGHT = 1050 +ROBOT_SPORT_API_ID_CROSSSTEP = 1051 + +""" +" error code +""" +# client side +SPORT_ERR_CLIENT_POINT_PATH = 4101 +# server side +SPORT_ERR_SERVER_OVERTIME = 4201 +SPORT_ERR_SERVER_NOT_INIT = 4202 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_client.py new file mode 100644 index 0000000..d058e51 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_client.py @@ -0,0 +1,446 @@ +import json + +from ...rpc.client import Client +from .sport_api import * + +""" +" SPORT_PATH_POINT_SIZE +""" +SPORT_PATH_POINT_SIZE = 30 + + +""" +" class PathPoint +""" +class PathPoint: + def __init__(self, timeFromStart: float, x: float, y: float, yaw: float, vx: float, vy: float, vyaw: float): + self.timeFromStart = timeFromStart + self.x = x + self.y = y + self.yaw = yaw + self.vx = vx + self.vy = vy + self.vyaw = vyaw + + +""" +" class SportClient +""" +class SportClient(Client): + def __init__(self, enableLease: bool = False): + super().__init__(SPORT_SERVICE_NAME, enableLease) + + + def Init(self): + # set api version + self._SetApiVerson(SPORT_API_VERSION) + + # regist api + self._RegistApi(SPORT_API_ID_DAMP, 0) + self._RegistApi(SPORT_API_ID_BALANCESTAND, 0) + self._RegistApi(SPORT_API_ID_STOPMOVE, 0) + self._RegistApi(SPORT_API_ID_STANDUP, 0) + self._RegistApi(SPORT_API_ID_STANDDOWN, 0) + self._RegistApi(SPORT_API_ID_RECOVERYSTAND, 0) + self._RegistApi(SPORT_API_ID_EULER, 0) + self._RegistApi(SPORT_API_ID_MOVE, 0) + self._RegistApi(SPORT_API_ID_SIT, 0) + self._RegistApi(SPORT_API_ID_RISESIT, 0) + self._RegistApi(SPORT_API_ID_SWITCHGAIT, 0) + self._RegistApi(SPORT_API_ID_TRIGGER, 0) + self._RegistApi(SPORT_API_ID_BODYHEIGHT, 0) + self._RegistApi(SPORT_API_ID_FOOTRAISEHEIGHT, 0) + self._RegistApi(SPORT_API_ID_SPEEDLEVEL, 0) + self._RegistApi(SPORT_API_ID_HELLO, 0) + self._RegistApi(SPORT_API_ID_STRETCH, 0) + self._RegistApi(SPORT_API_ID_TRAJECTORYFOLLOW, 0) + self._RegistApi(SPORT_API_ID_CONTINUOUSGAIT, 0) + # self._RegistApi(SPORT_API_ID_CONTENT, 0) + self._RegistApi(SPORT_API_ID_WALLOW, 0) + self._RegistApi(SPORT_API_ID_DANCE1, 0) + self._RegistApi(SPORT_API_ID_DANCE2, 0) + # self._RegistApi(SPORT_API_ID_GETBODYHEIGHT, 0) + # self._RegistApi(SPORT_API_ID_GETFOOTRAISEHEIGHT, 0) + # self._RegistApi(SPORT_API_ID_GETSPEEDLEVEL, 0) + self._RegistApi(SPORT_API_ID_SWITCHJOYSTICK, 0) + self._RegistApi(SPORT_API_ID_POSE, 0) + self._RegistApi(SPORT_API_ID_SCRAPE, 0) + self._RegistApi(SPORT_API_ID_FRONTFLIP, 0) + self._RegistApi(SPORT_API_ID_FRONTJUMP, 0) + self._RegistApi(SPORT_API_ID_FRONTPOUNCE, 0) + self._RegistApi(SPORT_API_ID_WIGGLEHIPS, 0) + self._RegistApi(SPORT_API_ID_GETSTATE, 0) + self._RegistApi(SPORT_API_ID_ECONOMICGAIT, 0) + self._RegistApi(SPORT_API_ID_HEART, 0) + + self._RegistApi(ROBOT_SPORT_API_ID_LEFTFLIP, 0) + self._RegistApi(ROBOT_SPORT_API_ID_BACKFLIP, 0) + self._RegistApi(ROBOT_SPORT_API_ID_FREEWALK, 0) + self._RegistApi(ROBOT_SPORT_API_ID_FREEBOUND, 0) + self._RegistApi(ROBOT_SPORT_API_ID_FREEJUMP, 0) + self._RegistApi(ROBOT_SPORT_API_ID_FREEAVOID, 0) + self._RegistApi(ROBOT_SPORT_API_ID_WALKSTAIR, 0) + self._RegistApi(ROBOT_SPORT_API_ID_WALKUPRIGHT, 0) + self._RegistApi(ROBOT_SPORT_API_ID_CROSSSTEP, 0) + + # 1001 + def Damp(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_DAMP, parameter) + return code + + # 1002 + def BalanceStand(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_BALANCESTAND, parameter) + return code + + # 1003 + def StopMove(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_STOPMOVE, parameter) + return code + + # 1004 + def StandUp(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_STANDUP, parameter) + return code + + # 1005 + def StandDown(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_STANDDOWN, parameter) + return code + + # 1006 + def RecoveryStand(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_RECOVERYSTAND, parameter) + return code + + # 1007 + def Euler(self, roll: float, pitch: float, yaw: float): + p = {} + p["x"] = roll + p["y"] = pitch + p["z"] = yaw + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_EULER, parameter) + return code + + # 1008 + def Move(self, vx: float, vy: float, vyaw: float): + p = {} + p["x"] = vx + p["y"] = vy + p["z"] = vyaw + parameter = json.dumps(p) + code = self._CallNoReply(SPORT_API_ID_MOVE, parameter) + return code + + # 1009 + def Sit(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_SIT, parameter) + return code + + #1010 + def RiseSit(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_RISESIT, parameter) + return code + + # 1011 + def SwitchGait(self, t: int): + p = {} + p["data"] = t + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_SWITCHGAIT, parameter) + return code + + # 1012 + def Trigger(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_TRIGGER, parameter) + return code + + # 1013 + def BodyHeight(self, height: float): + p = {} + p["data"] = height + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_BODYHEIGHT, parameter) + return code + + # 1014 + def FootRaiseHeight(self, height: float): + p = {} + p["data"] = height + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_FOOTRAISEHEIGHT, parameter) + return code + + # 1015 + def SpeedLevel(self, level: int): + p = {} + p["data"] = level + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_SPEEDLEVEL, parameter) + return code + + # 1016 + def Hello(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_HELLO, parameter) + return code + + # 1017 + def Stretch(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_STRETCH, parameter) + return code + + # 1018 + def TrajectoryFollow(self, path: list): + l = len(path) + if l != SPORT_PATH_POINT_SIZE: + return SPORT_ERR_CLIENT_POINT_PATH + + path_p = [] + for i in range(l): + point = path[i] + p = {} + p["t_from_start"] = point.timeFromStart + p["x"] = point.x + p["y"] = point.y + p["yaw"] = point.yaw + p["vx"] = point.vx + p["vy"] = point.vy + p["vyaw"] = point.vyaw + path_p.append(p) + + parameter = json.dumps(path_p) + code = self._CallNoReply(SPORT_API_ID_TRAJECTORYFOLLOW, parameter) + return code + + # 1019 + def ContinuousGait(self, flag: int): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_CONTINUOUSGAIT, parameter) + return code + + # # 1020 + # def Content(self): + # p = {} + # parameter = json.dumps(p) + # code, data = self._Call(SPORT_API_ID_CONTENT, parameter) + # return code + + # 1021 + def Wallow(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_WALLOW, parameter) + return code + + # 1022 + def Dance1(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_DANCE1, parameter) + return code + + # 1023 + def Dance2(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_DANCE2, parameter) + return code + + # 1025 + def GetFootRaiseHeight(self): + p = {} + parameter = json.dumps(p) + + code, data = self._Call(SPORT_API_ID_GETFOOTRAISEHEIGHT, parameter) + + if code == 0: + d = json.loads(data) + return code, d["data"] + else: + return code, None + + + # 1026 + def GetSpeedLevel(self): + p = {} + parameter = json.dumps(p) + + code, data = self._Call(SPORT_API_ID_GETSPEEDLEVEL, parameter) + + if code == 0: + d = json.loads(data) + return code, d["data"] + else: + return code, None + + # 1027 + def SwitchJoystick(self, on: bool): + p = {} + p["data"] = on + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_SWITCHJOYSTICK, parameter) + return code + + # 1028 + def Pose(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_POSE, parameter) + return code + + # 1029 + def Scrape(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_SCRAPE, parameter) + return code + + # 1030 + def FrontFlip(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_FRONTFLIP, parameter) + return code + + # 1031 + def FrontJump(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_FRONTJUMP, parameter) + return code + + # 1032 + def FrontPounce(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_FRONTPOUNCE, parameter) + return code + + # 1033 + def WiggleHips(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_WIGGLEHIPS, parameter) + return code + + # 1034 + def GetState(self, keys: list): + parameter = json.dumps(keys) + code, data = self._Call(SPORT_API_ID_GETSTATE, parameter) + if code == 0: + return code, json.loads(data) + else: + return code, None + + # 1035 + def EconomicGait(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_ECONOMICGAIT, parameter) + return code + + # 1036 + def Heart(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_HEART, parameter) + return code + + # 1042 + def LeftFlip(self): + p = {} + p["data"] = True + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_LEFTFLIP, parameter) + return code + + # 1044 + def BackFlip(self): + p = {} + p["data"] = True + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_BACKFLIP, parameter) + return code + + # 1045 + def FreeWalk(self, flag: bool): + p = {} + p["data"] = True + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_FREEWALK, parameter) + return code + + # 1046 + def FreeBound(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_FREEBOUND, parameter) + return code + + # 1047 + def FreeJump(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_FREEJUMP, parameter) + return code + + # 1048 + def FreeAvoid(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_FREEAVOID, parameter) + return code + + # 1049 + def WalkStair(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_WALKSTAIR, parameter) + return code + + # 1050 + def WalkUpright(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_WALKUPRIGHT, parameter) + return code + + # 1051 + def CrossStep(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_CROSSSTEP, parameter) + return code \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/video_api.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/video_api.py new file mode 100644 index 0000000..a4cb1b6 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/video_api.py @@ -0,0 +1,16 @@ +""" +" service name +""" +VIDEO_SERVICE_NAME = "videohub" + + +""" +" service api version +""" +VIDEO_API_VERSION = "1.0.0.1" + + +""" +" api id +""" +VIDEO_API_ID_GETIMAGESAMPLE = 1001 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/video_client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/video_client.py new file mode 100644 index 0000000..79e1fb5 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/video/video_client.py @@ -0,0 +1,23 @@ +import json + +from ...rpc.client import Client +from .video_api import * + + +""" +" class VideoClient +""" +class VideoClient(Client): + def __init__(self): + super().__init__(VIDEO_SERVICE_NAME, False) + + + def Init(self): + # set api version + self._SetApiVerson(VIDEO_API_VERSION) + # regist api + self._RegistApi(VIDEO_API_ID_GETIMAGESAMPLE, 0) + + # 1001 + def GetImageSample(self): + return self._CallBinary(VIDEO_API_ID_GETIMAGESAMPLE, []) diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/vui/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/vui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_api.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_api.py new file mode 100644 index 0000000..d5dcd34 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_api.py @@ -0,0 +1,21 @@ +""" +" service name +""" +VUI_SERVICE_NAME = "vui" + + +""" +" service api version +""" +VUI_API_VERSION = "1.0.0.1" + + +""" +" api id +""" +VUI_API_ID_SETSWITCH = 1001 +VUI_API_ID_GETSWITCH = 1002 +VUI_API_ID_SETVOLUME = 1003 +VUI_API_ID_GETVOLUME = 1004 +VUI_API_ID_SETBRIGHTNESS = 1005 +VUI_API_ID_GETBRIGHTNESS = 1006 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_client.py new file mode 100644 index 0000000..234f285 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_client.py @@ -0,0 +1,86 @@ +import json + +from ...rpc.client import Client +from .vui_api import * + + +""" +" class VideoClient +""" +class VuiClient(Client): + def __init__(self): + super().__init__(VUI_SERVICE_NAME, False) + + def Init(self): + # set api version + self._SetApiVerson(VUI_API_VERSION) + # regist api + self._RegistApi(VUI_API_ID_SETSWITCH, 0) + self._RegistApi(VUI_API_ID_GETSWITCH, 0) + self._RegistApi(VUI_API_ID_SETVOLUME, 0) + self._RegistApi(VUI_API_ID_GETVOLUME, 0) + self._RegistApi(VUI_API_ID_SETBRIGHTNESS, 0) + self._RegistApi(VUI_API_ID_GETBRIGHTNESS, 0) + + # 1001 + def SetSwitch(self, enable: int): + p = {} + p["enable"] = enable + parameter = json.dumps(p) + + code, data = self._Call(VUI_API_ID_SETSWITCH, parameter) + return code + + # 1002 + def GetSwitch(self): + p = {} + parameter = json.dumps(p) + + code, data = self._Call(VUI_API_ID_GETSWITCH, parameter) + if code == 0: + d = json.loads(data) + return code, d["enable"] + else: + return code, None + + # 1003 + def SetVolume(self, level: int): + p = {} + p["volume"] = level + parameter = json.dumps(p) + + code, data = self._Call(VUI_API_ID_SETVOLUME, parameter) + return code + + # 1006 + def GetVolume(self): + p = {} + parameter = json.dumps(p) + + code, data = self._Call(VUI_API_ID_GETVOLUME, parameter) + if code == 0: + d = json.loads(data) + return code, d["volume"] + else: + return code, None + + # 1005 + def SetBrightness(self, level: int): + p = {} + p["brightness"] = level + parameter = json.dumps(p) + + code, data = self._Call(VUI_API_ID_SETBRIGHTNESS, parameter) + return code + + # 1006 + def GetBrightness(self): + p = {} + parameter = json.dumps(p) + + code, data = self._Call(VUI_API_ID_GETBRIGHTNESS, parameter) + if code == 0: + d = json.loads(data) + return code, d["brightness"] + else: + return code, None \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/h1/loco/h1_loco_api.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/h1/loco/h1_loco_api.py new file mode 100644 index 0000000..bd8ddbe --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/h1/loco/h1_loco_api.py @@ -0,0 +1,31 @@ +""" +" service name +""" +LOCO_SERVICE_NAME = "loco" + + +""" +" service api version +""" +LOCO_API_VERSION = "2.0.0.0" + + +""" +" api id +""" +ROBOT_API_ID_LOCO_GET_FSM_ID = 8001 +ROBOT_API_ID_LOCO_GET_FSM_MODE = 8002 +ROBOT_API_ID_LOCO_GET_BALANCE_MODE = 8003 +ROBOT_API_ID_LOCO_GET_SWING_HEIGHT = 8004 +ROBOT_API_ID_LOCO_GET_STAND_HEIGHT = 8005 +ROBOT_API_ID_LOCO_GET_PHASE = 8006 # deprecated + +ROBOT_API_ID_LOCO_SET_FSM_ID = 8101 +ROBOT_API_ID_LOCO_SET_BALANCE_MODE = 8102 +ROBOT_API_ID_LOCO_SET_SWING_HEIGHT = 8103 +ROBOT_API_ID_LOCO_SET_STAND_HEIGHT = 8104 +ROBOT_API_ID_LOCO_SET_VELOCITY = 8105 + +""" +" error code +""" \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/h1/loco/h1_loco_client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/h1/loco/h1_loco_client.py new file mode 100644 index 0000000..3bc01e3 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/h1/loco/h1_loco_client.py @@ -0,0 +1,83 @@ +import json + +from ...rpc.client import Client +from .h1_loco_api import * + +""" +" class SportClient +""" +class LocoClient(Client): + def __init__(self): + super().__init__(LOCO_SERVICE_NAME, False) + + + def Init(self): + # set api version + self._SetApiVerson(LOCO_API_VERSION) + + # regist api + self._RegistApi(ROBOT_API_ID_LOCO_GET_FSM_ID, 0) + self._RegistApi(ROBOT_API_ID_LOCO_GET_FSM_MODE, 0) + self._RegistApi(ROBOT_API_ID_LOCO_GET_BALANCE_MODE, 0) + self._RegistApi(ROBOT_API_ID_LOCO_GET_SWING_HEIGHT, 0) + self._RegistApi(ROBOT_API_ID_LOCO_GET_STAND_HEIGHT, 0) + self._RegistApi(ROBOT_API_ID_LOCO_GET_PHASE, 0) # deprecated + + self._RegistApi(ROBOT_API_ID_LOCO_SET_FSM_ID, 0) + self._RegistApi(ROBOT_API_ID_LOCO_SET_BALANCE_MODE, 0) + self._RegistApi(ROBOT_API_ID_LOCO_SET_SWING_HEIGHT, 0) + self._RegistApi(ROBOT_API_ID_LOCO_SET_STAND_HEIGHT, 0) + self._RegistApi(ROBOT_API_ID_LOCO_SET_VELOCITY, 0) + + # 8101 + def SetFsmId(self, fsm_id: int): + p = {} + p["data"] = fsm_id + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_SET_FSM_ID, parameter) + return code + + # 8104 + def SetStandHeight(self, stand_height: float): + p = {} + p["data"] = stand_height + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_SET_STAND_HEIGHT, parameter) + return code + + # 8105 + def SetVelocity(self, vx: float, vy: float, omega: float, duration: float = 1.0): + p = {} + velocity = [vx,vy,omega] + p["velocity"] = velocity + p["duration"] = duration + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_SET_VELOCITY, parameter) + return code + + def Damp(self): + self.SetFsmId(1) + + def Start(self): + self.SetFsmId(204) + + def StandUp(self): + self.SetFsmId(2) + + def ZeroTorque(self): + self.SetFsmId(0) + + def StopMove(self): + self.SetVelocity(0., 0., 0.) + + def HighStand(self): + UINT32_MAX = (1 << 32) - 1 + self.SetStandHeight(UINT32_MAX) + + def LowStand(self): + UINT32_MIN = 0 + self.SetStandHeight(UINT32_MIN) + + def Move(self, vx: float, vy: float, vyaw: float, continous_move: bool = False): + duration = 864000.0 if continous_move else 1 + self.SetVelocity(vx, vy, vyaw, duration) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/__init__.py new file mode 100644 index 0000000..38d706e --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/__init__.py @@ -0,0 +1,12 @@ +from .default import * +from . import builtin_interfaces, geometry_msgs, sensor_msgs, std_msgs, unitree_go, unitree_api + +__all__ = [ + "builtin_interfaces", + "geometry_msgs", + "sensor_msgs", + "std_msgs", + "unitree_go", + "unitree_hg", + "unitree_api", +] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/__init__.py new file mode 100644 index 0000000..c0397fd --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: builtin_interfaces + +""" + +from . import msg +__all__ = ["msg", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/__init__.py new file mode 100644 index 0000000..b791398 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: builtin_interfaces.msg + +""" + +from . import dds_ +__all__ = ["dds_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/dds_/_Time_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/dds_/_Time_.py new file mode 100644 index 0000000..970c671 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/dds_/_Time_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: builtin_interfaces.msg.dds_ + IDL file: Time_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import builtin_interfaces + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Time_(idl.IdlStruct, typename="builtin_interfaces.msg.dds_.Time_"): + sec: types.int32 + nanosec: types.uint32 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/dds_/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/dds_/__init__.py new file mode 100644 index 0000000..c3f9730 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/dds_/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: builtin_interfaces.msg.dds_ + +""" + +from ._Time_ import Time_ +__all__ = ["Time_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/default.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/default.py new file mode 100644 index 0000000..778006a --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/default.py @@ -0,0 +1,272 @@ +from .builtin_interfaces.msg.dds_ import * +from .std_msgs.msg.dds_ import * +from .geometry_msgs.msg.dds_ import * +from .nav_msgs.msg.dds_ import * +from .sensor_msgs.msg.dds_ import * +from .unitree_go.msg.dds_ import * +from .unitree_api.msg.dds_ import * + +# IDL for unitree_hg +from .unitree_hg.msg.dds_ import LowCmd_ as HGLowCmd_ +from .unitree_hg.msg.dds_ import LowState_ as HGLowState_ +from .unitree_hg.msg.dds_ import MotorCmd_ as HGMotorCmd_ +from .unitree_hg.msg.dds_ import MotorState_ as HGMotorState_ +from .unitree_hg.msg.dds_ import BmsState_ as HGBmsState_ +from .unitree_hg.msg.dds_ import IMUState_ as HGIMUState_ +from .unitree_hg.msg.dds_ import MainBoardState_ as HGMainBoardState_ +from .unitree_hg.msg.dds_ import PressSensorState_ as HGPressSensorState_ +from .unitree_hg.msg.dds_ import HandCmd_ as HGHandCmd_ +from .unitree_hg.msg.dds_ import HandState_ as HGHandState_ +from .unitree_hg.msg.dds_ import OdoState_ as HGOdoState_ + +""" +" builtin_interfaces_msgs.msg.dds_ dafault +""" +def builtin_interfaces_msgs_msg_dds__Time_(): + return Time_(0, 0) + + +""" +" std_msgs.msg.dds_ dafault +""" +def std_msgs_msg_dds__Header_(): + return Header_(builtin_interfaces_msgs_msg_dds__Time_(), "") + +def std_msgs_msg_dds__String_(): + return String_("") + + +""" +" geometry_msgs.msg.dds_ dafault +""" +def geometry_msgs_msg_dds__Point_(): + return Point_(0.0, 0.0, 0.0) + +def geometry_msgs_msg_dds__Point32_(): + return Point32_(0.0, 0.0, 0.0) + +def geometry_msgs_msg_dds__PointStamped_(): + return PointStamped_(std_msgs_msg_dds__Header_(), geometry_msgs_msg_dds__Point_()) + +def geometry_msgs_msg_dds__Quaternion_(): + return Quaternion_(0.0, 0.0, 0.0, 0.0) + +def geometry_msgs_msg_dds__Vector3_(): + return Vector3_(0.0, 0.0, 0.0) + +def geometry_msgs_msg_dds__Pose_(): + return Pose_(geometry_msgs_msg_dds__Point_(), geometry_msgs_msg_dds__Quaternion_()) + +def geometry_msgs_msg_dds__Pose2D_(): + return Pose2D_(0.0, 0.0, 0.0) + +def geometry_msgs_msg_dds__PoseStamped_(): + return PoseStamped_(std_msgs_msg_dds__Header_(), geometry_msgs_msg_dds__Pose_()) + +def geometry_msgs_msg_dds__PoseWithCovariance_(): + return PoseWithCovariance_(geometry_msgs_msg_dds__Pose_(), [ + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + ]) + +def geometry_msgs_msg_dds__PoseWithCovarianceStamped_(): + return PoseWithCovarianceStamped_(std_msgs_msg_dds__Header_(), geometry_msgs_msg_dds__PoseWithCovariance_()) + +def geometry_msgs_msg_dds__QuaternionStamped_(): + return QuaternionStamped_(std_msgs_msg_dds__Header_(), geometry_msgs_msg_dds__Quaternion_()) + +def geometry_msgs_msg_dds__Twist_(): + return Twist_(geometry_msgs_msg_dds__Vector3_(), geometry_msgs_msg_dds__Vector3_()) + +def geometry_msgs_msg_dds__TwistStamped_(): + return TwistStamped_(std_msgs_msg_dds__Header_(), geometry_msgs_msg_dds__Twist_()) + +def geometry_msgs_msg_dds__TwistWithCovariance_(): + return TwistWithCovariance_(geometry_msgs_msg_dds__Twist_(), [ + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + ]) + +def geometry_msgs_msg_dds__TwistWithCovarianceStamped_(): + return TwistWithCovarianceStamped_(std_msgs_msg_dds__Header_(), geometry_msgs_msg_dds__TwistWithCovariance_()) + + +""" +" nav_msgs.msg.dds_ dafault +""" +def nav_msgs_msg_dds__MapMetaData_(): + return MapMetaData_(builtin_interfaces_msgs_msg_dds__Time_(), 0, 0, geometry_msgs_msg_dds__Pose_()) + +def nav_msgs_msg_dds__OccupancyGrid_(): + return OccupancyGrid_(std_msgs_msg_dds__Header_(), nav_msgs_msg_dds__MapMetaData_(), []) + +def nav_msgs_msg_dds__Odometry_(): + return Odometry_(std_msgs_msg_dds__Header_(), "", geometry_msgs_msg_dds__PoseWithCovariance_(), + geometry_msgs_msg_dds__TwistWithCovariance_()) + + +""" +" sensor_msgs.msg.dds_ dafault +""" +def sensor_msgs_msg_dds__PointField_Constants_PointField_(): + return PointField_("", 0, 0, 0) + +def sensor_msgs_msg_dds__PointField_Constants_PointCloud2_(): + return PointCloud2_(std_msgs_msg_dds__Header_(), 0, 0, [], False, 0, 0, [], False) + + +""" +" unitree_go.msg.dds_ dafault +""" +def unitree_go_msg_dds__AudioData_(): + return AudioData_(0, []) + +def unitree_go_msg_dds__BmsCmd_(): + return BmsCmd_(0, [0, 0, 0]) + +def unitree_go_msg_dds__BmsState_(): + return BmsState_(0, 0, 0, 0, 0, 0, [0, 0], [0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + +def unitree_go_msg_dds__Error_(): + return Error_(0, 0) + +def unitree_go_msg_dds__Go2FrontVideoData_(): + return Go2FrontVideoData_(0, [], [], []) + +def unitree_go_msg_dds__HeightMap_(): + return HeightMap_(0.0, "", 0.0, 0, 0, [0.0, 0.0], []) + +def unitree_go_msg_dds__IMUState_(): + return IMUState_([0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], 0) + +def unitree_go_msg_dds__InterfaceConfig_(): + return InterfaceConfig_(0, 0, [0, 0]) + +def unitree_go_msg_dds__LidarState_(): + return LidarState_(0.0, "", "", "", 0.0, 0.0, 0, 0.0, 0.0, 0, 0, 0.0, 0.0, [0.0, 0.0, 0.0], 0.0, 0, 0) + +def unitree_go_msg_dds__MotorCmd_(): + return MotorCmd_(0, 0.0, 0.0, 0.0, 0.0, 0.0, [0, 0, 0]) + +def unitree_go_msg_dds__MotorState_(): + return MotorState_(0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0, [0, 0]) + +def unitree_go_msg_dds__LowCmd_(): + return LowCmd_([0, 0], 0, 0, [0, 0], [0, 0], 0, [unitree_go_msg_dds__MotorCmd_() for i in range(20)], + unitree_go_msg_dds__BmsCmd_(), + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0], 0, 0, 0) + +def unitree_go_msg_dds__LowState_(): + return LowState_([0, 0], 0, 0, [0, 0], [0, 0], 0, unitree_go_msg_dds__IMUState_(), + [unitree_go_msg_dds__MotorState_() for i in range(20)], + unitree_go_msg_dds__BmsState_(), [0, 0, 0, 0], [0, 0, 0, 0], 0, + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + 0, 0, 0, 0, 0.0, 0.0, [0, 0, 0, 0], 0, 0) + +def unitree_go_msg_dds__Req_(): + return Req_("", "") + +def unitree_go_msg_dds__Res_(): + return Res_("", [], "") + +def unitree_go_msg_dds__TimeSpec_(): + return TimeSpec_(0, 0) + +def unitree_go_msg_dds__PathPoint_(): + return PathPoint_(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) + +def unitree_go_msg_dds__SportModeState_(): + return SportModeState_(unitree_go_msg_dds__TimeSpec_(), 0, unitree_go_msg_dds__IMUState_(), + 0, 0, 0, 0.0, [0.0, 0.0, 0.0], 0.0, + [0.0, 0.0, 0.0], 0.0, [0.0, 0.0, 0.0, 0.0], [0, 0, 0, 0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],[unitree_go_msg_dds__PathPoint_() for i in range(10)]) + +def unitree_go_msg_dds__UwbState_(): + return UwbState_([0, 0], 0, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, [0.0, 0.0], 0, 0, 0) + +def unitree_go_msg_dds__UwbSwitch_(): + return UwbSwitch_(0) + +def unitree_go_msg_dds__WirelessController_(): + return WirelessController_(0.0, 0.0, 0.0, 0.0, 0) + + +""" +" unitree_hg.msg.dds_ dafault +""" +def unitree_hg_msg_dds__BmsCmd_(): + return HGBmsCmd_(0, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + +def unitree_hg_msg_dds__BmsState_(): + return HGBmsState_(0, 0, 0, + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0], 0, 0, 0, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, 0, [0, 0, 0, 0, 0], [0, 0, 0]) + +def unitree_hg_msg_dds__IMUState_(): + return HGIMUState_([0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], 0) + +def unitree_hg_msg_dds__MotorCmd_(): + return HGMotorCmd_(0, 0.0, 0.0, 0.0, 0.0, 0.0, 0) + +def unitree_hg_msg_dds__MotorState_(): + return HGMotorState_(0, 0.0, 0.0, 0.0, 0.0, [0, 0], 0.0, [0, 0], 0, [0, 0, 0, 0]) + +def unitree_hg_msg_dds__MainBoardState_(): + return HGMainBoardState_([0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0, 0, 0, 0, 0, 0]) + +def unitree_hg_msg_dds__LowCmd_(): + return HGLowCmd_(0, 0, [unitree_hg_msg_dds__MotorCmd_() for i in range(35)], [0, 0, 0, 0], 0) + +def unitree_hg_msg_dds__LowState_(): + return HGLowState_([0, 0], 0, 0, 0, unitree_hg_msg_dds__IMUState_(), + [unitree_hg_msg_dds__MotorState_() for i in range(35)], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0], 0) + +def unitree_hg_msg_dds__PressSensorState_(): + return HGPressSensorState_([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], 0, 0) + +def unitree_hg_msg_dds__HandCmd_(): + return HGHandCmd_([unitree_hg_msg_dds__MotorCmd_() for i in range(7)], [0, 0, 0, 0]) + +def unitree_hg_msg_dds__HandState_(): + return HGHandState_([unitree_hg_msg_dds__MotorState_() for i in range(7)], + [unitree_hg_msg_dds__PressSensorState_() for i in range(7)], + unitree_hg_msg_dds__IMUState_(), + 0.0, 0.0, 0.0, 0.0, [0, 0], [0, 0]) + +def unitree_hg_msg_dds__OdoState_(): + return HGOdoState_([0, 0], 0, [0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], 0) + + +""" +" unitree_api.msg.dds_ dafault +""" +def unitree_api_msg_dds__RequestIdentity_(): + return RequestIdentity_(0, 0) + +def unitree_api_msg_dds__RequestLease_(): + return RequestLease_(0, unitree_hg_msg_dds__IMUState_(), [], ) + +def unitree_api_msg_dds__RequestPolicy_(): + return RequestPolicy_(0, False) + +def unitree_api_msg_dds__RequestHeader_(): + return RequestHeader_(unitree_api_msg_dds__RequestIdentity_(), unitree_api_msg_dds__RequestLease_(), + unitree_api_msg_dds__RequestPolicy_()) + +def unitree_api_msg_dds__Request_(): + return Request_(unitree_api_msg_dds__RequestHeader_(), "", []) + +def unitree_api_msg_dds__ResponseStatus_(): + return ResponseStatus_(0) + +def unitree_api_msg_dds__ResponseHeader_(): + return ResponseHeader_(unitree_api_msg_dds__RequestIdentity_(), unitree_api_msg_dds__ResponseStatus_()) + +def unitree_api_msg_dds__Response_(): + return Response_(unitree_api_msg_dds__ResponseHeader_(), "", [], 0, 0, [0, 0]) + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/__init__.py new file mode 100644 index 0000000..ab19108 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs + +""" + +from . import msg +__all__ = ["msg", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/__init__.py new file mode 100644 index 0000000..7d10dec --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg + +""" + +from . import dds_ +__all__ = ["dds_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Point32_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Point32_.py new file mode 100644 index 0000000..34eefd4 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Point32_.py @@ -0,0 +1,29 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + IDL file: Point32_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import geometry_msgs + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Point32_(idl.IdlStruct, typename="geometry_msgs.msg.dds_.Point32_"): + x: types.float32 + y: types.float32 + z: types.float32 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PointStamped_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PointStamped_.py new file mode 100644 index 0000000..19de6ae --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PointStamped_.py @@ -0,0 +1,31 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + IDL file: PointStamped_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import geometry_msgs + +# if TYPE_CHECKING: +# import std_msgs.msg.dds_ + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class PointStamped_(idl.IdlStruct, typename="geometry_msgs.msg.dds_.PointStamped_"): + header: 'unitree_sdk2py.idl.std_msgs.msg.dds_.Header_' + point: 'unitree_sdk2py.idl.geometry_msgs.msg.dds_.Point_' + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Point_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Point_.py new file mode 100644 index 0000000..efbc562 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Point_.py @@ -0,0 +1,29 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + IDL file: Point_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import geometry_msgs + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Point_(idl.IdlStruct, typename="geometry_msgs.msg.dds_.Point_"): + x: types.float64 + y: types.float64 + z: types.float64 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Pose2D_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Pose2D_.py new file mode 100644 index 0000000..c223370 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Pose2D_.py @@ -0,0 +1,29 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + IDL file: Pose2D_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import geometry_msgs + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Pose2D_(idl.IdlStruct, typename="geometry_msgs.msg.dds_.Pose2D_"): + x: types.float64 + y: types.float64 + theta: types.float64 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseStamped_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseStamped_.py new file mode 100644 index 0000000..7864bb0 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseStamped_.py @@ -0,0 +1,32 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + IDL file: PoseStamped_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import geometry_msgs + +# if TYPE_CHECKING: +# import std_msgs.msg.dds_ + + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class PoseStamped_(idl.IdlStruct, typename="geometry_msgs.msg.dds_.PoseStamped_"): + header: 'unitree_sdk2py.idl.std_msgs.msg.dds_.Header_' + pose: 'unitree_sdk2py.idl.geometry_msgs.msg.dds_.Pose_' + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseWithCovarianceStamped_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseWithCovarianceStamped_.py new file mode 100644 index 0000000..843d4c8 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseWithCovarianceStamped_.py @@ -0,0 +1,32 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + IDL file: PoseWithCovarianceStamped_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import geometry_msgs + +# if TYPE_CHECKING: +# import std_msgs.msg.dds_ + + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class PoseWithCovarianceStamped_(idl.IdlStruct, typename="geometry_msgs.msg.dds_.PoseWithCovarianceStamped_"): + header: 'unitree_sdk2py.idl.std_msgs.msg.dds_.Header_' + pose: 'unitree_sdk2py.idl.geometry_msgs.msg.dds_.PoseWithCovariance_' + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseWithCovariance_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseWithCovariance_.py new file mode 100644 index 0000000..bd75301 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseWithCovariance_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + IDL file: PoseWithCovariance_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import geometry_msgs + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class PoseWithCovariance_(idl.IdlStruct, typename="geometry_msgs.msg.dds_.PoseWithCovariance_"): + pose: 'unitree_sdk2py.idl.geometry_msgs.msg.dds_.Pose_' + covariance: types.array[types.float64, 36] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Pose_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Pose_.py new file mode 100644 index 0000000..2ea78d1 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Pose_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + IDL file: Pose_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import geometry_msgs + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Pose_(idl.IdlStruct, typename="geometry_msgs.msg.dds_.Pose_"): + position: 'unitree_sdk2py.idl.geometry_msgs.msg.dds_.Point_' + orientation: 'unitree_sdk2py.idl.geometry_msgs.msg.dds_.Quaternion_' + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_QuaternionStamped_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_QuaternionStamped_.py new file mode 100644 index 0000000..ae2b360 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_QuaternionStamped_.py @@ -0,0 +1,32 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + IDL file: QuaternionStamped_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import geometry_msgs + +# if TYPE_CHECKING: +# import std_msgs.msg.dds_ + + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class QuaternionStamped_(idl.IdlStruct, typename="geometry_msgs.msg.dds_.QuaternionStamped_"): + header: 'unitree_sdk2py.idl.std_msgs.msg.dds_.Header_' + quaternion: 'unitree_sdk2py.idl.geometry_msgs.msg.dds_.Quaternion_' + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Quaternion_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Quaternion_.py new file mode 100644 index 0000000..0a401fb --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Quaternion_.py @@ -0,0 +1,30 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + IDL file: Quaternion_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import geometry_msgs + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Quaternion_(idl.IdlStruct, typename="geometry_msgs.msg.dds_.Quaternion_"): + x: types.float64 + y: types.float64 + z: types.float64 + w: types.float64 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistStamped_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistStamped_.py new file mode 100644 index 0000000..97aa7c9 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistStamped_.py @@ -0,0 +1,32 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + IDL file: TwistStamped_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import geometry_msgs + +# if TYPE_CHECKING: +# import std_msgs.msg.dds_ + + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class TwistStamped_(idl.IdlStruct, typename="geometry_msgs.msg.dds_.TwistStamped_"): + header: 'unitree_sdk2py.idl.std_msgs.msg.dds_.Header_' + twist: 'unitree_sdk2py.idl.geometry_msgs.msg.dds_.Twist_' + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistWithCovarianceStamped_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistWithCovarianceStamped_.py new file mode 100644 index 0000000..d322b7f --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistWithCovarianceStamped_.py @@ -0,0 +1,32 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + IDL file: TwistWithCovarianceStamped_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import geometry_msgs + +# if TYPE_CHECKING: +# import std_msgs.msg.dds_ + + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class TwistWithCovarianceStamped_(idl.IdlStruct, typename="geometry_msgs.msg.dds_.TwistWithCovarianceStamped_"): + header: 'unitree_sdk2py.idl.std_msgs.msg.dds_.Header_' + twist: 'unitree_sdk2py.idl.geometry_msgs.msg.dds_.TwistWithCovariance_' + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistWithCovariance_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistWithCovariance_.py new file mode 100644 index 0000000..7281bfc --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistWithCovariance_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + IDL file: TwistWithCovariance_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import geometry_msgs + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class TwistWithCovariance_(idl.IdlStruct, typename="geometry_msgs.msg.dds_.TwistWithCovariance_"): + twist: 'unitree_sdk2py.idl.geometry_msgs.msg.dds_.Twist_' + covariance: types.array[types.float64, 36] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Twist_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Twist_.py new file mode 100644 index 0000000..5cd3b23 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Twist_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + IDL file: Twist_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import geometry_msgs + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Twist_(idl.IdlStruct, typename="geometry_msgs.msg.dds_.Twist_"): + linear: 'unitree_sdk2py.idl.geometry_msgs.msg.dds_.Vector3_' + angular: 'unitree_sdk2py.idl.geometry_msgs.msg.dds_.Vector3_' + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Vector3_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Vector3_.py new file mode 100644 index 0000000..9cdeb0f --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Vector3_.py @@ -0,0 +1,29 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + IDL file: Vector3_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import geometry_msgs + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Vector3_(idl.IdlStruct, typename="geometry_msgs.msg.dds_.Vector3_"): + x: types.float64 + y: types.float64 + z: types.float64 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/__init__.py new file mode 100644 index 0000000..68c4fe2 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/__init__.py @@ -0,0 +1,23 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: geometry_msgs.msg.dds_ + +""" + +from ._Point32_ import Point32_ +from ._Point_ import Point_ +from ._PointStamped_ import PointStamped_ +from ._Pose2D_ import Pose2D_ +from ._Pose_ import Pose_ +from ._PoseStamped_ import PoseStamped_ +from ._PoseWithCovariance_ import PoseWithCovariance_ +from ._PoseWithCovarianceStamped_ import PoseWithCovarianceStamped_ +from ._Quaternion_ import Quaternion_ +from ._QuaternionStamped_ import QuaternionStamped_ +from ._Twist_ import Twist_ +from ._TwistStamped_ import TwistStamped_ +from ._TwistWithCovariance_ import TwistWithCovariance_ +from ._TwistWithCovarianceStamped_ import TwistWithCovarianceStamped_ +from ._Vector3_ import Vector3_ +__all__ = ["Point32_", "Point_", "PointStamped_", "Pose2D_", "Pose_", "PoseStamped_", "PoseWithCovariance_", "PoseWithCovarianceStamped_", "Quaternion_", "QuaternionStamped_", "Twist_", "TwistStamped_", "TwistWithCovariance_", "TwistWithCovarianceStamped_", "Vector3_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/__init__.py new file mode 100644 index 0000000..a2724c2 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: nav_msgs + +""" + +from . import msg +__all__ = ["msg", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/__init__.py new file mode 100644 index 0000000..d47683b --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: nav_msgs.msg + +""" + +from . import dds_ +__all__ = ["dds_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_MapMetaData_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_MapMetaData_.py new file mode 100644 index 0000000..9228cd6 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_MapMetaData_.py @@ -0,0 +1,35 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: nav_msgs.msg.dds_ + IDL file: MapMetaData_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import nav_msgs + +# if TYPE_CHECKING: +# import builtin_interfaces.msg.dds_ +# import geometry_msgs.msg.dds_ + + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class MapMetaData_(idl.IdlStruct, typename="nav_msgs.msg.dds_.MapMetaData_"): + map_load_time: 'unitree_sdk2py.idl.builtin_interfaces.msg.dds_.Time_' + resolution: types.float32 + width: types.uint32 + height: types.uint32 + origin: 'unitree_sdk2py.idl.geometry_msgs.msg.dds_.Pose_' + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_OccupancyGrid_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_OccupancyGrid_.py new file mode 100644 index 0000000..0274c58 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_OccupancyGrid_.py @@ -0,0 +1,33 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: nav_msgs.msg.dds_ + IDL file: OccupancyGrid_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import nav_msgs + +# if TYPE_CHECKING: +# import std_msgs.msg.dds_ + + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class OccupancyGrid_(idl.IdlStruct, typename="nav_msgs.msg.dds_.OccupancyGrid_"): + header: 'unitree_sdk2py.idl.std_msgs.msg.dds_.Header_' + info: 'unitree_sdk2py.idl.nav_msgs.msg.dds_.MapMetaData_' + data: types.sequence[types.uint8] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_Odometry_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_Odometry_.py new file mode 100644 index 0000000..054bb5c --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_Odometry_.py @@ -0,0 +1,35 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: nav_msgs.msg.dds_ + IDL file: Odometry_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import nav_msgs + +# if TYPE_CHECKING: +# import geometry_msgs.msg.dds_ +# import std_msgs.msg.dds_ + + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Odometry_(idl.IdlStruct, typename="nav_msgs.msg.dds_.Odometry_"): + header: 'unitree_sdk2py.idl.std_msgs.msg.dds_.Header_' + child_frame_id: str + pose: 'unitree_sdk2py.idl.geometry_msgs.msg.dds_.PoseWithCovariance_' + twist: 'unitree_sdk2py.idl.geometry_msgs.msg.dds_.TwistWithCovariance_' + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/__init__.py new file mode 100644 index 0000000..a14e17a --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/__init__.py @@ -0,0 +1,11 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: nav_msgs.msg.dds_ + +""" + +from ._MapMetaData_ import MapMetaData_ +from ._OccupancyGrid_ import OccupancyGrid_ +from ._Odometry_ import Odometry_ +__all__ = ["MapMetaData_", "OccupancyGrid_", "Odometry_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/__init__.py new file mode 100644 index 0000000..984e7e2 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: sensor_msgs + +""" + +from . import msg +__all__ = ["msg", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/__init__.py new file mode 100644 index 0000000..62a2292 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: sensor_msgs.msg + +""" + +from . import dds_ +__all__ = ["dds_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/PointField_Constants/_PointField_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/PointField_Constants/_PointField_.py new file mode 100644 index 0000000..673aa84 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/PointField_Constants/_PointField_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: sensor_msgs.msg.dds_.PointField_Constants + IDL file: PointField_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import sensor_msgs + +INT8_ = 1 +UINT8_ = 2 +INT16_ = 3 +UINT16_ = 4 +INT32_ = 5 +UINT32_ = 6 +FLOAT32_ = 7 +FLOAT64_ = 8 + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/PointField_Constants/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/PointField_Constants/__init__.py new file mode 100644 index 0000000..310fecd --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/PointField_Constants/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: sensor_msgs.msg.dds_.PointField_Constants + +""" + +from ._PointField_ import FLOAT32_, FLOAT64_, INT16_, INT32_, INT8_, UINT16_, UINT32_, UINT8_ +__all__ = ["FLOAT32_", "FLOAT64_", "INT16_", "INT32_", "INT8_", "UINT16_", "UINT32_", "UINT8_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/_PointCloud2_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/_PointCloud2_.py new file mode 100644 index 0000000..ffdf713 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/_PointCloud2_.py @@ -0,0 +1,39 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: sensor_msgs.msg.dds_ + IDL file: PointCloud2_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import sensor_msgs + +# if TYPE_CHECKING: +# import std_msgs.msg.dds_ + + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class PointCloud2_(idl.IdlStruct, typename="sensor_msgs.msg.dds_.PointCloud2_"): + header: 'unitree_sdk2py.idl.std_msgs.msg.dds_.Header_' + height: types.uint32 + width: types.uint32 + fields: types.sequence['unitree_sdk2py.idl.sensor_msgs.msg.dds_.PointField_'] + is_bigendian: bool + point_step: types.uint32 + row_step: types.uint32 + data: types.sequence[types.uint8] + is_dense: bool + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/_PointField_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/_PointField_.py new file mode 100644 index 0000000..e62dbf9 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/_PointField_.py @@ -0,0 +1,30 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: sensor_msgs.msg.dds_ + IDL file: PointField_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import sensor_msgs + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class PointField_(idl.IdlStruct, typename="sensor_msgs.msg.dds_.PointField_"): + name: str + offset: types.uint32 + datatype: types.uint8 + count: types.uint32 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/__init__.py new file mode 100644 index 0000000..68e0141 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/__init__.py @@ -0,0 +1,11 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: sensor_msgs.msg.dds_ + +""" + +from . import PointField_Constants +from ._PointCloud2_ import PointCloud2_ +from ._PointField_ import PointField_ +__all__ = ["PointField_Constants", "PointCloud2_", "PointField_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/__init__.py new file mode 100644 index 0000000..c17a705 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: std_msgs + +""" + +from . import msg +__all__ = ["msg", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/__init__.py new file mode 100644 index 0000000..02c8fe3 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: std_msgs.msg + +""" + +from . import dds_ +__all__ = ["dds_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/_Header_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/_Header_.py new file mode 100644 index 0000000..bd3564f --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/_Header_.py @@ -0,0 +1,32 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: std_msgs.msg.dds_ + IDL file: Header_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import std_msgs + +# if TYPE_CHECKING: +# import builtin_interfaces.msg.dds_ + + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Header_(idl.IdlStruct, typename="std_msgs.msg.dds_.Header_"): + stamp: 'unitree_sdk2py.idl.builtin_interfaces.msg.dds_.Time_' + frame_id: str + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/_String_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/_String_.py new file mode 100644 index 0000000..052f122 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/_String_.py @@ -0,0 +1,27 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: std_msgs.msg.dds_ + IDL file: String_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import std_msgs + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class String_(idl.IdlStruct, typename="std_msgs.msg.dds_.String_"): + data: str + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/__init__.py new file mode 100644 index 0000000..f5282e5 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/__init__.py @@ -0,0 +1,10 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: std_msgs.msg.dds_ + +""" + +from ._Header_ import Header_ +from ._String_ import String_ +__all__ = ["Header_", "String_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/__init__.py new file mode 100644 index 0000000..e6546ff --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.10.2 + Module: unitree_api + +""" + +from . import msg +__all__ = ["msg", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/__init__.py new file mode 100644 index 0000000..bbf9fde --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.10.2 + Module: unitree_api.msg + +""" + +from . import dds_ +__all__ = ["dds_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestHeader_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestHeader_.py new file mode 100644 index 0000000..57d67a0 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestHeader_.py @@ -0,0 +1,29 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.10.2 + Module: unitree_api.msg.dds_ + IDL file: RequestHeader_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_api + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class RequestHeader_(idl.IdlStruct, typename="unitree_api.msg.dds_.RequestHeader_"): + identity: 'unitree_sdk2py.idl.unitree_api.msg.dds_.RequestIdentity_' + lease: 'unitree_sdk2py.idl.unitree_api.msg.dds_.RequestLease_' + policy: 'unitree_sdk2py.idl.unitree_api.msg.dds_.RequestPolicy_' + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestIdentity_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestIdentity_.py new file mode 100644 index 0000000..a891b54 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestIdentity_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.10.2 + Module: unitree_api.msg.dds_ + IDL file: RequestIdentity_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_api + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class RequestIdentity_(idl.IdlStruct, typename="unitree_api.msg.dds_.RequestIdentity_"): + id: types.int64 + api_id: types.int64 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestLease_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestLease_.py new file mode 100644 index 0000000..32cef0f --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestLease_.py @@ -0,0 +1,27 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.10.2 + Module: unitree_api.msg.dds_ + IDL file: RequestLease_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_api + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class RequestLease_(idl.IdlStruct, typename="unitree_api.msg.dds_.RequestLease_"): + id: types.int64 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestPolicy_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestPolicy_.py new file mode 100644 index 0000000..aa7ef2f --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestPolicy_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.10.2 + Module: unitree_api.msg.dds_ + IDL file: RequestPolicy_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_api + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class RequestPolicy_(idl.IdlStruct, typename="unitree_api.msg.dds_.RequestPolicy_"): + priority: types.int32 + noreply: bool + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_Request_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_Request_.py new file mode 100644 index 0000000..39c6b43 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_Request_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.10.2 + Module: unitree_api.msg.dds_ + IDL file: Request_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_api + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Request_(idl.IdlStruct, typename="unitree_api.msg.dds_.Request_"): + header: 'unitree_sdk2py.idl.unitree_api.msg.dds_.RequestHeader_' + parameter: str + binary: types.sequence[types.uint8] + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_ResponseHeader_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_ResponseHeader_.py new file mode 100644 index 0000000..ef47340 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_ResponseHeader_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.10.2 + Module: unitree_api.msg.dds_ + IDL file: ResponseHeader_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_api + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class ResponseHeader_(idl.IdlStruct, typename="unitree_api.msg.dds_.ResponseHeader_"): + identity: 'unitree_sdk2py.idl.unitree_api.msg.dds_.RequestIdentity_' + status: 'unitree_sdk2py.idl.unitree_api.msg.dds_.ResponseStatus_' + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_ResponseStatus_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_ResponseStatus_.py new file mode 100644 index 0000000..92c0b1b --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_ResponseStatus_.py @@ -0,0 +1,27 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.10.2 + Module: unitree_api.msg.dds_ + IDL file: ResponseStatus_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_api + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class ResponseStatus_(idl.IdlStruct, typename="unitree_api.msg.dds_.ResponseStatus_"): + code: types.int32 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_Response_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_Response_.py new file mode 100644 index 0000000..c743651 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_Response_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.10.2 + Module: unitree_api.msg.dds_ + IDL file: Response_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_api + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Response_(idl.IdlStruct, typename="unitree_api.msg.dds_.Response_"): + header: 'unitree_sdk2py.idl.unitree_api.msg.dds_.ResponseHeader_' + data: str + binary: types.sequence[types.uint8] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/__init__.py new file mode 100644 index 0000000..1e4c9f8 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/__init__.py @@ -0,0 +1,16 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.10.2 + Module: unitree_api.msg.dds_ + +""" + +from ._RequestHeader_ import RequestHeader_ +from ._RequestIdentity_ import RequestIdentity_ +from ._RequestLease_ import RequestLease_ +from ._RequestPolicy_ import RequestPolicy_ +from ._Request_ import Request_ +from ._ResponseHeader_ import ResponseHeader_ +from ._ResponseStatus_ import ResponseStatus_ +from ._Response_ import Response_ +__all__ = ["RequestHeader_", "RequestIdentity_", "RequestLease_", "RequestPolicy_", "Request_", "ResponseHeader_", "ResponseStatus_", "Response_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/__init__.py new file mode 100644 index 0000000..b553006 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go + +""" + +from . import msg +__all__ = ["msg", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/__init__.py new file mode 100644 index 0000000..f8cae9b --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg + +""" + +from . import dds_ +__all__ = ["dds_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_AudioData_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_AudioData_.py new file mode 100644 index 0000000..27913c9 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_AudioData_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: AudioData_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class AudioData_(idl.IdlStruct, typename="unitree_go.msg.dds_.AudioData_"): + time_frame: types.uint64 + data: types.sequence[types.uint8] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_BmsCmd_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_BmsCmd_.py new file mode 100644 index 0000000..5da647e --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_BmsCmd_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: BmsCmd_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class BmsCmd_(idl.IdlStruct, typename="unitree_go.msg.dds_.BmsCmd_"): + off: types.uint8 + reserve: types.array[types.uint8, 3] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_BmsState_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_BmsState_.py new file mode 100644 index 0000000..af635e8 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_BmsState_.py @@ -0,0 +1,35 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: BmsState_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class BmsState_(idl.IdlStruct, typename="unitree_go.msg.dds_.BmsState_"): + version_high: types.uint8 + version_low: types.uint8 + status: types.uint8 + soc: types.uint8 + current: types.int32 + cycle: types.uint16 + bq_ntc: types.array[types.uint8, 2] + mcu_ntc: types.array[types.uint8, 2] + cell_vol: types.array[types.uint16, 15] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Error_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Error_.py new file mode 100644 index 0000000..c338c69 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Error_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: Error_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Error_(idl.IdlStruct, typename="unitree_go.msg.dds_.Error_"): + source: types.uint32 + state: types.uint32 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Go2FrontVideoData_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Go2FrontVideoData_.py new file mode 100644 index 0000000..04d245b --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Go2FrontVideoData_.py @@ -0,0 +1,30 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: Go2FrontVideoData_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Go2FrontVideoData_(idl.IdlStruct, typename="unitree_go.msg.dds_.Go2FrontVideoData_"): + time_frame: types.uint64 + video720p: types.sequence[types.uint8] + video360p: types.sequence[types.uint8] + video180p: types.sequence[types.uint8] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_HeightMap_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_HeightMap_.py new file mode 100644 index 0000000..48168da --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_HeightMap_.py @@ -0,0 +1,33 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: HeightMap_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class HeightMap_(idl.IdlStruct, typename="unitree_go.msg.dds_.HeightMap_"): + stamp: types.float64 + frame_id: str + resolution: types.float32 + width: types.uint32 + height: types.uint32 + origin: types.array[types.float32, 2] + data: types.sequence[types.float32] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_IMUState_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_IMUState_.py new file mode 100644 index 0000000..e48908f --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_IMUState_.py @@ -0,0 +1,31 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: IMUState_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class IMUState_(idl.IdlStruct, typename="unitree_go.msg.dds_.IMUState_"): + quaternion: types.array[types.float32, 4] + gyroscope: types.array[types.float32, 3] + accelerometer: types.array[types.float32, 3] + rpy: types.array[types.float32, 3] + temperature: types.uint8 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_InterfaceConfig_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_InterfaceConfig_.py new file mode 100644 index 0000000..5efbd64 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_InterfaceConfig_.py @@ -0,0 +1,29 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: InterfaceConfig_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class InterfaceConfig_(idl.IdlStruct, typename="unitree_go.msg.dds_.InterfaceConfig_"): + mode: types.uint8 + value: types.uint8 + reserve: types.array[types.uint8, 2] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LidarState_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LidarState_.py new file mode 100644 index 0000000..6861068 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LidarState_.py @@ -0,0 +1,43 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: LidarState_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class LidarState_(idl.IdlStruct, typename="unitree_go.msg.dds_.LidarState_"): + stamp: types.float64 + firmware_version: str + software_version: str + sdk_version: str + sys_rotation_speed: types.float32 + com_rotation_speed: types.float32 + error_state: types.uint8 + cloud_frequency: types.float32 + cloud_packet_loss_rate: types.float32 + cloud_size: types.uint32 + cloud_scan_num: types.uint32 + imu_frequency: types.float32 + imu_packet_loss_rate: types.float32 + imu_rpy: types.array[types.float32, 3] + serial_recv_stamp: types.float64 + serial_buffer_size: types.uint32 + serial_buffer_read: types.uint32 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LowCmd_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LowCmd_.py new file mode 100644 index 0000000..bcd4781 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LowCmd_.py @@ -0,0 +1,40 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: LowCmd_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class LowCmd_(idl.IdlStruct, typename="unitree_go.msg.dds_.LowCmd_"): + head: types.array[types.uint8, 2] + level_flag: types.uint8 + frame_reserve: types.uint8 + sn: types.array[types.uint32, 2] + version: types.array[types.uint32, 2] + bandwidth: types.uint16 + motor_cmd: types.array['unitree_sdk2py.idl.unitree_go.msg.dds_.MotorCmd_', 20] + bms_cmd: 'unitree_sdk2py.idl.unitree_go.msg.dds_.BmsCmd_' + wireless_remote: types.array[types.uint8, 40] + led: types.array[types.uint8, 12] + fan: types.array[types.uint8, 2] + gpio: types.uint8 + reserve: types.uint32 + crc: types.uint32 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LowState_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LowState_.py new file mode 100644 index 0000000..79ecbe6 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LowState_.py @@ -0,0 +1,48 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: LowState_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class LowState_(idl.IdlStruct, typename="unitree_go.msg.dds_.LowState_"): + head: types.array[types.uint8, 2] + level_flag: types.uint8 + frame_reserve: types.uint8 + sn: types.array[types.uint32, 2] + version: types.array[types.uint32, 2] + bandwidth: types.uint16 + imu_state: 'unitree_sdk2py.idl.unitree_go.msg.dds_.IMUState_' + motor_state: types.array['unitree_sdk2py.idl.unitree_go.msg.dds_.MotorState_', 20] + bms_state: 'unitree_sdk2py.idl.unitree_go.msg.dds_.BmsState_' + foot_force: types.array[types.int16, 4] + foot_force_est: types.array[types.int16, 4] + tick: types.uint32 + wireless_remote: types.array[types.uint8, 40] + bit_flag: types.uint8 + adc_reel: types.float32 + temperature_ntc1: types.uint8 + temperature_ntc2: types.uint8 + power_v: types.float32 + power_a: types.float32 + fan_frequency: types.array[types.uint16, 4] + reserve: types.uint32 + crc: types.uint32 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorCmd_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorCmd_.py new file mode 100644 index 0000000..5a2dd85 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorCmd_.py @@ -0,0 +1,33 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: MotorCmd_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class MotorCmd_(idl.IdlStruct, typename="unitree_go.msg.dds_.MotorCmd_"): + mode: types.uint8 + q: types.float32 + dq: types.float32 + tau: types.float32 + kp: types.float32 + kd: types.float32 + reserve: types.array[types.uint32, 3] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorCmds_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorCmds_.py new file mode 100644 index 0000000..11d3029 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorCmds_.py @@ -0,0 +1,23 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: MotorCmds_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass, field + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class MotorCmds_(idl.IdlStruct, typename="unitree_go.msg.dds_.MotorCmds_"): + cmds: types.sequence['unitree_sdk2py.idl.unitree_go.msg.dds_.MotorCmd_'] = field(default_factory=lambda: []) + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorState_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorState_.py new file mode 100644 index 0000000..0787acb --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorState_.py @@ -0,0 +1,37 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: MotorState_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class MotorState_(idl.IdlStruct, typename="unitree_go.msg.dds_.MotorState_"): + mode: types.uint8 + q: types.float32 + dq: types.float32 + ddq: types.float32 + tau_est: types.float32 + q_raw: types.float32 + dq_raw: types.float32 + ddq_raw: types.float32 + temperature: types.uint8 + lost: types.uint32 + reserve: types.array[types.uint32, 2] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorStates_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorStates_.py new file mode 100644 index 0000000..4b0245b --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorStates_.py @@ -0,0 +1,23 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: MotorStates_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass, field + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class MotorStates_(idl.IdlStruct, typename="unitree_go.msg.dds_.MotorStates_"): + states: types.sequence['unitree_sdk2py.idl.unitree_go.msg.dds_.MotorState_'] = field(default_factory=lambda: []) + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_PathPoint_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_PathPoint_.py new file mode 100644 index 0000000..20a8c55 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_PathPoint_.py @@ -0,0 +1,33 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: PathPoint_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class PathPoint_(idl.IdlStruct, typename="unitree_go.msg.dds_.PathPoint_"): + t_from_start: types.float32 + x: types.float32 + y: types.float32 + yaw: types.float32 + vx: types.float32 + vy: types.float32 + vyaw: types.float32 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Req_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Req_.py new file mode 100644 index 0000000..5bb39b3 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Req_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: Req_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Req_(idl.IdlStruct, typename="unitree_go.msg.dds_.Req_"): + uuid: str + body: str + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Res_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Res_.py new file mode 100644 index 0000000..684a5a7 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Res_.py @@ -0,0 +1,29 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: Res_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class Res_(idl.IdlStruct, typename="unitree_go.msg.dds_.Res_"): + uuid: str + data: types.sequence[types.uint8] + body: str + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_SportModeState_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_SportModeState_.py new file mode 100644 index 0000000..e0a26a1 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_SportModeState_.py @@ -0,0 +1,42 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: SportModeState_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class SportModeState_(idl.IdlStruct, typename="unitree_go.msg.dds_.SportModeState_"): + stamp: 'unitree_sdk2py.idl.unitree_go.msg.dds_.TimeSpec_' + error_code: types.uint32 + imu_state: 'unitree_sdk2py.idl.unitree_go.msg.dds_.IMUState_' + mode: types.uint8 + progress: types.float32 + gait_type: types.uint8 + foot_raise_height: types.float32 + position: types.array[types.float32, 3] + body_height: types.float32 + velocity: types.array[types.float32, 3] + yaw_speed: types.float32 + range_obstacle: types.array[types.float32, 4] + foot_force: types.array[types.int16, 4] + foot_position_body: types.array[types.float32, 12] + foot_speed_body: types.array[types.float32, 12] + path_point: types.array['unitree_sdk2py.idl.unitree_go.msg.dds_.PathPoint_', 10] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_TimeSpec_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_TimeSpec_.py new file mode 100644 index 0000000..7a580b6 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_TimeSpec_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: TimeSpec_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class TimeSpec_(idl.IdlStruct, typename="unitree_go.msg.dds_.TimeSpec_"): + sec: types.int32 + nanosec: types.uint32 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_UwbState_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_UwbState_.py new file mode 100644 index 0000000..d5f2588 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_UwbState_.py @@ -0,0 +1,43 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: UwbState_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class UwbState_(idl.IdlStruct, typename="unitree_go.msg.dds_.UwbState_"): + version: types.array[types.uint8, 2] + channel: types.uint8 + joy_mode: types.uint8 + orientation_est: types.float32 + pitch_est: types.float32 + distance_est: types.float32 + yaw_est: types.float32 + tag_roll: types.float32 + tag_pitch: types.float32 + tag_yaw: types.float32 + base_roll: types.float32 + base_pitch: types.float32 + base_yaw: types.float32 + joystick: types.array[types.float32, 2] + error_state: types.uint8 + buttons: types.uint8 + enabled_from_app: types.uint8 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_UwbSwitch_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_UwbSwitch_.py new file mode 100644 index 0000000..b902ef8 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_UwbSwitch_.py @@ -0,0 +1,27 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: UwbSwitch_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class UwbSwitch_(idl.IdlStruct, typename="unitree_go.msg.dds_.UwbSwitch_"): + enabled: types.uint8 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_WirelessController_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_WirelessController_.py new file mode 100644 index 0000000..18919dd --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_WirelessController_.py @@ -0,0 +1,31 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + IDL file: WirelessController_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_go + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class WirelessController_(idl.IdlStruct, typename="unitree_go.msg.dds_.WirelessController_"): + lx: types.float32 + ly: types.float32 + rx: types.float32 + ry: types.float32 + keys: types.uint16 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/__init__.py new file mode 100644 index 0000000..fc24f6f --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/__init__.py @@ -0,0 +1,31 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_go.msg.dds_ + +""" + +from ._AudioData_ import AudioData_ +from ._BmsCmd_ import BmsCmd_ +from ._BmsState_ import BmsState_ +from ._Error_ import Error_ +from ._Go2FrontVideoData_ import Go2FrontVideoData_ +from ._HeightMap_ import HeightMap_ +from ._IMUState_ import IMUState_ +from ._InterfaceConfig_ import InterfaceConfig_ +from ._LidarState_ import LidarState_ +from ._LowCmd_ import LowCmd_ +from ._LowState_ import LowState_ +from ._MotorCmd_ import MotorCmd_ +from ._MotorCmds_ import MotorCmds_ +from ._MotorState_ import MotorState_ +from ._MotorStates_ import MotorStates_ +from ._Req_ import Req_ +from ._Res_ import Res_ +from ._SportModeState_ import SportModeState_ +from ._TimeSpec_ import TimeSpec_ +from ._PathPoint_ import PathPoint_ +from ._UwbState_ import UwbState_ +from ._UwbSwitch_ import UwbSwitch_ +from ._WirelessController_ import WirelessController_ +__all__ = ["AudioData_", "BmsCmd_", "BmsState_", "Error_", "Go2FrontVideoData_", "HeightMap_", "IMUState_", "InterfaceConfig_", "LidarState_", "LowCmd_", "LowState_", "MotorCmd_", "MotorCmds_", "MotorState_", "MotorStates_", "Req_", "Res_", "SportModeState_", "TimeSpec_", "PathPoint_", "UwbState_", "UwbSwitch_", "WirelessController_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/.idlpy_manifest b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/.idlpy_manifest new file mode 100644 index 0000000..fd4ea94 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/.idlpy_manifest @@ -0,0 +1,43 @@ +BmsCmd_ +msg + + +BmsState_ +msg + + +HandCmd_ +msg + + +HandState_ +msg + + +IMUState_ +msg + + +LowCmd_ +msg + + +LowState_ +msg + + +MainBoardState_ +msg + + +MotorCmd_ +msg + + +MotorState_ +msg + + +PressSensorState_ +msg + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/__init__.py new file mode 100644 index 0000000..5f55ee5 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_hg + +""" + +from . import msg +__all__ = ["msg", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/.idlpy_manifest b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/.idlpy_manifest new file mode 100644 index 0000000..3218e96 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/.idlpy_manifest @@ -0,0 +1,43 @@ +BmsCmd_ +dds_ + + +BmsState_ +dds_ + + +HandCmd_ +dds_ + + +HandState_ +dds_ + + +IMUState_ +dds_ + + +LowCmd_ +dds_ + + +LowState_ +dds_ + + +MainBoardState_ +dds_ + + +MotorCmd_ +dds_ + + +MotorState_ +dds_ + + +PressSensorState_ +dds_ + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/__init__.py new file mode 100644 index 0000000..02b132d --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/__init__.py @@ -0,0 +1,9 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_hg.msg + +""" + +from . import dds_ +__all__ = ["dds_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/.idlpy_manifest b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/.idlpy_manifest new file mode 100644 index 0000000..6174f37 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/.idlpy_manifest @@ -0,0 +1,43 @@ +BmsCmd_ + +BmsCmd_ + +BmsState_ + +BmsState_ + +HandCmd_ + +HandCmd_ + +HandState_ + +HandState_ + +IMUState_ + +IMUState_ + +LowCmd_ + +LowCmd_ + +LowState_ + +LowState_ + +MainBoardState_ + +MainBoardState_ + +MotorCmd_ + +MotorCmd_ + +MotorState_ + +MotorState_ + +PressSensorState_ + +PressSensorState_ diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_BmsCmd_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_BmsCmd_.py new file mode 100644 index 0000000..525d1a4 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_BmsCmd_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_hg.msg.dds_ + IDL file: BmsCmd_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_hg + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class BmsCmd_(idl.IdlStruct, typename="unitree_hg.msg.dds_.BmsCmd_"): + cmd: types.uint8 + reserve: types.array[types.uint8, 40] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_BmsState_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_BmsState_.py new file mode 100644 index 0000000..dd0c6bd --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_BmsState_.py @@ -0,0 +1,39 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_hg.msg.dds_ + IDL file: BmsState_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_hg + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class BmsState_(idl.IdlStruct, typename="unitree_hg.msg.dds_.BmsState_"): + version_high: types.uint8 + version_low: types.uint8 + fn: types.uint8 + cell_vol: types.array[types.uint16, 40] + bmsvoltage: types.array[types.uint32, 3] + current: types.int32 + soc: types.uint8 + soh: types.uint8 + temperature: types.array[types.int16, 12] + cycle: types.uint16 + manufacturer_date: types.uint16 + bmsstate: types.array[types.uint32, 5] + reserve: types.array[types.uint32, 3] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_HandCmd_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_HandCmd_.py new file mode 100644 index 0000000..044afcb --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_HandCmd_.py @@ -0,0 +1,28 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_hg.msg.dds_ + IDL file: HandCmd_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_hg + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class HandCmd_(idl.IdlStruct, typename="unitree_hg.msg.dds_.HandCmd_"): + motor_cmd: types.sequence['unitree_sdk2py.idl.unitree_hg.msg.dds_.MotorCmd_'] + reserve: types.array[types.uint32, 4] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_HandState_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_HandState_.py new file mode 100644 index 0000000..351dc75 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_HandState_.py @@ -0,0 +1,35 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_hg.msg.dds_ + IDL file: HandState_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_hg + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class HandState_(idl.IdlStruct, typename="unitree_hg.msg.dds_.HandState_"): + motor_state: types.sequence['unitree_sdk2py.idl.unitree_hg.msg.dds_.MotorState_'] + press_sensor_state: types.sequence['unitree_sdk2py.idl.unitree_hg.msg.dds_.PressSensorState_'] + imu_state: 'unitree_sdk2py.idl.unitree_hg.msg.dds_.IMUState_' + power_v: types.float32 + power_a: types.float32 + system_v: types.float32 + device_v: types.float32 + error: types.array[types.uint32, 2] + reserve: types.array[types.uint32, 2] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_IMUState_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_IMUState_.py new file mode 100644 index 0000000..75d01a6 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_IMUState_.py @@ -0,0 +1,31 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_hg.msg.dds_ + IDL file: IMUState_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_hg + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class IMUState_(idl.IdlStruct, typename="unitree_hg.msg.dds_.IMUState_"): + quaternion: types.array[types.float32, 4] + gyroscope: types.array[types.float32, 3] + accelerometer: types.array[types.float32, 3] + rpy: types.array[types.float32, 3] + temperature: types.int16 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_LowCmd_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_LowCmd_.py new file mode 100644 index 0000000..56b4b8b --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_LowCmd_.py @@ -0,0 +1,31 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_hg.msg.dds_ + IDL file: LowCmd_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_hg + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class LowCmd_(idl.IdlStruct, typename="unitree_hg.msg.dds_.LowCmd_"): + mode_pr: types.uint8 + mode_machine: types.uint8 + motor_cmd: types.array['unitree_sdk2py.idl.unitree_hg.msg.dds_.MotorCmd_', 35] + reserve: types.array[types.uint32, 4] + crc: types.uint32 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_LowState_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_LowState_.py new file mode 100644 index 0000000..003c815 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_LowState_.py @@ -0,0 +1,37 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_hg.msg.dds_ + IDL file: LowState_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_hg + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class LowState_(idl.IdlStruct, typename="unitree_hg.msg.dds_.LowState_"): + version: types.array[types.uint32, 2] + mode_pr: types.uint8 + mode_machine: types.uint8 + tick: types.uint32 + imu_state: 'unitree_sdk2py.idl.unitree_hg.msg.dds_.IMUState_' + # position: types.array[types.float32, 3] + # linear_velocity: types.array[types.float32, 3] + motor_state: types.array['unitree_sdk2py.idl.unitree_hg.msg.dds_.MotorState_', 35] + wireless_remote: types.array[types.uint8, 40] + reserve: types.array[types.uint32, 4] + crc: types.uint32 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MainBoardState_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MainBoardState_.py new file mode 100644 index 0000000..c280245 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MainBoardState_.py @@ -0,0 +1,30 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_hg.msg.dds_ + IDL file: MainBoardState_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_hg + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class MainBoardState_(idl.IdlStruct, typename="unitree_hg.msg.dds_.MainBoardState_"): + fan_state: types.array[types.uint16, 6] + temperature: types.array[types.int16, 6] + value: types.array[types.float32, 6] + state: types.array[types.uint32, 6] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MotorCmd_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MotorCmd_.py new file mode 100644 index 0000000..3dd8f9d --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MotorCmd_.py @@ -0,0 +1,33 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_hg.msg.dds_ + IDL file: MotorCmd_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_hg + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class MotorCmd_(idl.IdlStruct, typename="unitree_hg.msg.dds_.MotorCmd_"): + mode: types.uint8 + q: types.float32 + dq: types.float32 + tau: types.float32 + kp: types.float32 + kd: types.float32 + reserve: types.uint32 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MotorState_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MotorState_.py new file mode 100644 index 0000000..839fccf --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MotorState_.py @@ -0,0 +1,36 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_hg.msg.dds_ + IDL file: MotorState_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_hg + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class MotorState_(idl.IdlStruct, typename="unitree_hg.msg.dds_.MotorState_"): + mode: types.uint8 + q: types.float32 + dq: types.float32 + ddq: types.float32 + tau_est: types.float32 + temperature: types.array[types.int16, 2] + vol: types.float32 + sensor: types.array[types.uint32, 2] + motorstate: types.uint32 + reserve: types.array[types.uint32, 4] + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_OdoState_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_OdoState_.py new file mode 100644 index 0000000..f3ff673 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_OdoState_.py @@ -0,0 +1,32 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DX IDL version: v0.11.0 + Module: unitree_hg.msg.dds_ + IDL file: OdoState_.idl + + +""" + +from enum import auto +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_hg + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class OdoState_(idl.IdlStruct, typename="unitree_hg.msg.dds_.OdoState_"): + version: types.array[types.uint32, 2] + tick: types.uint32 + position: types.array[types.float32, 3] + orientation: types.array[types.float32, 4] # quaternion [x, y, z, w] + linear_velocity: types.array[types.float32, 3] + angular_velocity: types.array[types.float32, 3] + crc: types.uint32 + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_PressSensorState_.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_PressSensorState_.py new file mode 100644 index 0000000..7dcf794 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_PressSensorState_.py @@ -0,0 +1,30 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_hg.msg.dds_ + IDL file: PressSensorState_.idl + +""" + +from enum import auto +from typing import TYPE_CHECKING, Optional +from dataclasses import dataclass + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate +import cyclonedds.idl.types as types + +# root module import for resolving types +# import unitree_hg + + +@dataclass +@annotate.final +@annotate.autoid("sequential") +class PressSensorState_(idl.IdlStruct, typename="unitree_hg.msg.dds_.PressSensorState_"): + pressure: types.array[types.float32, 12] + temperature: types.array[types.float32, 12] + lost: types.uint32 + reserve: types.uint32 + + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/__init__.py new file mode 100644 index 0000000..b4bca37 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/__init__.py @@ -0,0 +1,20 @@ +""" + Generated by Eclipse Cyclone DDS idlc Python Backend + Cyclone DDS IDL version: v0.11.0 + Module: unitree_hg.msg.dds_ + +""" + +from ._BmsCmd_ import BmsCmd_ +from ._BmsState_ import BmsState_ +from ._HandCmd_ import HandCmd_ +from ._HandState_ import HandState_ +from ._IMUState_ import IMUState_ +from ._OdoState_ import OdoState_ +from ._LowCmd_ import LowCmd_ +from ._LowState_ import LowState_ +from ._MainBoardState_ import MainBoardState_ +from ._MotorCmd_ import MotorCmd_ +from ._MotorState_ import MotorState_ +from ._PressSensorState_ import PressSensorState_ +__all__ = ["BmsCmd_", "BmsState_", "HandCmd_", "HandState_", "IMUState_", "OdoState_", "LowCmd_", "LowState_", "MainBoardState_", "MotorCmd_", "MotorState_", "PressSensorState_", ] diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/client.py new file mode 100644 index 0000000..d774e53 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/client.py @@ -0,0 +1,89 @@ +from .client_base import ClientBase +from .lease_client import LeaseClient +from .internal import * + +""" +" class Client +""" +class Client(ClientBase): + def __init__(self, serviceName: str, enabaleLease: bool = False): + super().__init__(serviceName) + + self.__apiMapping = {} + self.__apiVersion = None + self.__leaseClient = None + self.__enableLease = enabaleLease + + if (self.__enableLease): + self.__leaseClient = LeaseClient(serviceName) + self.__leaseClient.Init() + + def WaitLeaseApplied(self): + if self.__enableLease: + self.__leaseClient.WaitApplied() + + def GetLeaseId(self): + if self.__enableLease: + return self.__leaseClient.GetId() + else: + return None + + def GetApiVersion(self): + return self.__apiVersion + + def GetServerApiVersion(self): + code, apiVerson = self._CallBase(RPC_API_ID_INTERNAL_API_VERSION, "{}", 0, 0) + if code != 0: + print("[Client] get server api version error:", code) + return code, None + else: + return code, apiVerson + + def _SetApiVerson(self, apiVersion: str): + self.__apiVersion = apiVersion + + def _Call(self, apiId: int, parameter: str): + ret, proirity, leaseId = self.__CheckApi(apiId) + if ret == 0: + return self._CallBase(apiId, parameter, proirity, leaseId) + else: + return RPC_ERR_CLIENT_API_NOT_REG, None + + def _CallNoReply(self, apiId: int, parameter: str): + ret, proirity, leaseId = self.__CheckApi(apiId) + if ret == 0: + return self._CallNoReplyBase(apiId, parameter, proirity, leaseId) + else: + return RPC_ERR_CLIENT_API_NOT_REG + + def _CallBinary(self, apiId: int, parameter: list): + ret, proirity, leaseId = self.__CheckApi(apiId) + if ret == 0: + return self._CallBinaryBase(apiId, parameter, proirity, leaseId) + else: + return RPC_ERR_CLIENT_API_NOT_REG, None + + def _CallBinaryNoReply(self, apiId: int, parameter: list): + ret, proirity, leaseId = self.__CheckApi(apiId) + if ret == 0: + return self._CallBinaryNoReplyBase(apiId, parameter, proirity, leaseId) + else: + return RPC_ERR_CLIENT_API_NOT_REG + + def _RegistApi(self, apiId: int, proirity: int): + self.__apiMapping[apiId] = proirity + + def __CheckApi(self, apiId: int): + proirity = 0 + leaseId = 0 + + if apiId > RPC_INTERNAL_API_ID_MAX: + proirity = self.__apiMapping.get(apiId) + + if proirity is None: + return RPC_ERR_CLIENT_API_NOT_REG, proirity, leaseId + + if self.__enableLease: + leaseId = self.__leaseClient.GetId() + + return 0, proirity, leaseId \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/client_base.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/client_base.py new file mode 100644 index 0000000..1bac028 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/client_base.py @@ -0,0 +1,93 @@ +import time + +from ..idl.unitree_api.msg.dds_ import Request_ as Request +from ..idl.unitree_api.msg.dds_ import RequestHeader_ as RequestHeader +from ..idl.unitree_api.msg.dds_ import RequestLease_ as RequestLease +from ..idl.unitree_api.msg.dds_ import RequestIdentity_ as RequestIdentity +from ..idl.unitree_api.msg.dds_ import RequestPolicy_ as RequestPolicy + +from ..utils.future import FutureResult + +from .client_stub import ClientStub +from .internal import * + + +""" +" class ClientBase +""" +class ClientBase: + def __init__(self, serviceName: str): + self.__timeout = 1.0 + self.__stub = ClientStub(serviceName) + self.__stub.Init() + + def SetTimeout(self, timeout: float): + self.__timeout = timeout + + def _CallBase(self, apiId: int, parameter: str, proirity: int = 0, leaseId: int = 0): + # print("[CallBase] call apiId:", apiId, ", proirity:", proirity, ", leaseId:", leaseId) + header = self.__SetHeader(apiId, leaseId, proirity, False) + request = Request(header, parameter, []) + + future = self.__stub.SendRequest(request, self.__timeout) + if future is None: + return RPC_ERR_CLIENT_SEND, None + + result = future.GetResult(self.__timeout) + + if result.code != FutureResult.FUTURE_SUCC: + self.__stub.RemoveFuture(request.header.identity.id) + code = RPC_ERR_CLIENT_API_TIMEOUT if result.code == FutureResult.FUTUTE_ERR_TIMEOUT else RPC_ERR_UNKNOWN + return code, None + + response = result.value + + if response.header.identity.api_id != apiId: + return RPC_ERR_CLIENT_API_NOT_MATCH, None + else: + return response.header.status.code, response.data + + def _CallNoReplyBase(self, apiId: int, parameter: str, proirity: int, leaseId: int): + header = self.__SetHeader(apiId, leaseId, proirity, True) + request = Request(header, parameter, []) + + if self.__stub.Send(request, self.__timeout): + return 0 + else: + return RPC_ERR_CLIENT_SEND + + def _CallBinaryBase(self, apiId: int, parameter: list, proirity: int, leaseId: int): + header = self.__SetHeader(apiId, leaseId, proirity, False) + request = Request(header, "", parameter) + + future = self.__stub.SendRequest(request, self.__timeout) + if future is None: + return RPC_ERR_CLIENT_SEND, None + + result = future.GetResult(self.__timeout) + if result.code != FutureResult.FUTURE_SUCC: + self.__stub.RemoveFuture(request.header.identity.id) + code = RPC_ERR_CLIENT_API_TIMEOUT if result.code == FutureResult.FUTUTE_ERR_TIMEOUT else RPC_ERR_UNKNOWN + return code, None + + response = result.value + + if response.header.identity.api_id != apiId: + return RPC_ERR_CLIENT_API_NOT_MATCH, None + else: + return response.header.status.code, response.binary + + def _CallBinaryNoReplyBase(self, apiId: int, parameter: list, proirity: int, leaseId: int): + header = self.__SetHeader(apiId, leaseId, proirity, True) + request = Request(header, "", parameter) + + if self.__stub.Send(request, self.__timeout): + return 0 + else: + return RPC_ERR_CLIENT_SEND + + def __SetHeader(self, apiId: int, leaseId: int, priority: int, noReply: bool): + identity = RequestIdentity(time.monotonic_ns(), apiId) + lease = RequestLease(leaseId) + policy = RequestPolicy(priority, noReply) + return RequestHeader(identity, lease, policy) diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/client_stub.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/client_stub.py new file mode 100644 index 0000000..67c2c8f --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/client_stub.py @@ -0,0 +1,69 @@ +import time + +from enum import Enum +from threading import Thread, Condition + +from ..idl.unitree_api.msg.dds_ import Request_ as Request +from ..idl.unitree_api.msg.dds_ import Response_ as Response + +from ..core.channel import ChannelFactory +from ..core.channel_name import ChannelType, GetClientChannelName +from .request_future import RequestFuture, RequestFutureQueue + + +""" +" class ClientStub +""" +class ClientStub: + def __init__(self, serviceName: str): + self.__serviceName = serviceName + self.__futureQueue = None + + self.__sendChannel = None + self.__recvChannel = None + + def Init(self): + factory = ChannelFactory() + self.__futureQueue = RequestFutureQueue() + + # create channel + self.__sendChannel = factory.CreateSendChannel(GetClientChannelName(self.__serviceName, ChannelType.SEND), Request) + self.__recvChannel = factory.CreateRecvChannel(GetClientChannelName(self.__serviceName, ChannelType.RECV), Response, + self.__ResponseHandler,10) + time.sleep(0.5) + + + def Send(self, request: Request, timeout: float): + if self.__sendChannel.Write(request, timeout): + return True + else: + print("[ClientStub] send error. id:", request.header.identity.id) + return False + + def SendRequest(self, request: Request, timeout: float): + id = request.header.identity.id + + future = RequestFuture() + future.SetRequestId(id) + self.__futureQueue.Set(id, future) + + if self.__sendChannel.Write(request, timeout): + return future + else: + print("[ClientStub] send request error. id:", request.header.identity.id) + self.__futureQueue.Remove(id) + return None + + def RemoveFuture(self, requestId: int): + self.__futureQueue.Remove(requestId) + + def __ResponseHandler(self, response: Response): + id = response.header.identity.id + # apiId = response.header.identity.api_id + # print("[ClientStub] responseHandler recv response id:", id, ", apiId:", apiId) + future = self.__futureQueue.Get(id) + if future is None: + # print("[ClientStub] get future from queue error. id:", id) + pass + elif not future.Ready(response): + print("[ClientStub] set future ready error.") diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/internal.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/internal.py new file mode 100644 index 0000000..875a78b --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/internal.py @@ -0,0 +1,31 @@ +# internal api id max +RPC_INTERNAL_API_ID_MAX = 100 + +# internal api id +RPC_API_ID_INTERNAL_API_VERSION = 1 + +# lease api id +RPC_API_ID_LEASE_APPLY = 101 +RPC_API_ID_LEASE_RENEWAL = 102 + +# lease term default +RPC_LEASE_TERM = 1.0 + +# internal error +RPC_OK = 0 +# client error +RPC_ERR_UNKNOWN = 3001 +RPC_ERR_CLIENT_SEND = 3102 +RPC_ERR_CLIENT_API_NOT_REG = 3103 +RPC_ERR_CLIENT_API_TIMEOUT = 3104 +RPC_ERR_CLIENT_API_NOT_MATCH = 3105 +RPC_ERR_CLIENT_API_DATA = 3106 +RPC_ERR_CLIENT_LEASE_INVALID = 3107 +# server error +RPC_ERR_SERVER_SEND = 3201 +RPC_ERR_SERVER_INTERNAL = 3202 +RPC_ERR_SERVER_API_NOT_IMPL = 3203 +RPC_ERR_SERVER_API_PARAMETER = 3204 +RPC_ERR_SERVER_LEASE_DENIED = 3205 +RPC_ERR_SERVER_LEASE_NOT_EXIST = 3206 +RPC_ERR_SERVER_LEASE_EXIST = 3207 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/lease_client.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/lease_client.py new file mode 100644 index 0000000..37f600f --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/lease_client.py @@ -0,0 +1,113 @@ +import time +import socket +import os +import json + +from threading import Thread, Lock + +from .client_base import ClientBase +from .internal import * + + +""" +" class LeaseContext +""" +class LeaseContext: + def __init__(self): + self.id = 0 + self.term = RPC_LEASE_TERM + + def Update(self, id, term): + self.id = id + self.term = term + + def Reset(self): + self.id = 0 + self.term = RPC_LEASE_TERM + + def Valid(self): + return self.id != 0 + + +""" +" class LeaseClient +""" +class LeaseClient(ClientBase): + def __init__(self, name: str): + self.__name = name + "_lease" + self.__contextName = socket.gethostname() + "/" + name + "/" + str(os.getpid()) + self.__context = LeaseContext() + self.__thread = None + self.__lock = Lock() + super().__init__(self.__name) + print("[LeaseClient] lease name:", self.__name, ", context name:", self.__contextName) + + def Init(self): + self.SetTimeout(1.0) + self.__thread = Thread(target=self.__ThreadFunc, name=self.__name, daemon=True) + self.__thread.start() + + def WaitApplied(self): + while True: + with self.__lock: + if self.__context.Valid(): + break + time.sleep(0.1) + + def GetId(self): + with self.__lock: + return self.__context.id + + def Applied(self): + with self.__lock: + return self.__context.Valid() + + def __Apply(self): + parameter = {} + parameter["name"] = self.__contextName + p = json.dumps(parameter) + + c, d = self._CallBase(RPC_API_ID_LEASE_APPLY, p) + if c != 0: + print("[LeaseClient] apply lease error. code:", c) + return + + data = json.loads(d) + + id = data["id"] + term = data["term"] + + print("[LeaseClient] lease applied id:", id, ", term:", term) + + with self.__lock: + self.__context.Update(id, float(term/1000000)) + + def __Renewal(self): + parameter = {} + p = json.dumps(parameter) + + c, d = self._CallBase(RPC_API_ID_LEASE_RENEWAL, p, 0, self.__context.id) + if c != 0: + print("[LeaseClient] renewal lease error. code:", c) + if c == RPC_ERR_SERVER_LEASE_NOT_EXIST: + with self.__lock: + self.__context.Reset() + + def __GetWaitSec(self): + waitsec = 0.0 + if self.__context.Valid(): + waitsec = self.__context.term + + if waitsec <= 0: + waitsec = RPC_LEASE_TERM + + return waitsec * 0.3 + + def __ThreadFunc(self): + while True: + if self.__context.Valid(): + self.__Renewal() + else: + self.__Apply() + # sleep waitsec + time.sleep(self.__GetWaitSec()) diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/lease_server.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/lease_server.py new file mode 100644 index 0000000..d5b27d1 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/lease_server.py @@ -0,0 +1,151 @@ +import time +import json + +from threading import Lock + +from ..idl.unitree_api.msg.dds_ import Request_ as Request +from ..idl.unitree_api.msg.dds_ import ResponseHeader_ as ResponseHeader +from ..idl.unitree_api.msg.dds_ import ResponseStatus_ as ResponseStatus +from ..idl.unitree_api.msg.dds_ import Response_ as Response + +from .internal import * +from .server_base import ServerBase + + +""" +" class LeaseCache +""" +class LeaseCache: + def __init__(self): + self.lastModified = 0 + self.id = 0 + self.name = None + + def Set(self, id: int, name: str, lastModified: int) : + self.id = id + self.name = name + self.lastModified = lastModified + + def Renewal(self, lastModified: int): + self.lastModified = lastModified + + def Clear(self): + self.id = 0 + self.lastModified = 0 + self.name = None + + +""" +" class LeaseServer +""" +class LeaseServer(ServerBase): + def __init__(self, name: str, term: float): + self.__term = int(term * 1000000) + self.__lock = Lock() + self.__cache = LeaseCache() + super().__init__(name + "_lease") + + def Init(self): + pass + + def Start(self, enablePrioQueue: bool = False): + super()._SetServerRequestHandler(self.__ServerRequestHandler) + super()._Start(enablePrioQueue) + + def CheckRequestLeaseDenied(self, leaseId: int): + with self.__lock: + if self.__cache.id == 0: + return self.__cache.id != leaseId + + now = self.__Now() + if now > self.__cache.lastModified + self.__term: + self.__cache.Clear() + return False + else: + return self.__cache.id != leaseId + + def __Apply(self, parameter: str): + name = "" + data = "" + + try: + p = json.loads(parameter) + name = p.get("name") + + except: + print("[LeaseServer] apply json loads error. parameter:", parameter) + return RPC_ERR_SERVER_API_PARAMETER, data + + if not name: + name = "anonymous" + + id = 0 + lastModified = 0 + setted = False + + now = self.__Now() + + with self.__lock: + id = self.__cache.id + lastModified = self.__cache.lastModified + + if id == 0 or now > lastModified + self.__term: + if id != 0: + print("[LeaseServer] id expired:", id, ", name:", self.__cache.name) + + id = self.__GenerateId() + self.__cache.Set(id, name, now) + setted = True + + print("[LeaseServer] id stored:", id, ", name:", name) + + if setted: + d = {} + d["id"] = id + d["term"] = self.__term + data = json.dumps(d) + return 0, data + else: + return RPC_ERR_SERVER_LEASE_EXIST, data + + + def __Renewal(self, id: int): + now = self.__Now() + + with self.__lock: + if self.__cache.id != id: + return RPC_ERR_SERVER_LEASE_NOT_EXIST + + if now > self.__cache.lastModified + self.__term: + self.__cache.Clear() + return RPC_ERR_SERVER_LEASE_NOT_EXIST + else: + self.__cache.Renewal(now) + return 0 + + def __ServerRequestHandler(self, request: Request): + identity = request.header.identity + parameter = request.parameter + apiId = identity.api_id + code = RPC_ERR_SERVER_API_NOT_IMPL + data = "" + + if apiId == RPC_API_ID_LEASE_APPLY: + code, data = self.__Apply(parameter) + elif apiId == RPC_API_ID_LEASE_RENEWAL: + code = self.__Renewal(request.header.lease.id) + else: + print("[LeaseServer] api is not implemented. apiId", apiId) + + if request.header.policy.noreply: + return + + status = ResponseStatus(code) + response = Response(ResponseHeader(identity, status), data, []) + self._SendResponse(response) + + def __GenerateId(self): + return self.__Now() + + def __Now(self): + return int(time.time_ns()/1000) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/request_future.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/request_future.py new file mode 100644 index 0000000..037ab10 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/request_future.py @@ -0,0 +1,46 @@ +from threading import Condition, Lock +from enum import Enum + +from ..idl.unitree_api.msg.dds_ import Response_ as Response +from ..utils.future import Future, FutureResult + + +""" +" class RequestFuture +""" +class RequestFuture(Future): + def __init__(self): + self.__requestId = None + super().__init__() + + def SetRequestId(self, requestId: int): + self.__requestId = requestId + + def GetRequestId(self): + return self.__requestId + + +class RequestFutureQueue: + def __init__(self): + self.__data = {} + self.__lock = Lock() + + def Set(self, requestId: int, future: RequestFuture): + if future is None: + return False + with self.__lock: + self.__data[requestId] = future + return True + + def Get(self, requestId: int): + future = None + with self.__lock: + future = self.__data.get(requestId) + if future is not None: + self.__data.pop(requestId) + return future + + def Remove(self, requestId: int): + with self.__lock: + if id in self.__data: + self.__data.pop(requestId) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/server.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/server.py new file mode 100644 index 0000000..4e389a8 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/server.py @@ -0,0 +1,122 @@ +import time + +from typing import Callable, Any + +from ..idl.unitree_api.msg.dds_ import Request_ as Request +from ..idl.unitree_api.msg.dds_ import ResponseStatus_ as ResponseStatus +from ..idl.unitree_api.msg.dds_ import ResponseHeader_ as ResponseHeader +from ..idl.unitree_api.msg.dds_ import Response_ as Response + +from .server_base import ServerBase +from .lease_server import LeaseServer +from .internal import * + +""" +" class Server +""" +class Server(ServerBase): + def __init__(self, name: str): + self.__apiVersion = "" + self.__apiHandlerMapping = {} + self.__apiBinaryHandlerMapping = {} + self.__apiBinarySet = {} + self.__enableLease = False + self.__leaseServer = None + super().__init__(name) + + def Init(self): + pass + + def StartLease(self, term: float = 1.0): + self.__enableLease = True + self.__leaseServer = LeaseServer(self.GetName(), term) + self.__leaseServer.Init() + self.__leaseServer.Start(False) + + def Start(self, enablePrioQueue: bool = False): + super()._SetServerRequestHandler(self.__ServerRequestHandler) + super()._Start(enablePrioQueue) + + def GetApiVersion(self): + return self.__apiVersion + + def _SetApiVersion(self, apiVersion: str): + self.__apiVersion = apiVersion + print("[Server] set api version:", self.__apiVersion) + + def _RegistHandler(self, apiId: int, handler: Callable, checkLease: bool): + self.__apiHandlerMapping[apiId] = (handler, checkLease) + + def _RegistBinaryHandler(self, apiId: int, handler: Callable, checkLease: bool): + self.__apiBinaryHandlerMapping[apiId] = (handler, checkLease) + self.__apiBinarySet.add(apiId) + + def __GetHandler(self, apiId: int): + if apiId in self.__apiHandlerMapping: + return self.__apiHandlerMapping.get(apiId) + else: + return None, False + + def __GetBinaryHandler(self, apiId: int): + if apiId in self.__apiBinaryHandlerMapping: + return self.__apiBinaryHandlerMapping.get(apiId) + else: + return None, False + + def __IsBinary(self, apiId): + return apiId in self.__apiBinarySet + + def __CheckLeaseDenied(self, leaseId: int): + if (self.__enableLease): + return self.__leaseServer.CheckRequestLeaseDenied(leaseId) + else: + return False + + def __ServerRequestHandler(self, request: Request): + parameter = request.parameter + parameterBinary = request.binary + + identity = request.header.identity + leaseId = request.header.lease.id + apiId = identity.api_id + + code = 0 + data = "" + dataBinary = [] + + if apiId == RPC_API_ID_INTERNAL_API_VERSION: + data = self.__apiVersion + else: + requestHandler = None + binaryRequestHandler = None + checkLease = False + + if self.__IsBinary(apiId): + binaryRequestHandler, checkLease = self.__GetBinaryHandler(apiId) + else: + requestHandler, checkLease = self.__GetHandler(apiId) + + if requestHandler is None and binaryRequestHandler is None: + code = RPC_ERR_SERVER_API_NOT_IMPL + elif checkLease and self.__CheckLeaseDenied(leaseId): + code = RPC_ERR_SERVER_LEASE_DENIED + else: + try: + if binaryRequestHandler is None: + code, data = requestHandler(parameter) + if code != 0: + data = "" + else: + code, dataBinary = binaryRequestHandler(parameterBinary) + if code != 0: + dataBinary = [] + except: + code = RPC_ERR_SERVER_INTERNAL + + if request.header.policy.noreply: + return + + status = ResponseStatus(code) + response = Response(ResponseHeader(identity, status), data, dataBinary) + + self._SendResponse(response) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/server_base.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/server_base.py new file mode 100644 index 0000000..f010561 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/server_base.py @@ -0,0 +1,32 @@ +import time + +from typing import Callable, Any + +from ..idl.unitree_api.msg.dds_ import Request_ as Request +from ..idl.unitree_api.msg.dds_ import Response_ as Response + +from .server_stub import ServerStub + + +""" +" class ServerBase +""" +class ServerBase: + def __init__(self, name: str): + self.__name = name + self.__serverRequestHandler = None + self.__serverStub = ServerStub(self.__name) + + def GetName(self): + return self.__name + + def _Start(self, enablePrioQueue: bool = False): + self.__serverStub.Init(self.__serverRequestHandler, enablePrioQueue) + print("[ServerBase] server started. name:", self.__name, ", enable proirity queue:", enablePrioQueue) + + def _SetServerRequestHandler(self, serverRequestHandler: Callable): + self.__serverRequestHandler = serverRequestHandler + + def _SendResponse(self, response: Response): + if not self.__serverStub.Send(response, 1.0): + print("[ServerBase] send response error.") diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/server_stub.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/server_stub.py new file mode 100644 index 0000000..e67be1f --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/rpc/server_stub.py @@ -0,0 +1,78 @@ +import time + +from enum import Enum +from threading import Thread, Condition +from typing import Callable, Any + +from ..utils.bqueue import BQueue +from ..idl.unitree_api.msg.dds_ import Request_ as Request +from ..idl.unitree_api.msg.dds_ import Response_ as Response + +from ..core.channel import ChannelFactory +from ..core.channel_name import ChannelType, GetServerChannelName + + +""" +" class ServerStub +""" +class ServerStub: + def __init__(self, serviceName: str): + self.__serviceName = serviceName + self.__serverRquestHandler = None + self.__sendChannel = None + self.__recvChannel = None + self.__enablePriority = None + self.__queue = None + self.__prioQueue = None + self.__queueThread = None + self.__prioQueueThread = None + + def Init(self, serverRequestHander: Callable, enablePriority: bool = False): + self.__serverRquestHandler = serverRequestHander + self.__enablePriority = enablePriority + + factory = ChannelFactory() + + # create channel + self.__sendChannel = factory.CreateSendChannel(GetServerChannelName(self.__serviceName, ChannelType.SEND), Response) + self.__recvChannel = factory.CreateRecvChannel(GetServerChannelName(self.__serviceName, ChannelType.RECV), Request, self.__Enqueue, 10) + + # start priority request thread + self.__queue = BQueue(10) + self.__queueThread = Thread(target=self.__QueueThreadFunc, name="server_queue", daemon=True) + self.__queueThread.start() + + if enablePriority: + self.__prioQueue = BQueue(5) + self.__prioQueueThread = Thread(target=self.__PrioQueueThreadFunc, name="server_prio_queue", daemon=True) + self.__prioQueueThread.start() + + # wait thread started + time.sleep(0.5) + + def Send(self, response: Response, timeout: float): + if self.__sendChannel.Write(response, timeout): + return True + else: + print("[ServerStub] send error. id:", response.header.identity.id) + return False + + def __Enqueue(self, request: Request): + if self.__enablePriority and request.header.policy.priority > 0: + self.__prioQueue.Put(request, True) + else: + self.__queue.Put(request, True) + + def __QueueThreadFunc(self): + while True: + request = self.__queue.Get() + if request is None: + continue + self.__serverRquestHandler(request) + + def __PrioQueueThreadFunc(self): + while True: + request = self.__prioQueue.Get() + if request is None: + continue + self.__serverRquestHandler(request) diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/obstacles_avoid_client_example.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/obstacles_avoid_client_example.py new file mode 100644 index 0000000..7936fe7 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/obstacles_avoid_client_example.py @@ -0,0 +1,91 @@ +import time +import os + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.go2.obstacles_avoid.obstacles_avoid_client import ObstaclesAvoidClient + +if __name__ == "__main__": + ChannelFactoryInitialize(0, "enp3s0") + + client = ObstaclesAvoidClient() + client.SetTimeout(3.0) + client.Init() + + while True: + print("##################GetServerApiVersion###################") + code, serverAPiVersion = client.GetServerApiVersion() + if code != 0: + print("get server api error. code:", code) + else: + print("get server api version:", serverAPiVersion) + + if serverAPiVersion != client.GetApiVersion(): + print("api version not equal.") + + time.sleep(3) + + print("##################SwitchGet###################") + code, enable = client.SwitchGet() + if code != 0: + print("switch get error. code:", code) + else: + print("switch get success. enable:", enable) + + time.sleep(3) + + print("##################SwitchSet (on)###################") + code = client.SwitchSet(True) + if code != 0: + print("switch set error. code:", code) + else: + print("switch set success.") + + time.sleep(3) + + print("##################SwitchGet###################") + code, enable1 = client.SwitchGet() + if code != 0: + print("switch get error. code:", code) + else: + print("switch get success. enable:", enable1) + + time.sleep(3) + + print("##################SwitchSet (off)###################") + code = client.SwitchSet(False) + if code != 0: + print("switch set error. code:", code) + else: + print("switch set success.") + + time.sleep(3) + + print("##################SwitchGet###################") + code, enable1 = client.SwitchGet() + if code != 0: + print("switch get error. code:", code) + else: + print("switch get success. enable:", enable1) + + time.sleep(3) + + + print("##################SwitchSet (enable)###################") + + code = client.SwitchSet(enable) + if code != 0: + print("switch set error. code:", code) + else: + print("switch set success. enable:", enable) + + time.sleep(3) + + print("##################SwitchGet###################") + code, enable = client.SwitchGet() + if code != 0: + print("switch get error. code:", code) + else: + print("switch get success. enable:", enable) + + time.sleep(3) + \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/robot_service_client_example.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/robot_service_client_example.py new file mode 100644 index 0000000..01467c0 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/robot_service_client_example.py @@ -0,0 +1,50 @@ +import time +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.go2.robot_state.robot_state_client import RobotStateClient + +if __name__ == "__main__": + ChannelFactoryInitialize(0, "enx000ec6768747") + rsc = RobotStateClient() + rsc.SetTimeout(3.0) + rsc.Init() + + while True: + print("##################GetServerApiVersion###################") + code, serverAPiVersion = rsc.GetServerApiVersion() + + if code != 0: + print("get server api error. code:", code) + else: + print("get server api version:", serverAPiVersion) + + time.sleep(3) + + print("##################ServiceList###################") + code, lst = rsc.ServiceList() + + if code != 0: + print("list sevrice error. code:", code) + else: + print("list service success. len:", len(lst)) + for s in lst: + print("name:", s.name, ", protect:", s.protect, ", status:", s.status) + + time.sleep(3) + + print("##################ServiceSwitch###################") + code = rsc.ServiceSwitch("sport_mode", False) + if code != 0: + print("service stop sport_mode error. code:", code) + else: + print("service stop sport_mode success. code:", code) + + time.sleep(1) + + code = rsc.ServiceSwitch("sport_mode", True) + if code != 0: + print("service start sport_mode error. code:", code) + else: + print("service start sport_mode success. code:", code) + + time.sleep(3) + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/sport_client_example.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/sport_client_example.py new file mode 100644 index 0000000..22a8de0 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/sport_client_example.py @@ -0,0 +1,109 @@ +import time +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.go2.sport.sport_client import SportClient, PathPoint, SPORT_PATH_POINT_SIZE + +if __name__ == "__main__": + ChannelFactoryInitialize(0, "enp2s0") + client = SportClient() + client.SetTimeout(10.0) + client.Init() + + print("##################GetServerApiVersion###################") + code, serverAPiVersion = client.GetServerApiVersion() + if code != 0: + print("get server api error. code:", code) + else: + print("get server api version:", serverAPiVersion) + + if serverAPiVersion != client.GetApiVersion(): + print("api version not equal.") + + time.sleep(3) + + print("##################Trigger###################") + code = client.Trigger() + if code != 0: + print("sport trigger error. code:", code) + else: + print("sport trigger success.") + + time.sleep(3) + + while True: + print("##################RecoveryStand###################") + code = client.RecoveryStand() + + if code != 0: + print("sport recovery stand error. code:", code) + else: + print("sport recovery stand success.") + + time.sleep(3) + + print("##################StandDown###################") + code = client.StandDown() + if code != 0: + print("sport stand down error. code:", code) + else: + print("sport stand down success.") + + time.sleep(3) + + print("##################Damp###################") + code = client.Damp() + if code != 0: + print("sport damp error. code:", code) + else: + print("sport damp down success.") + + time.sleep(3) + + print("##################RecoveryStand###################") + code = client.RecoveryStand() + + if code != 0: + print("sport recovery stand error. code:", code) + else: + print("sport recovery stand success.") + + time.sleep(3) + + print("##################Sit###################") + code = client.Sit() + if code != 0: + print("sport stand down error. code:", code) + else: + print("sport stand down success.") + + time.sleep(3) + + print("##################RiseSit###################") + code = client.RiseSit() + + if code != 0: + print("sport rise sit error. code:", code) + else: + print("sport rise sit success.") + + time.sleep(3) + + print("##################SetBodyHight###################") + code = client.BodyHeight(0.18) + + if code != 0: + print("sport body hight error. code:", code) + else: + print("sport body hight success.") + + time.sleep(3) + + print("##################GetState#################") + keys = ["state", "bodyHeight", "footRaiseHeight", "speedLevel", "gait"] + code, data = client.GetState(keys) + + if code != 0: + print("sport get state error. code:", code) + else: + print("sport get state success. data:", data) + + time.sleep(3) diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/video_client_example.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/video_client_example.py new file mode 100644 index 0000000..7eec949 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/video_client_example.py @@ -0,0 +1,26 @@ +import time +import os + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.go2.video.video_client import VideoClient + +if __name__ == "__main__": + ChannelFactoryInitialize(0, "enp2s0") + + client = VideoClient() + client.SetTimeout(3.0) + client.Init() + + print("##################GetImageSample###################") + code, data = client.GetImageSample() + + if code != 0: + print("get image sample error. code:", code) + else: + imageName = os.path.dirname(__file__) + time.strftime('/%Y%m%d%H%M%S.jpg',time.localtime()) + print("ImageName:", imageName) + + with open(imageName, "+wb") as f: + f.write(bytes(data)) + + time.sleep(1) diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/vui_client_example.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/vui_client_example.py new file mode 100644 index 0000000..6df7c09 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/client/vui_client_example.py @@ -0,0 +1,74 @@ +import time +import os + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.go2.vui.vui_client import VuiClient + +if __name__ == "__main__": + ChannelFactoryInitialize(0, "enp2s0") + + client = VuiClient() + client.SetTimeout(3.0) + client.Init() + + for i in range(1, 11): + print("#################GetBrightness####################") + code, level = client.GetBrightness() + + if code != 0: + print("get brightness error. code:", code) + else: + print("get brightness success. level:", level) + + time.sleep(1) + + print("#################SetBrightness####################") + + code = client.SetBrightness(i) + + if code != 0: + print("set brightness error. code:", code) + else: + print("set brightness success. level:", i) + + time.sleep(1) + + print("#################SetBrightness 0####################") + + code = client.SetBrightness(0) + + if code != 0: + print("set brightness error. code:", code) + else: + print("set brightness 0 success.") + + for i in range(1, 11): + print("#################GetVolume####################") + code, level = client.GetVolume() + + if code != 0: + print("get volume error. code:", code) + else: + print("get volume success. level:", level) + + time.sleep(1) + + print("#################SetVolume####################") + + code = client.SetVolume(i) + + if code != 0: + print("set volume error. code:", code) + else: + print("set volume success. level:", i) + + time.sleep(1) + + print("#################SetVolume 0####################") + + code = client.SetVolume(0) + + if code != 0: + print("set volume error. code:", code) + else: + print("set volume 0 success.") diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/crc/test_crc.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/crc/test_crc.py new file mode 100644 index 0000000..ceb1b4b --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/crc/test_crc.py @@ -0,0 +1,27 @@ +from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowCmd_, unitree_go_msg_dds__LowState_ +from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowCmd_, unitree_hg_msg_dds__LowState_ +from unitree_sdk2py.utils.crc import CRC + +crc = CRC() + +""" +" LowCmd/LowState CRC +""" +cmd = unitree_go_msg_dds__LowCmd_() +cmd.crc = crc.Crc(cmd) + +state = unitree_go_msg_dds__LowState_() +state.crc = crc.Crc(state) + +print("CRC[LowCmd, LowState]: {}, {}".format(cmd.crc, state.crc)) + +""" +" LowCmd/LowState for HG CRC. () +""" +cmd = unitree_hg_msg_dds__LowCmd_() +cmd.crc = crc.Crc(cmd) + +state = unitree_hg_msg_dds__LowState_() +state.crc = crc.Crc(state) + +print("CRC[HGLowCmd, HGLowState]: {}, {}".format(cmd.crc, state.crc)) diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/helloworld/helloworld.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/helloworld/helloworld.py new file mode 100644 index 0000000..7e93803 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/helloworld/helloworld.py @@ -0,0 +1,6 @@ +from dataclasses import dataclass +from cyclonedds.idl import IdlStruct + +@dataclass +class HelloWorld(IdlStruct, typename="HelloWorld"): + data: str \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/helloworld/publisher.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/helloworld/publisher.py new file mode 100644 index 0000000..36e8fb8 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/helloworld/publisher.py @@ -0,0 +1,22 @@ +import time + +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize +from helloworld import HelloWorld + +ChannelFactoryInitialize() + +pub = ChannelPublisher("topic", HelloWorld) +pub.Init() + +for i in range(30): + msg = HelloWorld("Hello world. time:" + str(time.time())) + # msg.data = "Hello world. time:" + str(time.time()) + + if pub.Write(msg, 0.5): + print("publish success. msg:", msg) + else: + print("publish error.") + + time.sleep(1) + +pub.Close() \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/helloworld/subscriber.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/helloworld/subscriber.py new file mode 100644 index 0000000..38e4763 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/helloworld/subscriber.py @@ -0,0 +1,19 @@ +import time + +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from helloworld import HelloWorld + +ChannelFactoryInitialize() + +sub = ChannelSubscriber("topic", HelloWorld) +sub.Init() + +while True: + msg = sub.Read() + + if msg is None: + print("subscribe error.") + else: + print("subscribe success. msg:", msg) + +pub.Close() \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/lowlevel_control.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/lowlevel_control.py new file mode 100644 index 0000000..8e94fe1 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/lowlevel_control.py @@ -0,0 +1,51 @@ +import time + +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowCmd_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowCmd_ +from unitree_sdk2py.utils.crc import CRC +from unitree_sdk2py.utils.thread import Thread +import unitree_go2_const as go2 + +crc = CRC() +lowCmdThreadPtr=Thread() + +if __name__ == '__main__': + + ChannelFactoryInitialize(1, "enp2s0") + # Create a publisher to publish the data defined in UserData class + pub = ChannelPublisher("lowcmd", LowCmd_) + pub.Init() + + while True: + # Create a Userdata message + cmd = unitree_go_msg_dds__LowCmd_() + + # Toque controle, set RL_2 toque + cmd.motor_cmd[go2.LegID["RL_2"]].mode = 0x01 + cmd.motor_cmd[go2.LegID["RL_2"]].q = go2.PosStopF # Set to stop position(rad) + cmd.motor_cmd[go2.LegID["RL_2"]].kp = 0 + cmd.motor_cmd[go2.LegID["RL_2"]].dq = go2.VelStopF # Set to stop angular velocity(rad/s) + cmd.motor_cmd[go2.LegID["RL_2"]].kd = 0 + cmd.motor_cmd[go2.LegID["RL_2"]].tau = 1 # target toque is set to 1N.m + + # Poinstion(rad) control, set RL_0 rad + cmd.motor_cmd[go2.LegID["RL_0"]].mode = 0x01 + cmd.motor_cmd[go2.LegID["RL_0"]].q = 0 # Taregt angular(rad) + cmd.motor_cmd[go2.LegID["RL_0"]].kp = 10 # Poinstion(rad) control kp gain + cmd.motor_cmd[go2.LegID["RL_0"]].dq = 0 # Taregt angular velocity(rad/ss) + cmd.motor_cmd[go2.LegID["RL_0"]].kd = 1 # Poinstion(rad) control kd gain + cmd.motor_cmd[go2.LegID["RL_0"]].tau = 0 # Feedforward toque 1N.m + + cmd.crc = crc.Crc(cmd) + + #Publish message + if pub.Write(cmd): + print("Publish success. msg:", cmd.crc) + else: + print("Waitting for subscriber.") + + time.sleep(0.002) + + pub.Close() diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/read_lowstate.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/read_lowstate.py new file mode 100644 index 0000000..d3f20ca --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/read_lowstate.py @@ -0,0 +1,24 @@ +import time +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowState_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_ + +import unitree_go2_const as go2 + + +def LowStateHandler(msg: LowState_): + + # print front right hip motor states + print("FR_0 motor state: ", msg.motor_state[go2.LegID["FR_0"]]) + print("IMU state: ", msg.imu_state) + print("Battery state: voltage: ", msg.power_v, "current: ", msg.power_a) + + +if __name__ == "__main__": + # Modify "enp2s0" to the actual network interface + ChannelFactoryInitialize(0, "enp2s0") + sub = ChannelSubscriber("rt/lowstate", LowState_) + sub.Init(LowStateHandler, 10) + + while True: + time.sleep(10.0) diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/sub_lowstate.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/sub_lowstate.py new file mode 100644 index 0000000..9a94619 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/sub_lowstate.py @@ -0,0 +1,15 @@ +import time +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowState_ +from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_ + +def LowStateHandler(msg: LowState_): + print(msg.motor_state) + + +ChannelFactoryInitialize(0, "enp2s0") +sub = ChannelSubscriber("rt/lowstate", LowState_) +sub.Init(LowStateHandler, 10) + +while True: + time.sleep(10.0) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/unitree_go2_const.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/unitree_go2_const.py new file mode 100644 index 0000000..153a051 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/unitree_go2_const.py @@ -0,0 +1,20 @@ +LegID = { + "FR_0": 0, # Front right hip + "FR_1": 1, # Front right thigh + "FR_2": 2, # Front right calf + "FL_0": 3, + "FL_1": 4, + "FL_2": 5, + "RR_0": 6, + "RR_1": 7, + "RR_2": 8, + "RL_0": 9, + "RL_1": 10, + "RL_2": 11, +} + +HIGHLEVEL = 0xEE +LOWLEVEL = 0xFF +TRIGERLEVEL = 0xF0 +PosStopF = 2.146e9 +VelStopF = 16000.0 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_api.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_api.py new file mode 100644 index 0000000..68778ca --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_api.py @@ -0,0 +1,9 @@ +# service name +TEST_SERVICE_NAME = "test" + +# api version +TEST_API_VERSION = "1.0.0.1" + +# api id +TEST_API_ID_MOVE = 1008 +TEST_API_ID_STOP = 1002 \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_client_example.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_client_example.py new file mode 100644 index 0000000..35c6ad4 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_client_example.py @@ -0,0 +1,62 @@ +import time +import json + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.rpc.client import Client + +from test_api import * + +""" +" class TestClient +""" +class TestClient(Client): + def __init__(self, enableLease: bool = False): + super().__init__("test", enableLease) + + def Init(self): + self._RegistApi(TEST_API_ID_MOVE, 0) + self._RegistApi(TEST_API_ID_STOP, 1) + self._SetApiVerson(TEST_API_VERSION) + + def Move(self, vx: float, vy: float, vyaw: float): + parameter = {} + parameter["vx"] = vx + parameter["vy"] = vy + parameter["vyaw"] = vyaw + p = json.dumps(parameter) + + c, d = self._Call(TEST_API_ID_MOVE, p) + return c + + def Stop(self): + parameter = {} + p = json.dumps(parameter) + + c, d = self._Call(TEST_API_ID_STOP, p) + return c + +if __name__ == "__main__": + # initialize channel factory. + ChannelFactoryInitialize(0) + + # create client + client = TestClient(True) + client.Init() + client.SetTimeout(5.0) + + # get server version + code, serverApiVersion = client.GetServerApiVersion() + print("server api version:", serverApiVersion) + + # wait lease applied + client.WaitLeaseApplied() + + # test api + while True: + code = client.Move(0.2, 0, 0) + print("client move ret:", code) + time.sleep(1.0) + + code = client.Stop() + print("client stop ret:", code) + time.sleep(1.0) diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_server_example.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_server_example.py new file mode 100644 index 0000000..a929e01 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_server_example.py @@ -0,0 +1,45 @@ +import time +import json + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.rpc.server import Server + +from test_api import * + + +""" +" class TestServer +""" +class TestServer(Server): + def __init__(self): + super().__init__("test") + + def Init(self): + self._RegistHandler(TEST_API_ID_MOVE, self.Move, 1) + self._RegistHandler(TEST_API_ID_STOP, self.Stop, 0) + self._SetApiVersion(TEST_API_VERSION) + + def Move(self, parameter: str): + p = json.loads(parameter) + x = p["vx"] + y = p["vy"] + yaw = p["vyaw"] + print("Move Called. vx:", x, ", vy:", y, ", vyaw:", yaw) + return 0, "" + + def Stop(self, parameter: str): + print("Stop Called.") + return 0, "" + +if __name__ == "__main__": + # initialize channel factory. + ChannelFactoryInitialize(0) + + # create server + server = TestServer() + server.Init() + server.StartLease(1.0) + server.Start(False) + + while True: + time.sleep(10) \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/__init__.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/bqueue.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/bqueue.py new file mode 100644 index 0000000..a612bfb --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/bqueue.py @@ -0,0 +1,58 @@ +from typing import Any +from collections import deque +from threading import Condition + +class BQueue: + def __init__(self, maxLen: int = 10): + self.__curLen = 0 + self.__maxLen = maxLen + self.__queue = deque() + self.__condition = Condition() + + def Put(self, x: Any, replace: bool = False): + noReplaced = True + with self.__condition: + if self.__curLen >= self.__maxLen: + if not replace: + return False + else: + noReplaced = False + self.__queue.popleft() + self.__curLen -= 1 + + self.__queue.append(x) + self.__curLen += 1 + self.__condition.notify() + + return noReplaced + + def Get(self, timeout: float = None): + with self.__condition: + if not self.__queue: + try: + self.__condition.wait(timeout) + except: + return None + + if not self.__queue: + return None + + self.__curLen -= 1 + return self.__queue.popleft() + + def Clear(self): + with self.__condition: + if self.__queue: + self.__queue.clear() + self.__curLen = 0 + + def Size(self): + with self.__condition: + return self.__curLen + + def Interrupt(self, notifyAll: bool = False): + with self.__condition: + if notifyAll: + self.__condition.notify() + else: + self.__condition.notify_all() diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/clib_lookup.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/clib_lookup.py new file mode 100644 index 0000000..ca7073d --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/clib_lookup.py @@ -0,0 +1,17 @@ +import os +import ctypes + +clib = ctypes.CDLL(None, use_errno=True) + +def CLIBCheckError(ret, func, args): + if ret < 0: + code = ctypes.get_errno() + raise OSError(code, os.strerror(code)) + return ret + +def CLIBLookup(name, resType, argTypes): + func = clib[name] + func.restye = resType + func.argtypes = argTypes + func.errcheck = CLIBCheckError + return func diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/crc.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/crc.py new file mode 100644 index 0000000..c250706 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/crc.py @@ -0,0 +1,228 @@ +import struct +import cyclonedds +import cyclonedds.idl as idl + +from .singleton import Singleton +from ..idl.unitree_go.msg.dds_ import LowCmd_ +from ..idl.unitree_go.msg.dds_ import LowState_ + +from ..idl.unitree_hg.msg.dds_ import LowCmd_ as HGLowCmd_ +from ..idl.unitree_hg.msg.dds_ import LowState_ as HGLowState_ +import ctypes +import os +import platform + +class CRC(Singleton): + def __init__(self): + #4 bytes aligned, little-endian format. + #size 812 + self.__packFmtLowCmd = '<4B4IH2x' + 'B3x5f3I' * 20 + '4B' + '55Bx2I' + #size 1180 + self.__packFmtLowState = '<4B4IH2x' + '13fb3x' + 'B3x7fb3x3I' * 20 + '4BiH4b15H' + '8hI41B3xf2b2x2f4h2I' + #size 1004 + self.__packFmtHGLowCmd = '<2B2x' + 'B3x5fI' * 35 + '5I' + #size 2092 + self.__packFmtHGLowState = '<2I2B2xI' + '13fh2x' + 'B3x4f2hf7I' * 35 + '40B5I' + + + script_dir = os.path.dirname(os.path.abspath(__file__)) + self.platform = platform.system() + if self.platform == "Linux": + if platform.machine()=="x86_64": + self.crc_lib = ctypes.CDLL(script_dir + '/lib/crc_amd64.so') + elif platform.machine()=="aarch64": + self.crc_lib = ctypes.CDLL(script_dir + '/lib/crc_aarch64.so') + + self.crc_lib.crc32_core.argtypes = (ctypes.POINTER(ctypes.c_uint32), ctypes.c_uint32) + self.crc_lib.crc32_core.restype = ctypes.c_uint32 + + def Crc(self, msg: idl.IdlStruct): + if msg.__idl_typename__ == 'unitree_go.msg.dds_.LowCmd_': + return self.__Crc32(self.__PackLowCmd(msg)) + elif msg.__idl_typename__ == 'unitree_go.msg.dds_.LowState_': + return self.__Crc32(self.__PackLowState(msg)) + if msg.__idl_typename__ == 'unitree_hg.msg.dds_.LowCmd_': + return self.__Crc32(self.__PackHGLowCmd(msg)) + elif msg.__idl_typename__ == 'unitree_hg.msg.dds_.LowState_': + return self.__Crc32(self.__PackHGLowState(msg)) + else: + raise TypeError('unknown IDL message type to crc') + + def __PackLowCmd(self, cmd: LowCmd_): + origData = [] + origData.extend(cmd.head) + origData.append(cmd.level_flag) + origData.append(cmd.frame_reserve) + origData.extend(cmd.sn) + origData.extend(cmd.version) + origData.append(cmd.bandwidth) + + for i in range(20): + origData.append(cmd.motor_cmd[i].mode) + origData.append(cmd.motor_cmd[i].q) + origData.append(cmd.motor_cmd[i].dq) + origData.append(cmd.motor_cmd[i].tau) + origData.append(cmd.motor_cmd[i].kp) + origData.append(cmd.motor_cmd[i].kd) + origData.extend(cmd.motor_cmd[i].reserve) + + origData.append(cmd.bms_cmd.off) + origData.extend(cmd.bms_cmd.reserve) + + origData.extend(cmd.wireless_remote) + origData.extend(cmd.led) + origData.extend(cmd.fan) + origData.append(cmd.gpio) + origData.append(cmd.reserve) + origData.append(cmd.crc) + + return self.__Trans(struct.pack(self.__packFmtLowCmd, *origData)) + + def __PackLowState(self, state: LowState_): + origData = [] + origData.extend(state.head) + origData.append(state.level_flag) + origData.append(state.frame_reserve) + origData.extend(state.sn) + origData.extend(state.version) + origData.append(state.bandwidth) + + origData.extend(state.imu_state.quaternion) + origData.extend(state.imu_state.gyroscope) + origData.extend(state.imu_state.accelerometer) + origData.extend(state.imu_state.rpy) + origData.append(state.imu_state.temperature) + + for i in range(20): + origData.append(state.motor_state[i].mode) + origData.append(state.motor_state[i].q) + origData.append(state.motor_state[i].dq) + origData.append(state.motor_state[i].ddq) + origData.append(state.motor_state[i].tau_est) + origData.append(state.motor_state[i].q_raw) + origData.append(state.motor_state[i].dq_raw) + origData.append(state.motor_state[i].ddq_raw) + origData.append(state.motor_state[i].temperature) + origData.append(state.motor_state[i].lost) + origData.extend(state.motor_state[i].reserve) + + origData.append(state.bms_state.version_high) + origData.append(state.bms_state.version_low) + origData.append(state.bms_state.status) + origData.append(state.bms_state.soc) + origData.append(state.bms_state.current) + origData.append(state.bms_state.cycle) + origData.extend(state.bms_state.bq_ntc) + origData.extend(state.bms_state.mcu_ntc) + origData.extend(state.bms_state.cell_vol) + + origData.extend(state.foot_force) + origData.extend(state.foot_force_est) + origData.append(state.tick) + origData.extend(state.wireless_remote) + origData.append(state.bit_flag) + origData.append(state.adc_reel) + origData.append(state.temperature_ntc1) + origData.append(state.temperature_ntc2) + origData.append(state.power_v) + origData.append(state.power_a) + origData.extend(state.fan_frequency) + origData.append(state.reserve) + origData.append(state.crc) + + return self.__Trans(struct.pack(self.__packFmtLowState, *origData)) + + def __PackHGLowCmd(self, cmd: HGLowCmd_): + origData = [] + origData.append(cmd.mode_pr) + origData.append(cmd.mode_machine) + + for i in range(35): + origData.append(cmd.motor_cmd[i].mode) + origData.append(cmd.motor_cmd[i].q) + origData.append(cmd.motor_cmd[i].dq) + origData.append(cmd.motor_cmd[i].tau) + origData.append(cmd.motor_cmd[i].kp) + origData.append(cmd.motor_cmd[i].kd) + origData.append(cmd.motor_cmd[i].reserve) + + origData.extend(cmd.reserve) + origData.append(cmd.crc) + + return self.__Trans(struct.pack(self.__packFmtHGLowCmd, *origData)) + + def __PackHGLowState(self, state: HGLowState_): + origData = [] + origData.extend(state.version) + origData.append(state.mode_pr) + origData.append(state.mode_machine) + origData.append(state.tick) + + origData.extend(state.imu_state.quaternion) + origData.extend(state.imu_state.gyroscope) + origData.extend(state.imu_state.accelerometer) + origData.extend(state.imu_state.rpy) + origData.append(state.imu_state.temperature) + + for i in range(35): + origData.append(state.motor_state[i].mode) + origData.append(state.motor_state[i].q) + origData.append(state.motor_state[i].dq) + origData.append(state.motor_state[i].ddq) + origData.append(state.motor_state[i].tau_est) + origData.extend(state.motor_state[i].temperature) + origData.append(state.motor_state[i].vol) + origData.extend(state.motor_state[i].sensor) + origData.append(state.motor_state[i].motorstate) + origData.extend(state.motor_state[i].reserve) + + origData.extend(state.wireless_remote) + origData.extend(state.reserve) + origData.append(state.crc) + + return self.__Trans(struct.pack(self.__packFmtHGLowState, *origData)) + + def __Trans(self, packData): + calcData = [] + calcLen = ((len(packData)>>2)-1) + + for i in range(calcLen): + d = ((packData[i*4+3] << 24) | (packData[i*4+2] << 16) | (packData[i*4+1] << 8) | (packData[i*4])) + calcData.append(d) + + return calcData + + def _crc_py(self, data): + bit = 0 + crc = 0xFFFFFFFF + polynomial = 0x04c11db7 + + for i in range(len(data)): + bit = 1 << 31 + current = data[i] + + for b in range(32): + if crc & 0x80000000: + crc = (crc << 1) & 0xFFFFFFFF + crc ^= polynomial + else: + crc = (crc << 1) & 0xFFFFFFFF + + if current & bit: + crc ^= polynomial + + bit >>= 1 + + return crc + + def _crc_ctypes(self, data): + uint32_array = (ctypes.c_uint32 * len(data))(*data) + length = len(data) + crc=self.crc_lib.crc32_core(uint32_array, length) + return crc + + def __Crc32(self, data): + if self.platform == "Linux": + return self._crc_ctypes(data) + else: + return self._crc_py(data) diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/future.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/future.py new file mode 100644 index 0000000..f539087 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/future.py @@ -0,0 +1,104 @@ +from threading import Condition +from typing import Any +from enum import Enum + +""" +" Enum RequtestFutureState +""" +class FutureState(Enum): + DEFER = 0 + READY = 1 + FAILED = 2 + +""" +" class FutureException +""" +class FutureResult: + FUTURE_SUCC = 0 + FUTUTE_ERR_TIMEOUT = 1 + FUTURE_ERR_FAILED = 2 + FUTURE_ERR_UNKNOWN = 3 + + def __init__(self, code: int, msg: str, value: Any = None): + self.code = code + self.msg = msg + self.value = value + + def __str__(self): + return f"FutureResult(code={str(self.code)}, msg='{self.msg}', value={self.value})" + +class Future: + def __init__(self): + self.__state = FutureState.DEFER + self.__msg = None + self.__condition = Condition() + + def GetResult(self, timeout: float = None): + with self.__condition: + return self.__WaitResult(timeout) + + def Wait(self, timeout: float = None): + with self.__condition: + return self.__Wait(timeout) + + def Ready(self, value): + with self.__condition: + ready = self.__Ready(value) + self.__condition.notify() + return ready + + def Fail(self, reason: str): + with self.__condition: + fail = self.__Fail(reason) + self.__condition.notify() + return fail + + def __Wait(self, timeout: float = None): + if not self.__IsDeferred(): + return True + try: + if timeout is None: + return self.__condition.wait() + else: + return self.__condition.wait(timeout) + except: + print("[Future] future wait error") + return False + + def __WaitResult(self, timeout: float = None): + if not self.__Wait(timeout): + return FutureResult(FutureResult.FUTUTE_ERR_TIMEOUT, "future wait timeout") + + if self.__IsReady(): + return FutureResult(FutureResult.FUTURE_SUCC, "success", self.__value) + elif self.__IsFailed(): + return FutureResult(FutureResult.FUTURE_ERR_FAILED, self.__msg) + else: + return FutureResult(FutureResult.FUTURE_ERR_UNKNOWN, "future state error:" + str(self.__state)) + + def __Ready(self, value): + if not self.__IsDeferred(): + print("[Future] futrue state is not defer") + return False + else: + self.__value = value + self.__state = FutureState.READY + return True + + def __Fail(self, message: str): + if not self.__IsDeferred(): + print("[Future] futrue state is not DEFER") + return False + else: + self.__msg = message + self.__state = FutureState.FAILED + return True + + def __IsDeferred(self): + return self.__state == FutureState.DEFER + + def __IsReady(self): + return self.__state == FutureState.READY + + def __IsFailed(self): + return self.__state == FutureState.FAILED \ No newline at end of file diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/hz_sample.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/hz_sample.py new file mode 100644 index 0000000..962fe3c --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/hz_sample.py @@ -0,0 +1,24 @@ +import time +from threading import Lock +from .thread import RecurrentThread + +class HZSample: + def __init__(self, interval: float = 1.0): + self.__count = 0 + self.__inter = interval if interval > 0.0 else 1.0 + self.__lock = Lock() + self.__thread = RecurrentThread(self.__inter, target=self.TimerFunc) + + def Start(self): + self.__thread.Start() + + def Sample(self): + with self.__lock: + self.__count += 1 + + def TimerFunc(self): + count = 0 + with self.__lock: + count = self.__count + self.__count = 0 + print("HZ: {}".format(count/self.__inter)) diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/joystick.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/joystick.py new file mode 100644 index 0000000..aeda672 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/joystick.py @@ -0,0 +1,251 @@ +import math +import struct +import os +os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide" # Disable pygame welcome message +import pygame +import time + +class Button: + def __init__(self) -> None: + self.pressed = False + self.on_pressed = False + self.on_released = False + self.data = 0 + self.click_count = 0 # 记录连续点击次数 + self.last_pressed_time = 0 # 上次按下时间 + + def __call__(self, data) -> None: + current_time = time.perf_counter() + # print('before',self.data) + + self.pressed = (data != 0) + self.on_pressed = self.pressed and self.data == 0 + self.on_released = not self.pressed and self.data != 0 + + # print('after',self.data) + # 处理连续点击 + if self.on_pressed: + # print('on_pressed') + # print('on_pressed current_time',current_time) + # print('on_pressed last_pressed_time',self.last_pressed_time) + # print('on_pressed diff',current_time-self.last_pressed_time) + + if current_time - self.last_pressed_time <= 0.3: # 0.1 秒以内的连续点击 + self.click_count += 1 + # print(self.click_count) + else: + self.click_count = 0 # 超过时间间隔,重置计数器 + self.last_pressed_time = current_time + self.data = data + + def reset_click_count(self): + """手动重置连续点击计数器""" + self.click_count = 0 + +class Axis: + def __init__(self) -> None: + self.data = 0.0 + self.pressed = False + self.on_pressed = False + self.on_released = False + + self.smooth = 0.03 + self.deadzone = 0.01 + self.threshold = 0.5 + + def __call__(self, data) -> None: + data_deadzone = 0.0 if math.fabs(data) < self.deadzone else data + new_data = self.data * (1 - self.smooth) + data_deadzone * self.smooth + self.pressed = math.fabs(new_data) > self.threshold + self.on_pressed = self.pressed and math.fabs(self.data) < self.threshold + self.on_released = not self.pressed and math.fabs(self.data) > self.threshold + self.data = new_data + + +class Joystick: + def __init__(self) -> None: + # Buttons + self.back = Button() + self.start = Button() + # self.LS = Button() + # self.RS = Button() + self.LB = Button() + self.RB = Button() + self.LT = Button() + self.RT = Button() + self.A = Button() + self.B = Button() + self.X = Button() + self.Y = Button() + self.up = Button() + self.down = Button() + self.left = Button() + self.right = Button() + self.F1 = Button() + self.F2 = Button() + + # Axes + # self.LT = Axis() + # self.RT = Axis() + self.lx = Axis() + self.ly = Axis() + self.rx = Axis() + self.ry = Axis() + + self.last_active_time = time.perf_counter() # 最后一次活动时间 + self.inactive_timeout = 0.5 # 超时时间(单位:秒) + def update(self): + """ + Update the current handle key based on the original data + Used to update flag bits such as on_pressed + + Examples: + >>> new_A_data = 1 + >>> self.A( new_A_data ) + """ + pass + + def extract(self, wireless_remote): + """ + Extract data from unitree_joystick + wireless_remote: uint8_t[40] + """ + # Buttons + button1 = [int(data) for data in f'{wireless_remote[2]:08b}'] + button2 = [int(data) for data in f'{wireless_remote[3]:08b}'] + self.LT(button1[2]) + self.RT(button1[3]) + self.back(button1[4]) + self.start(button1[5]) + self.LB(button1[6]) + self.RB(button1[7]) + self.left(button2[0]) + self.down(button2[1]) + self.right(button2[2]) + self.up(button2[3]) + self.Y(button2[4]) + self.X(button2[5]) + self.B(button2[6]) + self.A(button2[7]) + # Axes + self.lx( struct.unpack('f', bytes(wireless_remote[4:8]))[0] ) + self.rx( struct.unpack('f', bytes(wireless_remote[8:12]))[0] ) + self.ry( struct.unpack('f', bytes(wireless_remote[12:16]))[0] ) + self.ly( struct.unpack('f', bytes(wireless_remote[20:24]))[0] ) + + + # 检查是否有按键按下 + if any([ + self.LT.pressed, self.RT.pressed, self.back.pressed, self.start.pressed, + self.LB.pressed, self.RB.pressed, self.left.pressed, self.down.pressed, + self.right.pressed, self.up.pressed, self.Y.pressed, self.X.pressed, + self.B.pressed, self.A.pressed + ]): + self.last_active_time = time.perf_counter() # 更新最后一次活动时间 + elif time.perf_counter() - self.last_active_time > self.inactive_timeout: + # 超过设定的超时时间未按下任何键,重置所有按键的点击计数 + self.reset_all_click_counts() + self.last_active_time = time.perf_counter() # 重置最后活动时间 + + def reset_all_click_counts(self): + """重置所有按键的连续点击计数器""" + for button in [ + self.LT, self.RT, self.back, self.start, self.LB, self.RB, + self.left, self.down, self.right, self.up, self.Y, self.X, self.B, self.A + ]: + button.reset_click_count() + + def combine(self): + """ + Merge data from Joystick to wireless_remote + """ + # prepare an empty list + wireless_remote = [0 for _ in range(40)] + + # Buttons + wireless_remote[2] = int(''.join([f'{key}' for key in [ + 0, 0, round(self.LT.data), round(self.RT.data), + self.back.data, self.start.data, self.LB.data, self.RB.data, + ]]), 2) + wireless_remote[3] = int(''.join([f'{key}' for key in [ + self.left.data, self.down.data, self.right.data, + self.up.data, self.Y.data, self.X.data, self.B.data, self.A.data, + ]]), 2) + + # Axes + sticks = [self.lx.data, self.rx.data, self.ry.data, self.ly.data] + packs = list(map(lambda x: struct.pack('f', x), sticks)) + wireless_remote[4:8] = packs[0] + wireless_remote[8:12] = packs[1] + wireless_remote[12:16] = packs[2] + wireless_remote[20:24] = packs[3] + return wireless_remote + +class PyGameJoystick(Joystick): + def __init__(self) -> None: + super().__init__() + + pygame.init() + pygame.joystick.init() + if pygame.joystick.get_count() <= 0: + raise Exception("No joystick found!") + + self._joystick = pygame.joystick.Joystick(0) + self._joystick.init() + + def print(self): + print("\naxes: ") + for i in range(self._joystick.get_numaxes()): + print(self._joystick.get_axis(i), end=" ") + print("\nbuttons: ") + for i in range(self._joystick.get_numbuttons()): + print(self._joystick.get_button(i), end=" ") + print("\nhats: ") + for i in range(self._joystick.get_numhats()): + print(self._joystick.get_hat(i), end=" ") + print("\nballs: ") + for i in range(self._joystick.get_numballs()): + print(self._joystick.get_ball(i), end=" ") + print("\n") + +class LogicJoystick(PyGameJoystick): + """ Logic F710 """ + def __init__(self) -> None: + super().__init__() + + def update(self): + pygame.event.pump() + + self.back(self._joystick.get_button(6)) + self.start(self._joystick.get_button(7)) + self.LS(self._joystick.get_button(9)) + self.RS(self._joystick.get_button(10)) + self.LB(self._joystick.get_button(4)) + self.RB(self._joystick.get_button(5)) + self.A(self._joystick.get_button(0)) + self.B(self._joystick.get_button(1)) + self.X(self._joystick.get_button(2)) + self.Y(self._joystick.get_button(3)) + + self.LT((self._joystick.get_axis(2) + 1)/2) + self.RT((self._joystick.get_axis(5) + 1)/2) + self.rx(self._joystick.get_axis(3)) + self.ry(-self._joystick.get_axis(4)) + + + # Logitech controller has 2 modes + # mode 1: light down + self.up(1 if self._joystick.get_hat(0)[1] > 0.5 else 0) + self.down(1 if self._joystick.get_hat(0)[1] < -0.5 else 0) + self.left(1 if self._joystick.get_hat(0)[0] < -0.5 else 0) + self.right(1 if self._joystick.get_hat(0)[0] > 0.5 else 0) + self.lx(self._joystick.get_axis(0)) + self.ly(-self._joystick.get_axis(1)) + # mode 2: light up + # self.up(1 if self._joystick.get_axis(1) < -0.5 else 0) + # self.down(1 if self._joystick.get_axis(0) > 0.5 else 0) + # self.left(1 if self._joystick.get_axis(0) < -0.5 else 0) + # self.right(1 if self._joystick.get_axis(0) > 0.5 else 0) + # self.lx(self._joystick.get_hat(0)[1]) + # self.ly(self._joystick.get_hat(0)[1]) + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/lib/crc_aarch64.so b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/lib/crc_aarch64.so new file mode 100644 index 0000000..4dfb614 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/lib/crc_aarch64.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a4db103653db78540d141ff4dc55216b83e5298280e639b2ad54a84ac2fae9a5 +size 7928 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/lib/crc_amd64.so b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/lib/crc_amd64.so new file mode 100644 index 0000000..8db8bf6 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/lib/crc_amd64.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b136a91a7e99c5cf914f465e131f63c897a17107939184a6f85d1a57cf315ade +size 15136 diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/singleton.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/singleton.py new file mode 100644 index 0000000..62ca3b0 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/singleton.py @@ -0,0 +1,11 @@ +class Singleton: + __instance = None + + def __new__(cls, *args, **kwargs): + if cls.__instance is None: + cls.__instance = super(Singleton, cls).__new__(cls) + return cls.__instance + + def __init__(self): + pass + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/thread.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/thread.py new file mode 100644 index 0000000..f17cf4e --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/thread.py @@ -0,0 +1,83 @@ +import sys +import os +import errno +import ctypes +import struct +import threading + +from .future import Future +from .timerfd import * + +class Thread(Future): + def __init__(self, target = None, name = None, args = (), kwargs = None): + super().__init__() + self.__target = target + self.__args = args + self.__kwargs = {} if kwargs is None else kwargs + self.__thread = threading.Thread(target=self.__ThreadFunc, name=name, daemon=True) + + def Start(self): + return self.__thread.start() + + def GetId(self): + return self.__thread.ident + + def GetNativeId(self): + return self.__thread.native_id + + def __ThreadFunc(self): + value = None + try: + value = self.__target(*self.__args, **self.__kwargs) + self.Ready(value) + except: + info = sys.exc_info() + self.Fail(f"[Thread] target func raise exception: name={info[0].__name__}, args={str(info[1].args)}") + +class RecurrentThread(Thread): + def __init__(self, interval: float = 1.0, target = None, name = None, args = (), kwargs = None): + self.__quit = False + self.__inter = interval + self.__loopTarget = target + self.__loopArgs = args + self.__loopKwargs = {} if kwargs is None else kwargs + + if interval is None or interval <= 0.0: + super().__init__(target=self.__LoopFunc_0, name=name) + else: + super().__init__(target=self.__LoopFunc, name=name) + + def Wait(self, timeout: float = None): + self.__quit = True + super().Wait(timeout) + + def __LoopFunc(self): + # clock type CLOCK_MONOTONIC = 1 + tfd = timerfd_create(1, 0) + spec = itimerspec.from_seconds(self.__inter, self.__inter) + timerfd_settime(tfd, 0, ctypes.byref(spec), None) + + while not self.__quit: + try: + self.__loopTarget(*self.__loopArgs, **self.__loopKwargs) + except: + info = sys.exc_info() + print(f"[RecurrentThread] target func raise exception: name={info[0].__name__}, args={str(info[1].args)}") + + try: + buf = os.read(tfd, 8) + # print(struct.unpack("Q", buf)[0]) + except OSError as e: + if e.errno != errno.EAGAIN: + raise e + + os.close(tfd) + + def __LoopFunc_0(self): + while not self.__quit: + try: + self.__loopTarget(*self.__args, **self.__kwargs) + except: + info = sys.exc_info() + print(f"[RecurrentThread] target func raise exception: name={info[0].__name__}, args={str(info[1].args)}") + diff --git a/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/timerfd.py b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/timerfd.py new file mode 100644 index 0000000..002ef39 --- /dev/null +++ b/external_dependencies/unitree_sdk2_python/unitree_sdk2py/utils/timerfd.py @@ -0,0 +1,45 @@ +import math +import ctypes +from .clib_lookup import CLIBLookup + +class timespec(ctypes.Structure): + _fields_ = [("sec", ctypes.c_long), ("nsec", ctypes.c_long)] + __slots__ = [name for name,type in _fields_] + + @classmethod + def from_seconds(cls, secs): + c = cls() + c.seconds = secs + return c + + @property + def seconds(self): + return self.sec + self.nsec / 1000000000 + + @seconds.setter + def seconds(self, secs): + x, y = math.modf(secs) + self.sec = int(y) + self.nsec = int(x * 1000000000) + + +class itimerspec(ctypes.Structure): + _fields_ = [("interval", timespec),("value", timespec)] + __slots__ = [name for name,type in _fields_] + + @classmethod + def from_seconds(cls, interval, value): + spec = cls() + spec.interval.seconds = interval + spec.value.seconds = value + return spec + + +# function timerfd_create +timerfd_create = CLIBLookup("timerfd_create", ctypes.c_int, (ctypes.c_long, ctypes.c_int)) + +# function timerfd_settime +timerfd_settime = CLIBLookup("timerfd_settime", ctypes.c_int, (ctypes.c_int, ctypes.c_int, ctypes.POINTER(itimerspec), ctypes.POINTER(itimerspec))) + +# function timerfd_gettime +timerfd_gettime = CLIBLookup("timerfd_gettime", ctypes.c_int, (ctypes.c_int, ctypes.POINTER(itimerspec))) diff --git a/gr00t_wbc/__init__.py b/gr00t_wbc/__init__.py new file mode 100644 index 0000000..3308a84 --- /dev/null +++ b/gr00t_wbc/__init__.py @@ -0,0 +1,3 @@ +from .version import VERSION, VERSION_SHORT # noqa + +__version__ = VERSION # noqa diff --git a/gr00t_wbc/control/__init__.py b/gr00t_wbc/control/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gr00t_wbc/control/base/__init__.py b/gr00t_wbc/control/base/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gr00t_wbc/control/base/env.py b/gr00t_wbc/control/base/env.py new file mode 100644 index 0000000..dcca21e --- /dev/null +++ b/gr00t_wbc/control/base/env.py @@ -0,0 +1,45 @@ +import gymnasium as gym + + +class Env: + """Base interface for all environments in the Gr00t framework""" + + def observe(self) -> dict[str, any]: + """Read the current state of this environment + + Returns: + dict: A dictionary of observations + """ + pass + + def queue_action(self, action: dict[str, any]): + """Queue an action to be executed + + Args: + action: A dictionary of action parameters + """ + pass + + def reset(self, **kwargs): + """Reset this environment to initial state""" + pass + + def observation_space(self) -> gym.Space: + """Get the observation space of this environment + + Returns: + gym.Space: The observation space + """ + pass + + def action_space(self) -> gym.Space: + """Get the action space of this environment + + Returns: + gym.Space: The action space + """ + pass + + def close(self): + """Close and clean up this environment""" + pass diff --git a/gr00t_wbc/control/base/humanoid_env.py b/gr00t_wbc/control/base/humanoid_env.py new file mode 100644 index 0000000..92e7dfb --- /dev/null +++ b/gr00t_wbc/control/base/humanoid_env.py @@ -0,0 +1,60 @@ +from abc import abstractmethod + +from gr00t_wbc.control.base.env import Env +from gr00t_wbc.control.base.sensor import Sensor +from gr00t_wbc.control.robot_model.robot_model import RobotModel + + +class Hands: + """Container class for left and right hand environments. + + Attributes: + left: Environment for the left hand + right: Environment for the right hand + """ + + left: Env + right: Env + + +class HumanoidEnv(Env): + """Base class for humanoid robot environments. + + This class provides the interface for accessing the robot's body, hands, and sensors. + """ + + def body(self) -> Env: + """Get the robot's body environment. + + Returns: + Env: The body environment + """ + pass + + def hands(self) -> Hands: + """Get the robot's hands. + + Returns: + Hands: Container with left and right hand environments + """ + pass + + def sensors(self) -> dict[str, Sensor]: + """Get the sensors of this environment + + Returns: + dict: A dictionary of sensors + """ + pass + + @abstractmethod + def robot_model(self) -> RobotModel: + """Get the robot model of this environment + This robot model is used to dispatch whole body actions to body + and hand actuators and to reconstruct proprioceptive + observations from body and hands. + + Returns: + RobotModel: The robot model + """ + pass diff --git a/gr00t_wbc/control/base/policy.py b/gr00t_wbc/control/base/policy.py new file mode 100755 index 0000000..a6ebb7e --- /dev/null +++ b/gr00t_wbc/control/base/policy.py @@ -0,0 +1,47 @@ +from abc import ABC, abstractmethod +from typing import Optional + + +class Policy(ABC): + """Base class for implementing control policies in the Gr00t framework. + + A Policy defines how an agent should behave in an environment by mapping observations + to actions. This abstract base class provides the interface that all concrete policy + implementations must follow. + """ + + def set_goal(self, goal: dict[str, any]): + """Set the command from the planner that the policy should follow. + + Args: + goal: Dictionary containing high-level commands or goals from the planner + """ + pass + + def set_observation(self, observation: dict[str, any]): + """Update the policy's current observation of the environment. + + Args: + observation: Dictionary containing the current state/observation of the environment + """ + self.observation = observation + + @abstractmethod + def get_action(self, time: Optional[float] = None) -> dict[str, any]: + """Compute and return the next action at the specified time, based on current observation + and planner command. + + Args: + time: Optional "monotonic time" for time-dependent policies + + Returns: + Dictionary containing the action to be executed + """ + + def close(self): + """Clean up any resources used by the policy.""" + pass + + def reset(self): + """Reset the policy to its initial state.""" + pass diff --git a/gr00t_wbc/control/base/sensor.py b/gr00t_wbc/control/base/sensor.py new file mode 100644 index 0000000..c9e5ba8 --- /dev/null +++ b/gr00t_wbc/control/base/sensor.py @@ -0,0 +1,35 @@ +import gymnasium as gym + + +class Sensor: + """Base class for implementing sensors in the Gr00t framework. + + A Sensor provides information about a specific sensor on the robot (e.g. camera, IMU, + force sensor). This abstract base class defines the interface that all concrete sensor + implementations must follow. + """ + + def read(self, **kwargs) -> any: + """Read the current sensor value. + + Args: + **kwargs: Additional parameters specific to the sensor implementation + (e.g. camera resolution, sampling rate) + + Returns: + The sensor reading value (e.g. image data, acceleration measurements) + """ + pass + + def observation_space(self) -> gym.Space: + """Get the observation space of this sensor. + + Returns: + gym.Space: The observation space defining the shape and bounds of sensor readings + (e.g. image dimensions for camera, measurement ranges for IMU) + """ + pass + + def close(self): + """Clean up any resources used by the sensor.""" + pass diff --git a/gr00t_wbc/control/envs/__init__.py b/gr00t_wbc/control/envs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gr00t_wbc/control/envs/g1/__init__.py b/gr00t_wbc/control/envs/g1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gr00t_wbc/control/envs/g1/g1_body.py b/gr00t_wbc/control/envs/g1/g1_body.py new file mode 100644 index 0000000..8543f80 --- /dev/null +++ b/gr00t_wbc/control/envs/g1/g1_body.py @@ -0,0 +1,67 @@ +from typing import Any, Dict + +import gymnasium as gym +import numpy as np + +from gr00t_wbc.control.base.env import Env +from gr00t_wbc.control.envs.g1.utils.command_sender import BodyCommandSender +from gr00t_wbc.control.envs.g1.utils.state_processor import BodyStateProcessor + + +class G1Body(Env): + def __init__(self, config: Dict[str, Any]): + super().__init__() + self.body_state_processor = BodyStateProcessor(config=config) + self.body_command_sender = BodyCommandSender(config=config) + + def observe(self) -> dict[str, any]: + body_state = self.body_state_processor._prepare_low_state() # (1, 148) + assert body_state.shape == (1, 148) + body_q = body_state[ + 0, 7 : 7 + 12 + 3 + 7 + 7 + ] # leg (12) + waist (3) + left arm (7) + right arm (7) + body_dq = body_state[0, 42 : 42 + 12 + 3 + 7 + 7] + body_ddq = body_state[0, 112 : 112 + 12 + 3 + 7 + 7] + body_tau_est = body_state[0, 77 : 77 + 12 + 3 + 7 + 7] + floating_base_pose = body_state[0, 0:7] + floating_base_vel = body_state[0, 36:42] + floating_base_acc = body_state[0, 106:112] + torso_quat = body_state[0, 141:145] + torso_ang_vel = body_state[0, 145:148] + + return { + "body_q": body_q, + "body_dq": body_dq, + "body_ddq": body_ddq, + "body_tau_est": body_tau_est, + "floating_base_pose": floating_base_pose, + "floating_base_vel": floating_base_vel, + "floating_base_acc": floating_base_acc, + "torso_quat": torso_quat, + "torso_ang_vel": torso_ang_vel, + } + + def queue_action(self, action: dict[str, any]): + # action should contain body_q, body_dq, body_tau + self.body_command_sender.send_command( + action["body_q"], action["body_dq"], action["body_tau"] + ) + + def observation_space(self) -> gym.Space: + return gym.spaces.Dict( + { + "body_q": gym.spaces.Box(low=-np.inf, high=np.inf, shape=(29,)), + "body_dq": gym.spaces.Box(low=-np.inf, high=np.inf, shape=(29,)), + "floating_base_pose": gym.spaces.Box(low=-np.inf, high=np.inf, shape=(7,)), + "floating_base_vel": gym.spaces.Box(low=-np.inf, high=np.inf, shape=(6,)), + } + ) + + def action_space(self) -> gym.Space: + return gym.spaces.Dict( + { + "body_q": gym.spaces.Box(low=-np.inf, high=np.inf, shape=(29,)), + "body_dq": gym.spaces.Box(low=-np.inf, high=np.inf, shape=(29,)), + "body_tau": gym.spaces.Box(low=-np.inf, high=np.inf, shape=(29,)), + } + ) diff --git a/gr00t_wbc/control/envs/g1/g1_env.py b/gr00t_wbc/control/envs/g1/g1_env.py new file mode 100644 index 0000000..f687060 --- /dev/null +++ b/gr00t_wbc/control/envs/g1/g1_env.py @@ -0,0 +1,324 @@ +from copy import deepcopy +from typing import Dict + +import gymnasium as gym +import numpy as np +from scipy.spatial.transform import Rotation as R + +from gr00t_wbc.control.base.humanoid_env import Hands, HumanoidEnv +from gr00t_wbc.control.envs.g1.g1_body import G1Body +from gr00t_wbc.control.envs.g1.g1_hand import G1ThreeFingerHand +from gr00t_wbc.control.envs.g1.sim.simulator_factory import SimulatorFactory, init_channel +from gr00t_wbc.control.envs.g1.utils.joint_safety import JointSafetyMonitor +from gr00t_wbc.control.robot_model.instantiation.g1 import instantiate_g1_robot_model +from gr00t_wbc.control.robot_model.robot_model import RobotModel +from gr00t_wbc.control.utils.ros_utils import ROSManager + + +class G1Env(HumanoidEnv): + def __init__( + self, + env_name: str = "default", + robot_model: RobotModel = None, + wbc_version: str = "v2", + config: Dict[str, any] = None, + **kwargs, + ): + super().__init__() + self.robot_model = deepcopy(robot_model) # need to cache FK results + self.config = config + + # Initialize safety monitor (visualization disabled) + self.safety_monitor = JointSafetyMonitor( + robot_model, enable_viz=False, env_type=self.config.get("ENV_TYPE", "real") + ) + self.last_obs = None + self.last_safety_ok = True # Track last safety status from queue_action + + init_channel(config=self.config) + + # Initialize body and hands + self._body = G1Body(config=self.config) + + self.with_hands = config.get("with_hands", False) + + # Gravity compensation settings + self.enable_gravity_compensation = config.get("enable_gravity_compensation", False) + self.gravity_compensation_joints = config.get("gravity_compensation_joints", ["arms"]) + + if self.enable_gravity_compensation: + print( + f"Gravity compensation enabled for joint groups: {self.gravity_compensation_joints}" + ) + if self.with_hands: + self._hands = Hands() + self._hands.left = G1ThreeFingerHand(is_left=True) + self._hands.right = G1ThreeFingerHand(is_left=False) + + # Initialize simulator if in simulation mode + self.use_sim = self.config.get("ENV_TYPE") == "sim" + + if self.use_sim: + # Create simulator using factory + + kwargs.update( + { + "onscreen": self.config.get("ENABLE_ONSCREEN", True), + "offscreen": self.config.get("ENABLE_OFFSCREEN", False), + } + ) + self.sim = SimulatorFactory.create_simulator( + config=self.config, + env_name=env_name, + wbc_version=wbc_version, + body_ik_solver_settings_type=kwargs.get("body_ik_solver_settings_type", "default"), + **kwargs, + ) + else: + self.sim = None + + # using the real robot + self.calibrate_hands() + + # Initialize ROS 2 node + self.ros_manager = ROSManager(node_name="g1_env") + self.ros_node = self.ros_manager.node + + self.delay_list = [] + self.visualize_delay = False + self.print_delay_interval = 100 + self.cnt = 0 + + def start_simulator(self): + # imag epublish disabled since the sim is running in a sub-thread + SimulatorFactory.start_simulator(self.sim, as_thread=True, enable_image_publish=False) + + def step_simulator(self): + sim_num_steps = int(self.config["REWARD_DT"] / self.config["SIMULATE_DT"]) + for _ in range(sim_num_steps): + self.sim.sim_env.sim_step() + self.sim.sim_env.update_viewer() + + def body(self) -> G1Body: + return self._body + + def hands(self) -> Hands: + if not self.with_hands: + raise RuntimeError( + "Hands not initialized. Use --with_hands True to enable hand functionality." + ) + return self._hands + + def observe(self) -> Dict[str, any]: + # Get observations from body and hands + body_obs = self.body().observe() + + body_q = body_obs["body_q"] + body_dq = body_obs["body_dq"] + body_ddq = body_obs["body_ddq"] + body_tau_est = body_obs["body_tau_est"] + + if self.with_hands: + left_hand_obs = self.hands().left.observe() + right_hand_obs = self.hands().right.observe() + left_hand_q = left_hand_obs["hand_q"] + right_hand_q = right_hand_obs["hand_q"] + left_hand_dq = left_hand_obs["hand_dq"] + right_hand_dq = right_hand_obs["hand_dq"] + left_hand_ddq = left_hand_obs["hand_ddq"] + right_hand_ddq = right_hand_obs["hand_ddq"] + left_hand_tau_est = left_hand_obs["hand_tau_est"] + right_hand_tau_est = right_hand_obs["hand_tau_est"] + + # Body and hand joint measurements come in actuator order, so we need to convert them to joint order + whole_q = self.robot_model.get_configuration_from_actuated_joints( + body_actuated_joint_values=body_q, + left_hand_actuated_joint_values=left_hand_q, + right_hand_actuated_joint_values=right_hand_q, + ) + whole_dq = self.robot_model.get_configuration_from_actuated_joints( + body_actuated_joint_values=body_dq, + left_hand_actuated_joint_values=left_hand_dq, + right_hand_actuated_joint_values=right_hand_dq, + ) + whole_ddq = self.robot_model.get_configuration_from_actuated_joints( + body_actuated_joint_values=body_ddq, + left_hand_actuated_joint_values=left_hand_ddq, + right_hand_actuated_joint_values=right_hand_ddq, + ) + whole_tau_est = self.robot_model.get_configuration_from_actuated_joints( + body_actuated_joint_values=body_tau_est, + left_hand_actuated_joint_values=left_hand_tau_est, + right_hand_actuated_joint_values=right_hand_tau_est, + ) + else: + # Body and hand joint measurements come in actuator order, so we need to convert them to joint order + whole_q = self.robot_model.get_configuration_from_actuated_joints( + body_actuated_joint_values=body_q, + ) + whole_dq = self.robot_model.get_configuration_from_actuated_joints( + body_actuated_joint_values=body_dq, + ) + whole_ddq = self.robot_model.get_configuration_from_actuated_joints( + body_actuated_joint_values=body_ddq, + ) + whole_tau_est = self.robot_model.get_configuration_from_actuated_joints( + body_actuated_joint_values=body_tau_est, + ) + + eef_obs = self.get_eef_obs(whole_q) + + obs = { + "q": whole_q, + "dq": whole_dq, + "ddq": whole_ddq, + "tau_est": whole_tau_est, + "floating_base_pose": body_obs["floating_base_pose"], + "floating_base_vel": body_obs["floating_base_vel"], + "floating_base_acc": body_obs["floating_base_acc"], + "wrist_pose": np.concatenate([eef_obs["left_wrist_pose"], eef_obs["right_wrist_pose"]]), + "torso_quat": body_obs["torso_quat"], + "torso_ang_vel": body_obs["torso_ang_vel"], + } + + if self.use_sim and self.sim: + obs.update(self.sim.get_privileged_obs()) + + # Store last observation for safety checking + self.last_obs = obs + + return obs + + @property + def observation_space(self) -> gym.Space: + # @todo: check if the low and high bounds are correct for body_obs. + q_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(self.robot_model.num_dofs,)) + dq_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(self.robot_model.num_dofs,)) + ddq_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(self.robot_model.num_dofs,)) + tau_est_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(self.robot_model.num_dofs,)) + floating_base_pose_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(7,)) + floating_base_vel_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(6,)) + floating_base_acc_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(6,)) + wrist_pose_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(7 + 7,)) + return gym.spaces.Dict( + { + "floating_base_pose": floating_base_pose_space, + "floating_base_vel": floating_base_vel_space, + "floating_base_acc": floating_base_acc_space, + "q": q_space, + "dq": dq_space, + "ddq": ddq_space, + "tau_est": tau_est_space, + "wrist_pose": wrist_pose_space, + } + ) + + def queue_action(self, action: Dict[str, any]): + # Safety check + if self.last_obs is not None: + safety_result = self.safety_monitor.handle_violations(self.last_obs, action) + action = safety_result["action"] + + # Map action from joint order to actuator order + body_actuator_q = self.robot_model.get_body_actuated_joints(action["q"]) + + self.body().queue_action( + { + "body_q": body_actuator_q, + "body_dq": np.zeros_like(body_actuator_q), + "body_tau": np.zeros_like(body_actuator_q), + } + ) + + if self.with_hands: + left_hand_actuator_q = self.robot_model.get_hand_actuated_joints( + action["q"], side="left" + ) + right_hand_actuator_q = self.robot_model.get_hand_actuated_joints( + action["q"], side="right" + ) + self.hands().left.queue_action({"hand_q": left_hand_actuator_q}) + self.hands().right.queue_action({"hand_q": right_hand_actuator_q}) + + def action_space(self) -> gym.Space: + return gym.spaces.Box(low=-np.inf, high=np.inf, shape=(self.robot_model.num_dofs,)) + + def calibrate_hands(self): + """Calibrate the hand joint qpos if real robot""" + if self.with_hands: + print("calibrating left hand") + self.hands().left.calibrate_hand() + print("calibrating right hand") + self.hands().right.calibrate_hand() + else: + print("Skipping hand calibration - hands disabled") + + def set_ik_indicator(self, teleop_cmd): + """Set the IK indicators for the simulator""" + if self.config["SIMULATOR"] == "robocasa": + if "left_wrist" in teleop_cmd and "right_wrist" in teleop_cmd: + left_wrist_input_pose = teleop_cmd["left_wrist"] + right_wrist_input_pose = teleop_cmd["right_wrist"] + ik_wrapper = self.sim.env.env.unwrapped.env + ik_wrapper.set_target_poses_outside_env( + [left_wrist_input_pose, right_wrist_input_pose] + ) + else: + raise NotImplementedError("IK indicators are only implemented for robocasa simulator") + + def set_sync_mode(self, sync_mode: bool, steps_per_action: int = 4): + """When set to True, the simulator will wait for the action to be sent to it""" + if self.config["SIMULATOR"] == "robocasa": + self.sim.set_sync_mode(sync_mode, steps_per_action) + + def reset(self): + if self.sim: + self.sim.reset() + + def close(self): + if self.sim: + self.sim.close() + + def robot_model(self) -> RobotModel: + return self.robot_model + + def get_reward(self): + if self.sim: + return self.sim.get_reward() + + def reset_obj_pos(self): + if hasattr(self.sim, "base_env") and hasattr(self.sim.base_env, "reset_obj_pos"): + self.sim.base_env.reset_obj_pos() + + def get_eef_obs(self, q: np.ndarray) -> Dict[str, np.ndarray]: + self.robot_model.cache_forward_kinematics(q) + eef_obs = {} + for side in ["left", "right"]: + wrist_placement = self.robot_model.frame_placement( + self.robot_model.supplemental_info.hand_frame_names[side] + ) + wrist_pos, wrist_quat = wrist_placement.translation[:3], R.from_matrix( + wrist_placement.rotation + ).as_quat(scalar_first=True) + eef_obs[f"{side}_wrist_pose"] = np.concatenate([wrist_pos, wrist_quat]) + + return eef_obs + + def get_joint_safety_status(self) -> bool: + """Get current joint safety status from the last queue_action safety check. + + Returns: + bool: True if joints are safe (no shutdown required), False if unsafe + """ + return self.last_safety_ok + + def handle_keyboard_button(self, key): + # Only handles keyboard buttons for the mujoco simulator for now. + if self.use_sim and self.config.get("SIMULATOR", "mujoco") == "mujoco": + self.sim.handle_keyboard_button(key) + + +if __name__ == "__main__": + env = G1Env(robot_model=instantiate_g1_robot_model(), wbc_version="gear_wbc") + while True: + print(env.observe()) diff --git a/gr00t_wbc/control/envs/g1/g1_hand.py b/gr00t_wbc/control/envs/g1/g1_hand.py new file mode 100644 index 0000000..1473aa2 --- /dev/null +++ b/gr00t_wbc/control/envs/g1/g1_hand.py @@ -0,0 +1,89 @@ +import time + +import gymnasium as gym +import numpy as np + +from gr00t_wbc.control.base.env import Env +from gr00t_wbc.control.envs.g1.utils.command_sender import HandCommandSender +from gr00t_wbc.control.envs.g1.utils.state_processor import HandStateProcessor + + +class G1ThreeFingerHand(Env): + def __init__(self, is_left: bool = True): + super().__init__() + self.is_left = is_left + self.hand_state_processor = HandStateProcessor(is_left=self.is_left) + self.hand_command_sender = HandCommandSender(is_left=self.is_left) + self.hand_q_offset = np.zeros(7) + + def observe(self) -> dict[str, any]: + hand_state = self.hand_state_processor._prepare_low_state() # (1, 28) + assert hand_state.shape == (1, 28) + + # Apply offset to the hand state + hand_state[0, :7] = hand_state[0, :7] + self.hand_q_offset + + hand_q = hand_state[0, :7] + hand_dq = hand_state[0, 7:14] + hand_ddq = hand_state[0, 21:28] + hand_tau_est = hand_state[0, 14:21] + + # Return the state for this specific hand (left or right) + return { + "hand_q": hand_q, + "hand_dq": hand_dq, + "hand_ddq": hand_ddq, + "hand_tau_est": hand_tau_est, + } + + def queue_action(self, action: dict[str, any]): + # Apply offset to the hand target + action["hand_q"] = action["hand_q"] - self.hand_q_offset + + # action should contain hand_q + self.hand_command_sender.send_command(action["hand_q"]) + + def observation_space(self) -> gym.Space: + return gym.spaces.Dict( + { + "hand_q": gym.spaces.Box(low=-np.inf, high=np.inf, shape=(7,)), + "hand_dq": gym.spaces.Box(low=-np.inf, high=np.inf, shape=(7,)), + "hand_ddq": gym.spaces.Box(low=-np.inf, high=np.inf, shape=(7,)), + "hand_tau_est": gym.spaces.Box(low=-np.inf, high=np.inf, shape=(7,)), + } + ) + + def action_space(self) -> gym.Space: + return gym.spaces.Dict({"hand_q": gym.spaces.Box(low=-np.inf, high=np.inf, shape=(7,))}) + + def calibrate_hand(self): + hand_obs = self.observe() + hand_q = hand_obs["hand_q"] + + hand_q_target = np.zeros_like(hand_q) + hand_q_target[0] = hand_q[0] + + # joint limit + hand_q0_upper_limit = np.deg2rad(60) # lower limit is -60 + + # move the figure counterclockwise until the limit + while True: + + if hand_q_target[0] - hand_q[0] < np.deg2rad(60): + hand_q_target[0] += np.deg2rad(10) + else: + self.hand_q_offset[0] = hand_q0_upper_limit - hand_q[0] + break + + self.queue_action({"hand_q": hand_q_target}) + + hand_obs = self.observe() + hand_q = hand_obs["hand_q"] + + time.sleep(0.1) + + print("done calibration, q0 offset (deg):", np.rad2deg(self.hand_q_offset[0])) + + # done calibrating, set target to zero + self.hand_q_target = np.zeros_like(hand_q) + self.queue_action({"hand_q": self.hand_q_target}) diff --git a/gr00t_wbc/control/envs/g1/sim/__init__.py b/gr00t_wbc/control/envs/g1/sim/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gr00t_wbc/control/envs/g1/sim/base_sim.py b/gr00t_wbc/control/envs/g1/sim/base_sim.py new file mode 100644 index 0000000..6848eb6 --- /dev/null +++ b/gr00t_wbc/control/envs/g1/sim/base_sim.py @@ -0,0 +1,772 @@ +import argparse +import pathlib +from pathlib import Path +import threading +from threading import Lock, Thread +from typing import Dict + +import mujoco +import mujoco.viewer +import numpy as np +import rclpy +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +import yaml + +from gr00t_wbc.control.envs.g1.sim.image_publish_utils import ImagePublishProcess +from gr00t_wbc.control.envs.g1.sim.metric_utils import check_contact, check_height +from gr00t_wbc.control.envs.g1.sim.sim_utilts import get_subtree_body_names +from gr00t_wbc.control.envs.g1.sim.unitree_sdk2py_bridge import ElasticBand, UnitreeSdk2Bridge + +GR00T_WBC_ROOT = Path(__file__).resolve().parent.parent.parent.parent.parent.parent + + +class DefaultEnv: + """Base environment class that handles simulation environment setup and step""" + + def __init__( + self, + config: Dict[str, any], + env_name: str = "default", + camera_configs: Dict[str, any] = {}, + onscreen: bool = False, + offscreen: bool = False, + enable_image_publish: bool = False, + ): + # global_view is only set up for this specifc scene for now. + if config["ROBOT_SCENE"] == "gr00t_wbc/control/robot_model/model_data/g1/scene_29dof.xml": + camera_configs["global_view"] = { + "height": 400, + "width": 400, + } + self.config = config + self.env_name = env_name + self.num_body_dof = self.config["NUM_JOINTS"] + self.num_hand_dof = self.config["NUM_HAND_JOINTS"] + self.sim_dt = self.config["SIMULATE_DT"] + self.obs = None + self.torques = np.zeros(self.num_body_dof + self.num_hand_dof * 2) + self.torque_limit = np.array(self.config["motor_effort_limit_list"]) + self.camera_configs = camera_configs + + # Thread safety lock + self.reward_lock = Lock() + + # Unitree bridge will be initialized by the simulator + self.unitree_bridge = None + + # Store display mode + self.onscreen = onscreen + + # Initialize scene (defined in subclasses) + self.init_scene() + self.last_reward = 0 + + # Setup offscreen rendering if needed + self.offscreen = offscreen + if self.offscreen: + self.init_renderers() + self.image_dt = self.config.get("IMAGE_DT", 0.033333) + self.image_publish_process = None + + def start_image_publish_subprocess(self, start_method: str = "spawn", camera_port: int = 5555): + # Use spawn method for better GIL isolation, or configured method + if len(self.camera_configs) == 0: + print( + "Warning: No camera configs provided, image publishing subprocess will not be started" + ) + return + start_method = self.config.get("MP_START_METHOD", "spawn") + self.image_publish_process = ImagePublishProcess( + camera_configs=self.camera_configs, + image_dt=self.image_dt, + zmq_port=camera_port, + start_method=start_method, + verbose=self.config.get("verbose", False), + ) + self.image_publish_process.start_process() + + def init_scene(self): + """Initialize the default robot scene""" + self.mj_model = mujoco.MjModel.from_xml_path( + str(pathlib.Path(GR00T_WBC_ROOT) / self.config["ROBOT_SCENE"]) + ) + self.mj_data = mujoco.MjData(self.mj_model) + self.mj_model.opt.timestep = self.sim_dt + self.torso_index = mujoco.mj_name2id(self.mj_model, mujoco.mjtObj.mjOBJ_BODY, "torso_link") + self.root_body = "pelvis" + # Enable the elastic band + if self.config["ENABLE_ELASTIC_BAND"]: + self.elastic_band = ElasticBand() + if "g1" in self.config["ROBOT_TYPE"]: + if self.config["enable_waist"]: + self.band_attached_link = self.mj_model.body("pelvis").id + else: + self.band_attached_link = self.mj_model.body("torso_link").id + elif "h1" in self.config["ROBOT_TYPE"]: + self.band_attached_link = self.mj_model.body("torso_link").id + else: + self.band_attached_link = self.mj_model.body("base_link").id + + if self.onscreen: + self.viewer = mujoco.viewer.launch_passive( + self.mj_model, + self.mj_data, + key_callback=self.elastic_band.MujuocoKeyCallback, + show_left_ui=False, + show_right_ui=False, + ) + else: + mujoco.mj_forward(self.mj_model, self.mj_data) + self.viewer = None + else: + if self.onscreen: + self.viewer = mujoco.viewer.launch_passive( + self.mj_model, self.mj_data, show_left_ui=False, show_right_ui=False + ) + else: + mujoco.mj_forward(self.mj_model, self.mj_data) + self.viewer = None + + if self.viewer: + # viewer camera + self.viewer.cam.azimuth = 120 # Horizontal rotation in degrees + self.viewer.cam.elevation = -30 # Vertical tilt in degrees + self.viewer.cam.distance = 2.0 # Distance from camera to target + self.viewer.cam.lookat = np.array([0, 0, 0.5]) # Point the camera is looking at + + # Note that the actuator order is the same as the joint order in the mujoco model. + self.body_joint_index = [] + self.left_hand_index = [] + self.right_hand_index = [] + for i in range(self.mj_model.njnt): + name = self.mj_model.joint(i).name + if any( + [ + part_name in name + for part_name in ["hip", "knee", "ankle", "waist", "shoulder", "elbow", "wrist"] + ] + ): + self.body_joint_index.append(i) + elif "left_hand" in name: + self.left_hand_index.append(i) + elif "right_hand" in name: + self.right_hand_index.append(i) + + assert len(self.body_joint_index) == self.config["NUM_JOINTS"] + assert len(self.left_hand_index) == self.config["NUM_HAND_JOINTS"] + assert len(self.right_hand_index) == self.config["NUM_HAND_JOINTS"] + + self.body_joint_index = np.array(self.body_joint_index) + self.left_hand_index = np.array(self.left_hand_index) + self.right_hand_index = np.array(self.right_hand_index) + + def init_renderers(self): + # Initialize camera renderers + self.renderers = {} + for camera_name, camera_config in self.camera_configs.items(): + renderer = mujoco.Renderer( + self.mj_model, height=camera_config["height"], width=camera_config["width"] + ) + self.renderers[camera_name] = renderer + + def compute_body_torques(self) -> np.ndarray: + """Compute body torques based on the current robot state""" + body_torques = np.zeros(self.num_body_dof) + if self.unitree_bridge is not None and self.unitree_bridge.low_cmd: + for i in range(self.unitree_bridge.num_body_motor): + if self.unitree_bridge.use_sensor: + body_torques[i] = ( + self.unitree_bridge.low_cmd.motor_cmd[i].tau + + self.unitree_bridge.low_cmd.motor_cmd[i].kp + * (self.unitree_bridge.low_cmd.motor_cmd[i].q - self.mj_data.sensordata[i]) + + self.unitree_bridge.low_cmd.motor_cmd[i].kd + * ( + self.unitree_bridge.low_cmd.motor_cmd[i].dq + - self.mj_data.sensordata[i + self.unitree_bridge.num_body_motor] + ) + ) + else: + body_torques[i] = ( + self.unitree_bridge.low_cmd.motor_cmd[i].tau + + self.unitree_bridge.low_cmd.motor_cmd[i].kp + * ( + self.unitree_bridge.low_cmd.motor_cmd[i].q + - self.mj_data.qpos[self.body_joint_index[i] + 7 - 1] + ) + + self.unitree_bridge.low_cmd.motor_cmd[i].kd + * ( + self.unitree_bridge.low_cmd.motor_cmd[i].dq + - self.mj_data.qvel[self.body_joint_index[i] + 6 - 1] + ) + ) + return body_torques + + def compute_hand_torques(self) -> np.ndarray: + """Compute hand torques based on the current robot state""" + left_hand_torques = np.zeros(self.num_hand_dof) + right_hand_torques = np.zeros(self.num_hand_dof) + if self.unitree_bridge is not None and self.unitree_bridge.low_cmd: + for i in range(self.unitree_bridge.num_hand_motor): + left_hand_torques[i] = ( + self.unitree_bridge.left_hand_cmd.motor_cmd[i].tau + + self.unitree_bridge.left_hand_cmd.motor_cmd[i].kp + * ( + self.unitree_bridge.left_hand_cmd.motor_cmd[i].q + - self.mj_data.qpos[self.left_hand_index[i] + 7 - 1] + ) + + self.unitree_bridge.left_hand_cmd.motor_cmd[i].kd + * ( + self.unitree_bridge.left_hand_cmd.motor_cmd[i].dq + - self.mj_data.qvel[self.left_hand_index[i] + 6 - 1] + ) + ) + right_hand_torques[i] = ( + self.unitree_bridge.right_hand_cmd.motor_cmd[i].tau + + self.unitree_bridge.right_hand_cmd.motor_cmd[i].kp + * ( + self.unitree_bridge.right_hand_cmd.motor_cmd[i].q + - self.mj_data.qpos[self.right_hand_index[i] + 7 - 1] + ) + + self.unitree_bridge.right_hand_cmd.motor_cmd[i].kd + * ( + self.unitree_bridge.right_hand_cmd.motor_cmd[i].dq + - self.mj_data.qvel[self.right_hand_index[i] + 6 - 1] + ) + ) + return np.concatenate((left_hand_torques, right_hand_torques)) + + def compute_body_qpos(self) -> np.ndarray: + """Compute body joint positions based on the current command""" + body_qpos = np.zeros(self.num_body_dof) + if self.unitree_bridge is not None and self.unitree_bridge.low_cmd: + for i in range(self.unitree_bridge.num_body_motor): + body_qpos[i] = self.unitree_bridge.low_cmd.motor_cmd[i].q + return body_qpos + + def compute_hand_qpos(self) -> np.ndarray: + """Compute hand joint positions based on the current command""" + hand_qpos = np.zeros(self.num_hand_dof * 2) + if self.unitree_bridge is not None and self.unitree_bridge.low_cmd: + for i in range(self.unitree_bridge.num_hand_motor): + hand_qpos[i] = self.unitree_bridge.left_hand_cmd.motor_cmd[i].q + hand_qpos[i + self.num_hand_dof] = self.unitree_bridge.right_hand_cmd.motor_cmd[i].q + return hand_qpos + + def prepare_obs(self) -> Dict[str, any]: + """Prepare observation dictionary from the current robot state""" + obs = {} + obs["floating_base_pose"] = self.mj_data.qpos[:7] + obs["floating_base_vel"] = self.mj_data.qvel[:6] + obs["floating_base_acc"] = self.mj_data.qacc[:6] + obs["secondary_imu_quat"] = self.mj_data.xquat[self.torso_index] + obs["secondary_imu_vel"] = self.mj_data.cvel[self.torso_index] + obs["body_q"] = self.mj_data.qpos[self.body_joint_index + 7 - 1] + obs["body_dq"] = self.mj_data.qvel[self.body_joint_index + 6 - 1] + obs["body_ddq"] = self.mj_data.qacc[self.body_joint_index + 6 - 1] + obs["body_tau_est"] = self.mj_data.actuator_force[self.body_joint_index - 1] + if self.num_hand_dof > 0: + obs["left_hand_q"] = self.mj_data.qpos[self.left_hand_index + 7 - 1] + obs["left_hand_dq"] = self.mj_data.qvel[self.left_hand_index + 6 - 1] + obs["left_hand_ddq"] = self.mj_data.qacc[self.left_hand_index + 6 - 1] + obs["left_hand_tau_est"] = self.mj_data.actuator_force[self.left_hand_index - 1] + obs["right_hand_q"] = self.mj_data.qpos[self.right_hand_index + 7 - 1] + obs["right_hand_dq"] = self.mj_data.qvel[self.right_hand_index + 6 - 1] + obs["right_hand_ddq"] = self.mj_data.qacc[self.right_hand_index + 6 - 1] + obs["right_hand_tau_est"] = self.mj_data.actuator_force[self.right_hand_index - 1] + obs["time"] = self.mj_data.time + return obs + + def sim_step(self): + self.obs = self.prepare_obs() + self.unitree_bridge.PublishLowState(self.obs) + if self.unitree_bridge.joystick: + self.unitree_bridge.PublishWirelessController() + if self.config["ENABLE_ELASTIC_BAND"]: + if self.elastic_band.enable: + # Get Cartesian pose and velocity of the band_attached_link + pose = np.concatenate( + [ + self.mj_data.xpos[self.band_attached_link], # link position in world + self.mj_data.xquat[ + self.band_attached_link + ], # link quaternion in world [w,x,y,z] + np.zeros(6), # placeholder for velocity + ] + ) + + # Get velocity in world frame + mujoco.mj_objectVelocity( + self.mj_model, + self.mj_data, + mujoco.mjtObj.mjOBJ_BODY, + self.band_attached_link, + pose[7:13], + 0, # 0 for world frame + ) + + # Reorder velocity from [ang, lin] to [lin, ang] + pose[7:10], pose[10:13] = pose[10:13], pose[7:10].copy() + self.mj_data.xfrc_applied[self.band_attached_link] = self.elastic_band.Advance(pose) + else: + # explicitly resetting the force when the band is not enabled + self.mj_data.xfrc_applied[self.band_attached_link] = np.zeros(6) + body_torques = self.compute_body_torques() + hand_torques = self.compute_hand_torques() + self.torques[self.body_joint_index - 1] = body_torques + if self.num_hand_dof > 0: + self.torques[self.left_hand_index - 1] = hand_torques[: self.num_hand_dof] + self.torques[self.right_hand_index - 1] = hand_torques[self.num_hand_dof :] + + self.torques = np.clip(self.torques, -self.torque_limit, self.torque_limit) + + if self.config["FREE_BASE"]: + self.mj_data.ctrl = np.concatenate((np.zeros(6), self.torques)) + else: + self.mj_data.ctrl = self.torques + mujoco.mj_step(self.mj_model, self.mj_data) + # self.check_self_collision() + + def kinematics_step(self): + """ + Run kinematics only: compute the qpos of the robot and directly set the qpos. + For debugging purposes. + """ + if self.unitree_bridge is not None: + self.unitree_bridge.PublishLowState(self.prepare_obs()) + if self.unitree_bridge.joystick: + self.unitree_bridge.PublishWirelessController() + + if self.config["ENABLE_ELASTIC_BAND"]: + if self.elastic_band.enable: + # Get Cartesian pose and velocity of the band_attached_link + pose = np.concatenate( + [ + self.mj_data.xpos[self.band_attached_link], # link position in world + self.mj_data.xquat[ + self.band_attached_link + ], # link quaternion in world [w,x,y,z] + np.zeros(6), # placeholder for velocity + ] + ) + + # Get velocity in world frame + mujoco.mj_objectVelocity( + self.mj_model, + self.mj_data, + mujoco.mjtObj.mjOBJ_BODY, + self.band_attached_link, + pose[7:13], + 0, # 0 for world frame + ) + + # Reorder velocity from [ang, lin] to [lin, ang] + pose[7:10], pose[10:13] = pose[10:13], pose[7:10].copy() + + self.mj_data.xfrc_applied[self.band_attached_link] = self.elastic_band.Advance(pose) + else: + # explicitly resetting the force when the band is not enabled + self.mj_data.xfrc_applied[self.band_attached_link] = np.zeros(6) + + body_qpos = self.compute_body_qpos() # (num_body_dof,) + hand_qpos = self.compute_hand_qpos() # (num_hand_dof * 2,) + + self.mj_data.qpos[self.body_joint_index + 7 - 1] = body_qpos + self.mj_data.qpos[self.left_hand_index + 7 - 1] = hand_qpos[: self.num_hand_dof] + self.mj_data.qpos[self.right_hand_index + 7 - 1] = hand_qpos[self.num_hand_dof :] + + mujoco.mj_kinematics(self.mj_model, self.mj_data) + mujoco.mj_comPos(self.mj_model, self.mj_data) + + def apply_perturbation(self, key): + """Apply perturbation to the robot""" + # Add velocity perturbations in body frame + perturbation_x_body = 0.0 # forward/backward in body frame + perturbation_y_body = 0.0 # left/right in body frame + if key == "up": + perturbation_x_body = 1.0 # forward + elif key == "down": + perturbation_x_body = -1.0 # backward + elif key == "left": + perturbation_y_body = 1.0 # left + elif key == "right": + perturbation_y_body = -1.0 # right + + # Transform body frame velocity to world frame using MuJoCo's rotation + vel_body = np.array([perturbation_x_body, perturbation_y_body, 0.0]) + vel_world = np.zeros(3) + base_quat = self.mj_data.qpos[3:7] # [w, x, y, z] quaternion + + # Use MuJoCo's robust quaternion rotation (handles invalid quaternions automatically) + mujoco.mju_rotVecQuat(vel_world, vel_body, base_quat) + + # Apply to base linear velocity in world frame + self.mj_data.qvel[0] += vel_world[0] # world X velocity + self.mj_data.qvel[1] += vel_world[1] # world Y velocity + + # Update dynamics after velocity change + mujoco.mj_forward(self.mj_model, self.mj_data) + + def update_viewer(self): + if self.viewer is not None: + self.viewer.sync() + + def update_viewer_camera(self): + if self.viewer is not None: + if self.viewer.cam.type == mujoco.mjtCamera.mjCAMERA_TRACKING: + self.viewer.cam.type = mujoco.mjtCamera.mjCAMERA_FREE + else: + self.viewer.cam.type = mujoco.mjtCamera.mjCAMERA_TRACKING + + def update_reward(self): + """Calculate reward. Should be implemented by subclasses.""" + with self.reward_lock: + self.last_reward = 0 + + def get_reward(self): + """Thread-safe way to get the last calculated reward.""" + with self.reward_lock: + return self.last_reward + + def set_unitree_bridge(self, unitree_bridge): + """Set the unitree bridge from the simulator""" + self.unitree_bridge = unitree_bridge + + def get_privileged_obs(self): + """Get privileged observation. Should be implemented by subclasses.""" + return {} + + def update_render_caches(self): + """Update render cache and shared memory for subprocess.""" + render_caches = {} + for camera_name, camera_config in self.camera_configs.items(): + renderer = self.renderers[camera_name] + if "params" in camera_config: + renderer.update_scene(self.mj_data, camera=camera_config["params"]) + else: + renderer.update_scene(self.mj_data, camera=camera_name) + render_caches[camera_name + "_image"] = renderer.render() + + # Update shared memory if image publishing process is available + if self.image_publish_process is not None: + self.image_publish_process.update_shared_memory(render_caches) + + return render_caches + + def handle_keyboard_button(self, key): + if self.elastic_band is not None: + self.elastic_band.handle_keyboard_button(key) + + if key == "backspace": + self.reset() + if key == "v": + self.update_viewer_camera() + if key in ["up", "down", "left", "right"]: + self.apply_perturbation(key) + + def check_fall(self): + """Check if the robot has fallen""" + self.fall = False + if self.mj_data.qpos[2] < 0.2: + self.fall = True + print(f"Warning: Robot has fallen, height: {self.mj_data.qpos[2]:.3f} m") + + if self.fall: + self.reset() + + def check_self_collision(self): + """Check for self-collision of the robot""" + robot_bodies = get_subtree_body_names(self.mj_model, self.mj_model.body(self.root_body).id) + self_collision, contact_bodies = check_contact( + self.mj_model, self.mj_data, robot_bodies, robot_bodies, return_all_contact_bodies=True + ) + if self_collision: + print(f"Warning: Self-collision detected: {contact_bodies}") + return self_collision + + def reset(self): + mujoco.mj_resetData(self.mj_model, self.mj_data) + + +class CubeEnv(DefaultEnv): + """Environment with a cube object for pick and place tasks""" + + def __init__( + self, + config: Dict[str, any], + onscreen: bool = False, + offscreen: bool = False, + enable_image_publish: bool = False, + ): + # Override the robot scene + config = config.copy() # Create a copy to avoid modifying the original + config["ROBOT_SCENE"] = "gr00t_wbc/control/robot_model/model_data/g1/pnp_cube_43dof.xml" + super().__init__(config, "cube", {}, onscreen, offscreen, enable_image_publish) + + def update_reward(self): + """Calculate reward based on gripper contact with cube and cube height""" + right_hand_body = [ + "right_hand_thumb_2_link", + "right_hand_middle_1_link", + "right_hand_index_1_link", + ] + gripper_cube_contact = check_contact( + self.mj_model, self.mj_data, right_hand_body, "cube_body" + ) + cube_lifted = check_height(self.mj_model, self.mj_data, "cube", 0.85, 2.0) + + with self.reward_lock: + self.last_reward = gripper_cube_contact & cube_lifted + + +class BoxEnv(DefaultEnv): + """Environment with a box object for manipulation tasks""" + + def __init__( + self, + config: Dict[str, any], + onscreen: bool = False, + offscreen: bool = False, + enable_image_publish: bool = False, + ): + # Override the robot scene + config = config.copy() # Create a copy to avoid modifying the original + config["ROBOT_SCENE"] = "gr00t_wbc/control/robot_model/model_data/g1/lift_box_43dof.xml" + super().__init__(config, "box", {}, onscreen, offscreen, enable_image_publish) + + def reward(self): + """Calculate reward based on gripper contact with cube and cube height""" + left_hand_body = [ + "left_hand_thumb_2_link", + "left_hand_middle_1_link", + "left_hand_index_1_link", + ] + right_hand_body = [ + "right_hand_thumb_2_link", + "right_hand_middle_1_link", + "right_hand_index_1_link", + ] + gripper_box_contact = check_contact(self.mj_model, self.mj_data, left_hand_body, "box_body") + gripper_box_contact &= check_contact( + self.mj_model, self.mj_data, right_hand_body, "box_body" + ) + box_lifted = check_height(self.mj_model, self.mj_data, "box", 0.92, 2.0) + + print("gripper_box_contact: ", gripper_box_contact, "box_lifted: ", box_lifted) + + with self.reward_lock: + self.last_reward = gripper_box_contact & box_lifted + return self.last_reward + + +class BottleEnv(DefaultEnv): + """Environment with a cylinder object for manipulation tasks""" + + def __init__( + self, + config: Dict[str, any], + onscreen: bool = False, + offscreen: bool = False, + enable_image_publish: bool = False, + ): + # Override the robot scene + config = config.copy() # Create a copy to avoid modifying the original + config["ROBOT_SCENE"] = "gr00t_wbc/control/robot_model/model_data/g1/pnp_bottle_43dof.xml" + camera_configs = { + "egoview": { + "height": 400, + "width": 400, + }, + } + super().__init__( + config, "cylinder", camera_configs, onscreen, offscreen, enable_image_publish + ) + + self.bottle_body = self.mj_model.body("bottle_body") + self.bottle_geom = self.mj_model.geom("bottle") + + if self.viewer is not None: + self.viewer.cam.type = mujoco.mjtCamera.mjCAMERA_FIXED + self.viewer.cam.fixedcamid = self.mj_model.camera("egoview").id + + def update_reward(self): + """Calculate reward based on gripper contact with cylinder and cylinder height""" + pass + + def get_privileged_obs(self): + obs_pos = self.mj_data.xpos[self.bottle_body.id] + obs_quat = self.mj_data.xquat[self.bottle_body.id] + return {"bottle_pos": obs_pos, "bottle_quat": obs_quat} + + +class BaseSimulator: + """Base simulator class that handles initialization and running of simulations""" + + def __init__(self, config: Dict[str, any], env_name: str = "default", **kwargs): + self.config = config + self.env_name = env_name + + # Initialize ROS 2 node + if not rclpy.ok(): + rclpy.init() + self.node = rclpy.create_node("sim_mujoco") + self.thread = threading.Thread(target=rclpy.spin, args=(self.node,), daemon=True) + self.thread.start() + else: + self.thread = None + executor = rclpy.get_global_executor() + self.node = executor.get_nodes()[0] # will only take the first node + + # Create rate objects for different update frequencies + self.sim_dt = self.config["SIMULATE_DT"] + self.reward_dt = self.config.get("REWARD_DT", 0.02) + self.image_dt = self.config.get("IMAGE_DT", 0.033333) + self.viewer_dt = self.config.get("VIEWER_DT", 0.02) + self.rate = self.node.create_rate(1 / self.sim_dt) + + # Create the appropriate environment based on name + if env_name == "default": + self.sim_env = DefaultEnv(config, env_name, **kwargs) + elif env_name == "pnp_cube": + self.sim_env = CubeEnv(config, **kwargs) + elif env_name == "lift_box": + self.sim_env = BoxEnv(config, **kwargs) + elif env_name == "pnp_bottle": + self.sim_env = BottleEnv(config, **kwargs) + else: + raise ValueError(f"Invalid environment name: {env_name}") + + # Initialize the DDS communication layer - should be safe to call multiple times + + try: + if self.config.get("INTERFACE", None): + ChannelFactoryInitialize(self.config["DOMAIN_ID"], self.config["INTERFACE"]) + else: + ChannelFactoryInitialize(self.config["DOMAIN_ID"]) + except Exception as e: + # If it fails because it's already initialized, that's okay + print(f"Note: Channel factory initialization attempt: {e}") + + # Initialize the unitree bridge and pass it to the environment + self.init_unitree_bridge() + self.sim_env.set_unitree_bridge(self.unitree_bridge) + + # Initialize additional components + self.init_subscriber() + self.init_publisher() + + self.sim_thread = None + + def start_as_thread(self): + # Create simulation thread + self.sim_thread = Thread(target=self.start) + self.sim_thread.start() + + def start_image_publish_subprocess(self, start_method: str = "spawn", camera_port: int = 5555): + """Start the image publish subprocess""" + self.sim_env.start_image_publish_subprocess(start_method, camera_port) + + def init_subscriber(self): + """Initialize subscribers. Can be overridden by subclasses.""" + pass + + def init_publisher(self): + """Initialize publishers. Can be overridden by subclasses.""" + pass + + def init_unitree_bridge(self): + """Initialize the unitree SDK bridge""" + self.unitree_bridge = UnitreeSdk2Bridge(self.config) + if self.config["USE_JOYSTICK"]: + self.unitree_bridge.SetupJoystick( + device_id=self.config["JOYSTICK_DEVICE"], js_type=self.config["JOYSTICK_TYPE"] + ) + + def start(self): + """Main simulation loop""" + sim_cnt = 0 + + try: + while ( + self.sim_env.viewer and self.sim_env.viewer.is_running() + ) or self.sim_env.viewer is None: + # Run simulation step + self.sim_env.sim_step() + + # Update viewer at viewer rate + if sim_cnt % int(self.viewer_dt / self.sim_dt) == 0: + self.sim_env.update_viewer() + + # Calculate reward at reward rate + if sim_cnt % int(self.reward_dt / self.sim_dt) == 0: + self.sim_env.update_reward() + + # Update render caches at image rate + if sim_cnt % int(self.image_dt / self.sim_dt) == 0: + self.sim_env.update_render_caches() + + # Sleep to maintain correct rate + self.rate.sleep() + + sim_cnt += 1 + except rclpy.exceptions.ROSInterruptException: + # This is expected when ROS shuts down - exit cleanly + pass + except Exception: + self.close() + + def __del__(self): + """Clean up resources when simulator is deleted""" + self.close() + + def reset(self): + """Reset the simulation. Can be overridden by subclasses.""" + self.sim_env.reset() + + def close(self): + """Close the simulation. Can be overridden by subclasses.""" + try: + # Stop image publishing subprocess + if self.sim_env.image_publish_process is not None: + self.sim_env.image_publish_process.stop() + + # Close viewer + if hasattr(self.sim_env, "viewer") and self.sim_env.viewer is not None: + self.sim_env.viewer.close() + + # Shutdown ROS + if rclpy.ok(): + rclpy.shutdown() + except Exception as e: + print(f"Warning during close: {e}") + + def get_privileged_obs(self): + obs = self.sim_env.get_privileged_obs() + # TODO: add ros2 topic to get privileged obs + return obs + + def handle_keyboard_button(self, key): + # Only handles keyboard buttons for default env. + if self.env_name == "default": + self.sim_env.handle_keyboard_button(key) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Robot") + parser.add_argument( + "--config", + type=str, + default="./gr00t_wbc/control/main/teleop/configs/g1_29dof_gear_wbc.yaml", + help="config file", + ) + args = parser.parse_args() + + with open(args.config, "r") as file: + config = yaml.load(file, Loader=yaml.FullLoader) + + if config.get("INTERFACE", None): + ChannelFactoryInitialize(config["DOMAIN_ID"], config["INTERFACE"]) + else: + ChannelFactoryInitialize(config["DOMAIN_ID"]) + + simulation = BaseSimulator(config) + simulation.start_as_thread() diff --git a/gr00t_wbc/control/envs/g1/sim/image_publish_utils.py b/gr00t_wbc/control/envs/g1/sim/image_publish_utils.py new file mode 100644 index 0000000..feed08c --- /dev/null +++ b/gr00t_wbc/control/envs/g1/sim/image_publish_utils.py @@ -0,0 +1,256 @@ +import multiprocessing as mp +from multiprocessing import shared_memory +import time +from typing import Any, Dict + +import numpy as np + +from gr00t_wbc.control.sensor.sensor_server import ImageMessageSchema, SensorServer + + +def get_multiprocessing_info(verbose: bool = True): + """Get information about multiprocessing start methods""" + + if verbose: + print(f"Available start methods: {mp.get_all_start_methods()}") + return mp.get_start_method() + + +class ImagePublishProcess: + """Subprocess for publishing images using shared memory and ZMQ""" + + def __init__( + self, + camera_configs: Dict[str, Any], + image_dt: float, + zmq_port: int = 5555, + start_method: str = "spawn", + verbose: bool = False, + ): + self.camera_configs = camera_configs + self.image_dt = image_dt + self.zmq_port = zmq_port + self.verbose = verbose + self.shared_memory_blocks = {} + self.shared_memory_info = {} + self.process = None + + # Use specific context to avoid global state pollution + self.mp_context = mp.get_context(start_method) + if self.verbose: + print(f"Using multiprocessing context: {start_method}") + + self.stop_event = self.mp_context.Event() + self.data_ready_event = self.mp_context.Event() + + # Ensure events start in correct state + self.stop_event.clear() + self.data_ready_event.clear() + + if self.verbose: + print(f"Initial stop_event state: {self.stop_event.is_set()}") + print(f"Initial data_ready_event state: {self.data_ready_event.is_set()}") + + # Calculate shared memory requirements for each camera + for camera_name, camera_config in camera_configs.items(): + height = camera_config["height"] + width = camera_config["width"] + # RGB image: height * width * 3 (uint8) + size = height * width * 3 + + # Create shared memory block + shm = shared_memory.SharedMemory(create=True, size=size) + self.shared_memory_blocks[camera_name] = shm + self.shared_memory_info[camera_name] = { + "name": shm.name, + "size": size, + "shape": (height, width, 3), + "dtype": np.uint8, + } + + def start_process(self): + """Start the image publishing subprocess""" + if self.verbose: + print(f"Starting subprocess with stop_event state: {self.stop_event.is_set()}") + self.process = self.mp_context.Process( + target=self._image_publish_worker, + args=( + self.shared_memory_info, + self.image_dt, + self.zmq_port, + self.stop_event, + self.data_ready_event, + self.verbose, + ), + ) + self.process.start() + if self.verbose: + print(f"Subprocess started, PID: {self.process.pid}") + + def update_shared_memory(self, render_caches: Dict[str, np.ndarray]): + """Update shared memory with new rendered images""" + images_updated = 0 + for camera_name in self.camera_configs.keys(): + image_key = f"{camera_name}_image" + if image_key in render_caches: + image = render_caches[image_key] + + # Ensure image is uint8 and has correct shape + if image.dtype != np.uint8: + image = (image * 255).astype(np.uint8) + + # Get shared memory array + shm = self.shared_memory_blocks[camera_name] + shared_array = np.ndarray( + self.shared_memory_info[camera_name]["shape"], + dtype=self.shared_memory_info[camera_name]["dtype"], + buffer=shm.buf, + ) + + # Copy image data to shared memory atomically + np.copyto(shared_array, image) + images_updated += 1 + + # Signal that new data is ready only after all images are written + if images_updated > 0: + if self.verbose: + print(f"Main process: Updated {images_updated} images, setting data_ready_event") + self.data_ready_event.set() + elif self.verbose: + print( + "Main process: No images to update. " + "please check if camera configs are provided and the renderer is properly initialized" + ) + + def stop(self): + """Stop the image publishing subprocess""" + if self.verbose: + print("Stopping image publishing subprocess...") + self.stop_event.set() + + if self.process and self.process.is_alive(): + # Give the process time to clean up gracefully + self.process.join(timeout=5) + if self.process.is_alive(): + if self.verbose: + print("Subprocess didn't stop gracefully, terminating...") + self.process.terminate() + self.process.join(timeout=2) + if self.process.is_alive(): + if self.verbose: + print("Force killing subprocess...") + self.process.kill() + self.process.join() + + # Clean up shared memory + for camera_name, shm in self.shared_memory_blocks.items(): + try: + shm.close() + shm.unlink() + if self.verbose: + print(f"Cleaned up shared memory for {camera_name}") + except Exception as e: + if self.verbose: + print(f"Warning: Failed to cleanup shared memory for {camera_name}: {e}") + + self.shared_memory_blocks.clear() + if self.verbose: + print("Image publishing subprocess stopped and cleaned up") + + @staticmethod + def _image_publish_worker( + shared_memory_info, image_dt, zmq_port, stop_event, data_ready_event, verbose + ): + """Worker function that runs in the subprocess""" + if verbose: + print(f"Worker started! PID: {__import__('os').getpid()}") + print(f"Worker stop_event state at start: {stop_event.is_set()}") + print(f"Worker data_ready_event state at start: {data_ready_event.is_set()}") + + try: + # Initialize ZMQ sensor server + sensor_server = SensorServer() + sensor_server.start_server(port=zmq_port) + + # Connect to shared memory blocks + shared_arrays = {} + shm_blocks = {} + for camera_name, info in shared_memory_info.items(): + shm = shared_memory.SharedMemory(name=info["name"]) + shm_blocks[camera_name] = shm + shared_arrays[camera_name] = np.ndarray( + info["shape"], dtype=info["dtype"], buffer=shm.buf + ) + + print( + f"Image publishing subprocess started with {len(shared_arrays)} cameras on ZMQ port {zmq_port}" + ) + + loop_count = 0 + last_data_time = time.time() + + while not stop_event.is_set(): + loop_count += 1 + + # Wait for new data with shorter timeout for better responsiveness + timeout = min(image_dt, 0.1) # Max 100ms timeout + data_available = data_ready_event.wait(timeout=timeout) + + current_time = time.time() + + if data_available: + data_ready_event.clear() + if loop_count % 50 == 0: + print("Image publish frequency: ", 1 / (current_time - last_data_time)) + last_data_time = current_time + + # Collect all camera images and serialize them + try: + from gr00t_wbc.control.sensor.sensor_server import ImageUtils + + # Copy all images atomically at once + image_copies = {name: arr.copy() for name, arr in shared_arrays.items()} + + # Create message with all camera images + message_dict = { + "images": image_copies, + "timestamps": {name: current_time for name in image_copies.keys()}, + } + + # Create ImageMessageSchema and serialize + image_msg = ImageMessageSchema( + timestamps=message_dict.get("timestamps"), + images=message_dict.get("images", None), + ) + + # Serialize and send via ZMQ + serialized_data = image_msg.serialize() + + # Add individual camera images to the message + for camera_name, image_copy in image_copies.items(): + serialized_data[f"{camera_name}"] = ImageUtils.encode_image(image_copy) + + sensor_server.send_message(serialized_data) + + except Exception as e: + print(f"Error publishing images: {e}") + + elif verbose and loop_count % 10 == 0: + print(f"Subprocess: Still waiting for data... (iteration {loop_count})") + + # Small sleep to prevent busy waiting when no data + if not data_available: + time.sleep(0.001) + + except KeyboardInterrupt: + print("Image publisher interrupted by user") + finally: + # Clean up + try: + for shm in shm_blocks.values(): + shm.close() + sensor_server.stop_server() + except Exception as e: + print(f"Error during subprocess cleanup: {e}") + if verbose: + print("Image publish subprocess stopped") diff --git a/gr00t_wbc/control/envs/g1/sim/metric_utils.py b/gr00t_wbc/control/envs/g1/sim/metric_utils.py new file mode 100644 index 0000000..454577f --- /dev/null +++ b/gr00t_wbc/control/envs/g1/sim/metric_utils.py @@ -0,0 +1,71 @@ +from typing import List, Tuple + +import mujoco + +from gr00t_wbc.control.envs.g1.sim.sim_utilts import get_body_geom_ids + + +def check_contact( + mj_model: mujoco.MjModel, + mj_data: mujoco.MjData, + bodies_1: List[str] | str, + bodies_2: List[str] | str, + return_all_contact_bodies: bool = False, +) -> Tuple[bool, List[Tuple[str, str]]] | bool: + """ + Finds contact between two body groups. Any geom in the body is considered to be in contact. + Args: + mj_model (MujocoModel): Current simulation object + mj_data (MjData): Current simulation data + bodies_1 (str or list of int): an individual body name or list of body names. + bodies_2 (str or list of int): another individual body name or list of body names. + Returns: + bool: True if any body in @bodies_1 is in contact with any body in @bodies_2. + """ + if isinstance(bodies_1, str): + bodies_1 = [bodies_1] + if isinstance(bodies_2, str): + bodies_2 = [bodies_2] + + geoms_1 = [get_body_geom_ids(mj_model, mj_model.body(g).id) for g in bodies_1] + geoms_1 = [g for geom_list in geoms_1 for g in geom_list] + geoms_2 = [get_body_geom_ids(mj_model, mj_model.body(g).id) for g in bodies_2] + geoms_2 = [g for geom_list in geoms_2 for g in geom_list] + contact_bodies = [] + for i in range(mj_data.ncon): + contact = mj_data.contact[i] + # check contact geom in geoms + c1_in_g1 = contact.geom1 in geoms_1 + c2_in_g2 = contact.geom2 in geoms_2 if geoms_2 is not None else True + # check contact geom in geoms (flipped) + c2_in_g1 = contact.geom2 in geoms_1 + c1_in_g2 = contact.geom1 in geoms_2 if geoms_2 is not None else True + if (c1_in_g1 and c2_in_g2) or (c1_in_g2 and c2_in_g1): + contact_bodies.append( + ( + mj_model.body(mj_model.geom(contact.geom1).bodyid).name, + mj_model.body(mj_model.geom(contact.geom2).bodyid).name, + ) + ) + if not return_all_contact_bodies: + break + if return_all_contact_bodies: + return len(contact_bodies) > 0, set(contact_bodies) + else: + return len(contact_bodies) > 0 + + +def check_height( + mj_model: mujoco.MjModel, + mj_data: mujoco.MjData, + geom_name: str, + lower_bound: float = -float("inf"), + upper_bound: float = float("inf"), +): + """ + Checks if the height of a geom is greater than a given height. + """ + geom_id = mj_model.geom(geom_name).id + return ( + mj_data.geom_xpos[geom_id][2] < upper_bound and mj_data.geom_xpos[geom_id][2] > lower_bound + ) diff --git a/gr00t_wbc/control/envs/g1/sim/robocasa_sim.py b/gr00t_wbc/control/envs/g1/sim/robocasa_sim.py new file mode 100644 index 0000000..a470018 --- /dev/null +++ b/gr00t_wbc/control/envs/g1/sim/robocasa_sim.py @@ -0,0 +1,63 @@ +from typing import Any, Dict, Tuple + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize + +from gr00t_wbc.control.envs.g1.sim.unitree_sdk2py_bridge import UnitreeSdk2Bridge +from gr00t_wbc.control.envs.robocasa.async_env_server import RoboCasaEnvServer +from gr00t_wbc.control.robot_model.instantiation import get_robot_type_and_model + + +class RoboCasaG1EnvServer(RoboCasaEnvServer): + def __init__( + self, env_name: str, wbc_config: Dict[str, Any], env_kwargs: Dict[str, Any], **kwargs + ): + if UnitreeSdk2Bridge is None: + raise ImportError("UnitreeSdk2Bridge is required for RoboCasaG1EnvServer") + self.wbc_config = wbc_config + _, robot_model = get_robot_type_and_model( + "G1", + enable_waist_ik=wbc_config["enable_waist"], + ) + if env_kwargs.get("camera_names", None) is None: + env_kwargs["camera_names"] = [ + "robot0_oak_egoview", + "robot0_oak_left_monoview", + "robot0_oak_right_monoview", + "robot0_rs_tppview", + ] + if env_kwargs.get("render_camera", None) is None: + if env_kwargs.get("renderer", "mjviewer") == "mjviewer": + env_kwargs["render_camera"] = "robot0_oak_egoview" + else: + env_kwargs["render_camera"] = [ + "robot0_oak_egoview", + "robot0_rs_tppview", + ] + + super().__init__(env_name, "G1", robot_model, env_kwargs=env_kwargs, **kwargs) + + def init_channel(self): + + try: + if self.wbc_config.get("INTERFACE", None): + ChannelFactoryInitialize(self.wbc_config["DOMAIN_ID"], self.wbc_config["INTERFACE"]) + else: + ChannelFactoryInitialize(self.wbc_config["DOMAIN_ID"]) + except Exception: + # If it fails because it's already initialized, that's okay + pass + + self.channel_bridge = UnitreeSdk2Bridge(config=self.wbc_config) + + def publish_obs(self): + # with self.cache_lock: + obs = self.caches["obs"] + self.channel_bridge.PublishLowState(obs) + + def get_action(self) -> Tuple[Dict[str, Any], bool, bool]: + q, ready, is_new_action = self.channel_bridge.GetAction() + return {"q": q}, ready, is_new_action + + def reset(self): + super().reset() + self.channel_bridge.reset() diff --git a/gr00t_wbc/control/envs/g1/sim/sim_utilts.py b/gr00t_wbc/control/envs/g1/sim/sim_utilts.py new file mode 100644 index 0000000..e032eb6 --- /dev/null +++ b/gr00t_wbc/control/envs/g1/sim/sim_utilts.py @@ -0,0 +1,96 @@ +""" +Utility functions for working with Mujoco models. +copied from https://github.com/kevinzakka/mink/blob/main/mink/utils.py +""" + +from typing import List + +import mujoco + + +def get_body_body_ids(model: mujoco.MjModel, body_id: int) -> List[int]: + """Get immediate children bodies belonging to a given body. + + Args: + model: Mujoco model. + body_id: ID of body. + + Returns: + A List containing all child body ids. + """ + return [ + i + for i in range(model.nbody) + if model.body_parentid[i] == body_id and body_id != i # Exclude the body itself. + ] + + +def get_subtree_body_ids(model: mujoco.MjModel, body_id: int) -> List[int]: + """Get all bodies belonging to subtree starting at a given body. + + Args: + model: Mujoco model. + body_id: ID of body where subtree starts. + + Returns: + A List containing all subtree body ids. + """ + body_ids: List[int] = [] + stack = [body_id] + while stack: + body_id = stack.pop() + body_ids.append(body_id) + stack += get_body_body_ids(model, body_id) + return body_ids + + +def get_subtree_body_names(model: mujoco.MjModel, body_id: int) -> List[str]: + """Get all bodies belonging to subtree starting at a given body. + Args: + model: Mujoco model. + body_id: ID of body where subtree starts. + + Returns: + A List containing all subtree body names. + """ + return [model.body(i).name for i in get_subtree_body_ids(model, body_id)] + + +def get_body_geom_ids(model: mujoco.MjModel, body_id: int) -> List[int]: + """Get immediate geoms belonging to a given body. + + Here, immediate geoms are those directly attached to the body and not its + descendants. + + Args: + model: Mujoco model. + body_id: ID of body. + + Returns: + A list containing all body geom ids. + """ + geom_start = model.body_geomadr[body_id] + geom_end = geom_start + model.body_geomnum[body_id] + return list(range(geom_start, geom_end)) + + +def get_subtree_geom_ids(model: mujoco.MjModel, body_id: int) -> List[int]: + """Get all geoms belonging to subtree starting at a given body. + + Here, a subtree is defined as the kinematic tree starting at the body and including + all its descendants. + + Args: + model: Mujoco model. + body_id: ID of body where subtree starts. + + Returns: + A list containing all subtree geom ids. + """ + geom_ids: List[int] = [] + stack = [body_id] + while stack: + body_id = stack.pop() + geom_ids.extend(get_body_geom_ids(model, body_id)) + stack += get_body_body_ids(model, body_id) + return geom_ids diff --git a/gr00t_wbc/control/envs/g1/sim/simulator_factory.py b/gr00t_wbc/control/envs/g1/sim/simulator_factory.py new file mode 100644 index 0000000..690d2f9 --- /dev/null +++ b/gr00t_wbc/control/envs/g1/sim/simulator_factory.py @@ -0,0 +1,144 @@ +import time +from typing import Any, Dict + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize + +from gr00t_wbc.control.envs.g1.sim.base_sim import BaseSimulator + + +def init_channel(config: Dict[str, Any]) -> None: + """ + Initialize the communication channel for simulator/robot communication. + + Args: + config: Configuration dictionary containing DOMAIN_ID and optionally INTERFACE + """ + if config.get("INTERFACE", None): + ChannelFactoryInitialize(config["DOMAIN_ID"], config["INTERFACE"]) + else: + ChannelFactoryInitialize(config["DOMAIN_ID"]) + + +class SimulatorFactory: + """Factory class for creating different types of simulators.""" + + @staticmethod + def create_simulator(config: Dict[str, Any], env_name: str = "default", **kwargs): + """ + Create a simulator based on the configuration. + + Args: + config: Configuration dictionary containing SIMULATOR type + env_name: Environment name + **kwargs: Additional keyword arguments for specific simulators + """ + simulator_type = config.get("SIMULATOR", "mujoco") + if simulator_type == "mujoco": + return SimulatorFactory._create_mujoco_simulator(config, env_name, **kwargs) + elif simulator_type == "robocasa": + return SimulatorFactory._create_robocasa_simulator(config, env_name, **kwargs) + else: + print( + f"Warning: Invalid simulator type: {simulator_type}. " + "If you are using run_sim_loop, please ignore this warning." + ) + return None + + @staticmethod + def _create_mujoco_simulator(config: Dict[str, Any], env_name: str = "default", **kwargs): + """Create a MuJoCo simulator instance.""" + env_kwargs = dict( + onscreen=kwargs.pop("onscreen", True), + offscreen=kwargs.pop("offscreen", False), + enable_image_publish=kwargs.get("enable_image_publish", False), + ) + return BaseSimulator(config=config, env_name=env_name, **env_kwargs) + + @staticmethod + def _create_robocasa_simulator(config: Dict[str, Any], env_name: str = "default", **kwargs): + """Create a RoboCasa simulator instance.""" + from gr00t_wbc.control.envs.g1.sim.robocasa_sim import RoboCasaG1EnvServer + from gr00t_wbc.control.envs.robocasa.utils.controller_utils import ( + update_robosuite_controller_configs, + ) + from gr00t_wbc.control.envs.robocasa.utils.sim_utils import change_simulation_timestep + + change_simulation_timestep(config["SIMULATE_DT"]) + + # Use default environment if not specified + if env_name == "default": + env_name = "GroundOnly" + + # Get or create controller configurations + controller_configs = kwargs.get("controller_configs") + if controller_configs is None: + wbc_version = kwargs.get("wbc_version", "gear_wbc") + controller_configs = update_robosuite_controller_configs("G1", wbc_version) + + # Build environment kwargs + env_kwargs = dict( + onscreen=kwargs.pop("onscreen", True), + offscreen=kwargs.pop("offscreen", False), + camera_names=kwargs.pop("camera_names", None), + camera_heights=kwargs.pop("camera_heights", None), + camera_widths=kwargs.pop("camera_widths", None), + control_freq=kwargs.pop("control_freq", 50), + controller_configs=controller_configs, + ik_indicator=kwargs.pop("ik_indicator", False), + randomize_cameras=kwargs.pop("randomize_cameras", True), + ) + + kwargs.update( + { + "verbose": config.pop("verbose", False), + "sim_freq": 1 / config.pop("SIMULATE_DT"), + } + ) + + return RoboCasaG1EnvServer( + env_name=env_name, + wbc_config=config, + env_kwargs=env_kwargs, + **kwargs, + ) + + @staticmethod + def start_simulator( + simulator, + as_thread: bool = True, + enable_image_publish: bool = False, + mp_start_method: str = "spawn", + camera_port: int = 5555, + ): + """ + Start the simulator either as a thread or as a separate process. + + Args: + simulator: The simulator instance to start + config: Configuration dictionary + as_thread: If True, start as thread; if False, start as subprocess + enable_offscreen: If True and not as_thread, start image publishing + """ + + if as_thread: + simulator.start_as_thread() + else: + # Wrap in try-except to make sure simulator is properly closed upon exit. + try: + if enable_image_publish: + simulator.start_image_publish_subprocess( + start_method=mp_start_method, + camera_port=camera_port, + ) + time.sleep(1) + simulator.start() + except KeyboardInterrupt: + print("+++++Simulator interrupted by user.") + except Exception as e: + print(f"++++error in simulator: {e} ++++") + finally: + print("++++closing simulator ++++") + simulator.close() + + # Allow simulator to initialize + time.sleep(1) diff --git a/gr00t_wbc/control/envs/g1/sim/unitree_sdk2py_bridge.py b/gr00t_wbc/control/envs/g1/sim/unitree_sdk2py_bridge.py new file mode 100644 index 0000000..ad0ed90 --- /dev/null +++ b/gr00t_wbc/control/envs/g1/sim/unitree_sdk2py_bridge.py @@ -0,0 +1,459 @@ +import sys +import threading +from typing import Dict, Tuple + +import glfw +from loguru import logger +import mujoco +import numpy as np +import pygame +import scipy.spatial.transform +from termcolor import colored +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelSubscriber +from unitree_sdk2py.idl.default import ( + unitree_go_msg_dds__WirelessController_, + unitree_hg_msg_dds__HandCmd_ as HandCmd_default, + unitree_hg_msg_dds__HandState_ as HandState_default, +) +from unitree_sdk2py.idl.unitree_go.msg.dds_ import WirelessController_ +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import HandCmd_, HandState_, OdoState_ + + +class UnitreeSdk2Bridge: + """ + This class is responsible for bridging the Unitree SDK2 with the Gr00t environment. + It is responsible for sending and receiving messages to and from the Unitree SDK2. + Both the body and hand are supported. + """ + + def __init__(self, config): + # Note that we do not give the mjdata and mjmodel to the UnitreeSdk2Bridge. + # It is unsafe and would be unflexible if we use a hand-plugged robot model + + robot_type = config["ROBOT_TYPE"] + if "g1" in robot_type or "h1-2" in robot_type: + from unitree_sdk2py.idl.default import ( + unitree_hg_msg_dds__IMUState_ as IMUState_default, + unitree_hg_msg_dds__LowCmd_, + unitree_hg_msg_dds__LowState_ as LowState_default, + unitree_hg_msg_dds__OdoState_ as OdoState_default, + ) + from unitree_sdk2py.idl.unitree_hg.msg.dds_ import IMUState_, LowCmd_, LowState_ + + self.low_cmd = unitree_hg_msg_dds__LowCmd_() + elif "h1" == robot_type or "go2" == robot_type: + from unitree_sdk2py.idl.default import ( + unitree_go_msg_dds__LowCmd_, + unitree_go_msg_dds__LowState_ as LowState_default, + unitree_hg_msg_dds__IMUState_ as IMUState_default, + ) + from unitree_sdk2py.idl.unitree_go.msg.dds_ import IMUState_, LowCmd_, LowState_ + + self.low_cmd = unitree_go_msg_dds__LowCmd_() + else: + raise ValueError(f"Invalid robot type '{robot_type}'. Expected 'g1', 'h1', or 'go2'.") + + self.num_body_motor = config["NUM_MOTORS"] + self.num_hand_motor = config.get("NUM_HAND_MOTORS", 0) + self.use_sensor = config["USE_SENSOR"] + + self.have_imu_ = False + self.have_frame_sensor_ = False + # if self.use_sensor: + # MOTOR_SENSOR_NUM = 3 + # self.dim_motor_sensor = MOTOR_SENSOR_NUM * self.num_motor + # # Check sensor + # for i in range(self.dim_motor_sensor, self.mj_model.nsensor): + # name = mujoco.mj_id2name(self.mj_model, mujoco._enums.mjtObj.mjOBJ_SENSOR, i) + # if name == "imu_quat": + # self.have_imu_ = True + # if name == "frame_pos": + # self.have_frame_sensor_ = True + + # Unitree sdk2 message + self.low_state = LowState_default() + self.low_state_puber = ChannelPublisher("rt/lowstate", LowState_) + self.low_state_puber.Init() + + # Only create odo_state for supported robot types + if "g1" in robot_type or "h1-2" in robot_type: + self.odo_state = OdoState_default() + self.odo_state_puber = ChannelPublisher("rt/odostate", OdoState_) + self.odo_state_puber.Init() + else: + self.odo_state = None + self.odo_state_puber = None + self.torso_imu_state = IMUState_default() + self.torso_imu_puber = ChannelPublisher("rt/secondary_imu", IMUState_) + self.torso_imu_puber.Init() + + self.left_hand_state = HandState_default() + self.left_hand_state_puber = ChannelPublisher("rt/dex3/left/state", HandState_) + self.left_hand_state_puber.Init() + self.right_hand_state = HandState_default() + self.right_hand_state_puber = ChannelPublisher("rt/dex3/right/state", HandState_) + self.right_hand_state_puber.Init() + + self.low_cmd_suber = ChannelSubscriber("rt/lowcmd", LowCmd_) + self.low_cmd_suber.Init(self.LowCmdHandler, 1) + + self.left_hand_cmd = HandCmd_default() + self.left_hand_cmd_suber = ChannelSubscriber("rt/dex3/left/cmd", HandCmd_) + self.left_hand_cmd_suber.Init(self.LeftHandCmdHandler, 1) + self.right_hand_cmd = HandCmd_default() + self.right_hand_cmd_suber = ChannelSubscriber("rt/dex3/right/cmd", HandCmd_) + self.right_hand_cmd_suber.Init(self.RightHandCmdHandler, 1) + + self.low_cmd_lock = threading.Lock() + self.left_hand_cmd_lock = threading.Lock() + self.right_hand_cmd_lock = threading.Lock() + + self.wireless_controller = unitree_go_msg_dds__WirelessController_() + self.wireless_controller_puber = ChannelPublisher( + "rt/wirelesscontroller", WirelessController_ + ) + self.wireless_controller_puber.Init() + + # joystick + self.key_map = { + "R1": 0, + "L1": 1, + "start": 2, + "select": 3, + "R2": 4, + "L2": 5, + "F1": 6, + "F2": 7, + "A": 8, + "B": 9, + "X": 10, + "Y": 11, + "up": 12, + "right": 13, + "down": 14, + "left": 15, + } + self.joystick = None + + self.reset() + + def reset(self): + with self.low_cmd_lock: + self.low_cmd_received = False + self.new_low_cmd = False + with self.left_hand_cmd_lock: + self.left_hand_cmd_received = False + self.new_left_hand_cmd = False + with self.right_hand_cmd_lock: + self.right_hand_cmd_received = False + self.new_right_hand_cmd = False + + def LowCmdHandler(self, msg): + with self.low_cmd_lock: + self.low_cmd = msg + self.low_cmd_received = True + self.new_low_cmd = True + + def LeftHandCmdHandler(self, msg): + with self.left_hand_cmd_lock: + self.left_hand_cmd = msg + self.left_hand_cmd_received = True + self.new_left_hand_cmd = True + + def RightHandCmdHandler(self, msg): + with self.right_hand_cmd_lock: + self.right_hand_cmd = msg + self.right_hand_cmd_received = True + self.new_right_hand_cmd = True + + def cmd_received(self): + with self.low_cmd_lock: + low_cmd_received = self.low_cmd_received + with self.left_hand_cmd_lock: + left_hand_cmd_received = self.left_hand_cmd_received + with self.right_hand_cmd_lock: + right_hand_cmd_received = self.right_hand_cmd_received + return low_cmd_received or left_hand_cmd_received or right_hand_cmd_received + + def PublishLowState(self, obs: Dict[str, any]): + # publish body state + if self.use_sensor: + raise NotImplementedError("Sensor data is not implemented yet.") + else: + for i in range(self.num_body_motor): + self.low_state.motor_state[i].q = obs["body_q"][i] + self.low_state.motor_state[i].dq = obs["body_dq"][i] + self.low_state.motor_state[i].ddq = obs["body_ddq"][i] + self.low_state.motor_state[i].tau_est = obs["body_tau_est"][i] + + if self.use_sensor and self.have_frame_sensor_: + raise NotImplementedError("Frame sensor data is not implemented yet.") + else: + # Get data from ground truth + self.odo_state.position[:] = obs["floating_base_pose"][:3] + self.odo_state.linear_velocity[:] = obs["floating_base_vel"][:3] + self.odo_state.orientation[:] = obs["floating_base_pose"][3:7] + self.odo_state.angular_velocity[:] = obs["floating_base_vel"][3:6] + # quaternion: w, x, y, z + self.low_state.imu_state.quaternion[:] = obs["floating_base_pose"][3:7] + # angular velocity + self.low_state.imu_state.gyroscope[:] = obs["floating_base_vel"][3:6] + # linear acceleration + self.low_state.imu_state.accelerometer[:] = obs["floating_base_acc"][:3] + + self.torso_imu_state.quaternion[:] = obs["secondary_imu_quat"] + self.torso_imu_state.gyroscope[:] = obs["secondary_imu_vel"][3:6] + + # acceleration: x, y, z (only available when frame sensor is enabled) + if self.have_frame_sensor_: + raise NotImplementedError("Frame sensor data is not implemented yet.") + self.low_state.tick = int(obs["time"] * 1e3) + self.low_state_puber.Write(self.low_state) + + self.odo_state.tick = int(obs["time"] * 1e3) + self.odo_state_puber.Write(self.odo_state) + + self.torso_imu_puber.Write(self.torso_imu_state) + + # publish hand state + for i in range(self.num_hand_motor): + self.left_hand_state.motor_state[i].q = obs["left_hand_q"][i] + self.left_hand_state.motor_state[i].dq = obs["left_hand_dq"][i] + self.left_hand_state_puber.Write(self.left_hand_state) + + for i in range(self.num_hand_motor): + self.right_hand_state.motor_state[i].q = obs["right_hand_q"][i] + self.right_hand_state.motor_state[i].dq = obs["right_hand_dq"][i] + self.right_hand_state_puber.Write(self.right_hand_state) + + def GetAction(self) -> Tuple[np.ndarray, bool, bool]: + with self.low_cmd_lock: + body_q = [self.low_cmd.motor_cmd[i].q for i in range(self.num_body_motor)] + with self.left_hand_cmd_lock: + left_hand_q = [self.left_hand_cmd.motor_cmd[i].q for i in range(self.num_hand_motor)] + with self.right_hand_cmd_lock: + right_hand_q = [self.right_hand_cmd.motor_cmd[i].q for i in range(self.num_hand_motor)] + with self.low_cmd_lock and self.left_hand_cmd_lock and self.right_hand_cmd_lock: + is_new_action = self.new_low_cmd and self.new_left_hand_cmd and self.new_right_hand_cmd + if is_new_action: + self.new_low_cmd = False + self.new_left_hand_cmd = False + self.new_right_hand_cmd = False + + return ( + np.concatenate([body_q[:-7], left_hand_q, body_q[-7:], right_hand_q]), + self.cmd_received(), + is_new_action, + ) + + def PublishWirelessController(self): + if self.joystick is not None: + pygame.event.get() + key_state = [0] * 16 + key_state[self.key_map["R1"]] = self.joystick.get_button(self.button_id["RB"]) + key_state[self.key_map["L1"]] = self.joystick.get_button(self.button_id["LB"]) + key_state[self.key_map["start"]] = self.joystick.get_button(self.button_id["START"]) + key_state[self.key_map["select"]] = self.joystick.get_button(self.button_id["SELECT"]) + key_state[self.key_map["R2"]] = self.joystick.get_axis(self.axis_id["RT"]) > 0 + key_state[self.key_map["L2"]] = self.joystick.get_axis(self.axis_id["LT"]) > 0 + key_state[self.key_map["F1"]] = 0 + key_state[self.key_map["F2"]] = 0 + key_state[self.key_map["A"]] = self.joystick.get_button(self.button_id["A"]) + key_state[self.key_map["B"]] = self.joystick.get_button(self.button_id["B"]) + key_state[self.key_map["X"]] = self.joystick.get_button(self.button_id["X"]) + key_state[self.key_map["Y"]] = self.joystick.get_button(self.button_id["Y"]) + key_state[self.key_map["up"]] = self.joystick.get_hat(0)[1] > 0 + key_state[self.key_map["right"]] = self.joystick.get_hat(0)[0] > 0 + key_state[self.key_map["down"]] = self.joystick.get_hat(0)[1] < 0 + key_state[self.key_map["left"]] = self.joystick.get_hat(0)[0] < 0 + + key_value = 0 + for i in range(16): + key_value += key_state[i] << i + + self.wireless_controller.keys = key_value + self.wireless_controller.lx = self.joystick.get_axis(self.axis_id["LX"]) + self.wireless_controller.ly = -self.joystick.get_axis(self.axis_id["LY"]) + self.wireless_controller.rx = self.joystick.get_axis(self.axis_id["RX"]) + self.wireless_controller.ry = -self.joystick.get_axis(self.axis_id["RY"]) + + self.wireless_controller_puber.Write(self.wireless_controller) + + def SetupJoystick(self, device_id=0, js_type="xbox"): + pygame.init() + pygame.joystick.init() + joystick_count = pygame.joystick.get_count() + if joystick_count > 0: + self.joystick = pygame.joystick.Joystick(device_id) + self.joystick.init() + else: + print("No gamepad detected.") + sys.exit() + + if js_type == "xbox": + if sys.platform.startswith("linux"): + self.axis_id = { + "LX": 0, # Left stick axis x + "LY": 1, # Left stick axis y + "RX": 3, # Right stick axis x + "RY": 4, # Right stick axis y + "LT": 2, # Left trigger + "RT": 5, # Right trigger + "DX": 6, # Directional pad x + "DY": 7, # Directional pad y + } + self.button_id = { + "X": 2, + "Y": 3, + "B": 1, + "A": 0, + "LB": 4, + "RB": 5, + "SELECT": 6, + "START": 7, + "XBOX": 8, + "LSB": 9, + "RSB": 10, + } + elif sys.platform == "darwin": + self.axis_id = { + "LX": 0, # Left stick axis x + "LY": 1, # Left stick axis y + "RX": 2, # Right stick axis x + "RY": 3, # Right stick axis y + "LT": 4, # Left trigger + "RT": 5, # Right trigger + } + self.button_id = { + "X": 2, + "Y": 3, + "B": 1, + "A": 0, + "LB": 9, + "RB": 10, + "SELECT": 4, + "START": 6, + "XBOX": 5, + "LSB": 7, + "RSB": 8, + "DYU": 11, + "DYD": 12, + "DXL": 13, + "DXR": 14, + } + else: + print("Unsupported OS. ") + + elif js_type == "switch": + # Yuanhang: may differ for different OS, need to be checked + self.axis_id = { + "LX": 0, # Left stick axis x + "LY": 1, # Left stick axis y + "RX": 2, # Right stick axis x + "RY": 3, # Right stick axis y + "LT": 5, # Left trigger + "RT": 4, # Right trigger + "DX": 6, # Directional pad x + "DY": 7, # Directional pad y + } + + self.button_id = { + "X": 3, + "Y": 4, + "B": 1, + "A": 0, + "LB": 6, + "RB": 7, + "SELECT": 10, + "START": 11, + } + else: + print("Unsupported gamepad. ") + + def PrintSceneInformation(self): + print(" ") + logger.info(colored("<<------------- Link ------------->>", "green")) + for i in range(self.mj_model.nbody): + name = mujoco.mj_id2name(self.mj_model, mujoco._enums.mjtObj.mjOBJ_BODY, i) + if name: + logger.info(f"link_index: {i}, name: {name}") + print(" ") + + logger.info(colored("<<------------- Joint ------------->>", "green")) + for i in range(self.mj_model.njnt): + name = mujoco.mj_id2name(self.mj_model, mujoco._enums.mjtObj.mjOBJ_JOINT, i) + if name: + logger.info(f"joint_index: {i}, name: {name}") + print(" ") + + logger.info(colored("<<------------- Actuator ------------->>", "green")) + for i in range(self.mj_model.nu): + name = mujoco.mj_id2name(self.mj_model, mujoco._enums.mjtObj.mjOBJ_ACTUATOR, i) + if name: + logger.info(f"actuator_index: {i}, name: {name}") + print(" ") + + logger.info(colored("<<------------- Sensor ------------->>", "green")) + index = 0 + for i in range(self.mj_model.nsensor): + name = mujoco.mj_id2name(self.mj_model, mujoco._enums.mjtObj.mjOBJ_SENSOR, i) + if name: + logger.info( + f"sensor_index: {index}, name: {name}, dim: {self.mj_model.sensor_dim[i]}" + ) + index = index + self.mj_model.sensor_dim[i] + print(" ") + + +class ElasticBand: + """ + ref: https://github.com/unitreerobotics/unitree_mujoco + """ + + def __init__(self): + self.kp_pos = 10000 + self.kd_pos = 1000 + self.kp_ang = 1000 + self.kd_ang = 10 + self.point = np.array([0, 0, 1]) + self.length = 0 + self.enable = True + + def Advance(self, pose): + """ + Args: + pose: 13D array containing: + - pose[0:3]: position in world frame + - pose[3:7]: quaternion [w,x,y,z] in world frame + - pose[7:10]: linear velocity in world frame + - pose[10:13]: angular velocity in world frame + Returns: + np.ndarray: 6D vector [fx, fy, fz, tx, ty, tz] + """ + pos = pose[0:3] + quat = pose[3:7] + lin_vel = pose[7:10] + ang_vel = pose[10:13] + + δx = self.point - pos + f = self.kp_pos * (δx + np.array([0, 0, self.length])) + self.kd_pos * (0 - lin_vel) + + # --- Orientation PD control for torque --- + quat = np.array([quat[1], quat[2], quat[3], quat[0]]) # reorder to [x,y,z,w] for scipy + rot = scipy.spatial.transform.Rotation.from_quat(quat) + rotvec = rot.as_rotvec() # axis-angle error + torque = -self.kp_ang * rotvec - self.kd_ang * ang_vel + + return np.concatenate([f, torque]) + + def MujuocoKeyCallback(self, key): + if key == glfw.KEY_7: + self.length -= 0.1 + if key == glfw.KEY_8: + self.length += 0.1 + if key == glfw.KEY_9: + self.enable = not self.enable + + def handle_keyboard_button(self, key): + if key == "9": + self.enable = not self.enable diff --git a/gr00t_wbc/control/envs/g1/utils/__init__.py b/gr00t_wbc/control/envs/g1/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gr00t_wbc/control/envs/g1/utils/command_sender.py b/gr00t_wbc/control/envs/g1/utils/command_sender.py new file mode 100644 index 0000000..41f60b3 --- /dev/null +++ b/gr00t_wbc/control/envs/g1/utils/command_sender.py @@ -0,0 +1,146 @@ +from typing import Dict + +import numpy as np +from unitree_sdk2py.core.channel import ChannelPublisher +from unitree_sdk2py.idl.default import unitree_hg_msg_dds__HandCmd_ +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import HandCmd_ +from unitree_sdk2py.utils.crc import CRC + + +class BodyCommandSender: + def __init__(self, config: Dict): + self.config = config + if self.config["ROBOT_TYPE"] == "h1" or self.config["ROBOT_TYPE"] == "go2": + from unitree_sdk2py.idl.default import unitree_go_msg_dds__LowCmd_ + from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowCmd_ + + self.low_cmd = unitree_go_msg_dds__LowCmd_() + elif ( + self.config["ROBOT_TYPE"] == "g1_29dof" + or self.config["ROBOT_TYPE"] == "h1-2_21dof" + or self.config["ROBOT_TYPE"] == "h1-2_27dof" + ): + from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowCmd_ + from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowCmd_ + + self.low_cmd = unitree_hg_msg_dds__LowCmd_() + else: + raise NotImplementedError( + f"Robot type {self.config['ROBOT_TYPE']} is not supported yet" + ) + # init kp kd + self.kp_level = 1.0 + self.waist_kp_level = 1.0 + self.robot_kp = np.zeros(self.config["NUM_MOTORS"]) + self.robot_kd = np.zeros(self.config["NUM_MOTORS"]) + # set kp level + for i in range(len(self.config["MOTOR_KP"])): + self.robot_kp[i] = self.config["MOTOR_KP"][i] * self.kp_level + for i in range(len(self.config["MOTOR_KD"])): + self.robot_kd[i] = self.config["MOTOR_KD"][i] * 1.0 + self.weak_motor_joint_index = [] + for _, value in self.config["WeakMotorJointIndex"].items(): + self.weak_motor_joint_index.append(value) + # init low cmd publisher + self.lowcmd_publisher_ = ChannelPublisher("rt/lowcmd", LowCmd_) + self.lowcmd_publisher_.Init() + self.InitLowCmd() + self.low_state = None + self.crc = CRC() + + def InitLowCmd(self): + # h1/go2: + if self.config["ROBOT_TYPE"] == "h1" or self.config["ROBOT_TYPE"] == "go2": + self.low_cmd.head[0] = 0xFE + self.low_cmd.head[1] = 0xEF + else: + pass + + self.low_cmd.level_flag = 0xFF + self.low_cmd.gpio = 0 + for i in range(self.config["NUM_MOTORS"]): + if self.is_weak_motor(i): + self.low_cmd.motor_cmd[i].mode = 0x01 + else: + self.low_cmd.motor_cmd[i].mode = 0x0A + self.low_cmd.motor_cmd[i].q = self.config["UNITREE_LEGGED_CONST"]["PosStopF"] + self.low_cmd.motor_cmd[i].kp = 0 + self.low_cmd.motor_cmd[i].dq = self.config["UNITREE_LEGGED_CONST"]["VelStopF"] + self.low_cmd.motor_cmd[i].kd = 0 + self.low_cmd.motor_cmd[i].tau = 0 + if ( + self.config["ROBOT_TYPE"] == "g1_29dof" + or self.config["ROBOT_TYPE"] == "h1-2_21dof" + or self.config["ROBOT_TYPE"] == "h1-2_27dof" + ): + self.low_cmd.mode_machine = self.config["UNITREE_LEGGED_CONST"]["MODE_MACHINE"] + self.low_cmd.mode_pr = self.config["UNITREE_LEGGED_CONST"]["MODE_PR"] + else: + pass + + def is_weak_motor(self, motor_index: int) -> bool: + return motor_index in self.weak_motor_joint_index + + def send_command(self, cmd_q: np.ndarray, cmd_dq: np.ndarray, cmd_tau: np.ndarray): + for i in range(self.config["NUM_MOTORS"]): + motor_index = self.config["JOINT2MOTOR"][i] + joint_index = self.config["MOTOR2JOINT"][i] + # print(f"motor_index: {motor_index}, joint_index: {joint_index}") + if joint_index == -1: + # send default joint position command + self.low_cmd.motor_cmd[motor_index].q = self.config["DEFAULT_MOTOR_ANGLES"][ + motor_index + ] + self.low_cmd.motor_cmd[motor_index].dq = 0.0 + self.low_cmd.motor_cmd[motor_index].tau = 0.0 + else: + self.low_cmd.motor_cmd[motor_index].q = cmd_q[joint_index] + self.low_cmd.motor_cmd[motor_index].dq = cmd_dq[joint_index] + self.low_cmd.motor_cmd[motor_index].tau = cmd_tau[joint_index] + # kp kd + self.low_cmd.motor_cmd[motor_index].kp = self.robot_kp[motor_index] + self.low_cmd.motor_cmd[motor_index].kd = self.robot_kd[motor_index] + + self.low_cmd.crc = self.crc.Crc(self.low_cmd) + self.lowcmd_publisher_.Write(self.low_cmd) + + +def make_hand_mode(motor_index: int) -> int: + status = 0x01 + timeout = 0x01 + mode = motor_index & 0x0F + mode |= status << 4 # bits [4..6] + mode |= timeout << 7 # bit 7 + return mode + + +class HandCommandSender: + def __init__(self, is_left: bool = True): + self.is_left = is_left + if self.is_left: + self.cmd_pub = ChannelPublisher("rt/dex3/left/cmd", HandCmd_) + else: + self.cmd_pub = ChannelPublisher("rt/dex3/right/cmd", HandCmd_) + + self.cmd_pub.Init() + self.cmd = unitree_hg_msg_dds__HandCmd_() + + self.hand_dof = 7 + + self.kp = [1.0] * self.hand_dof + self.kd = [0.2] * self.hand_dof + self.kp[0] = 2.0 + self.kd[0] = 0.5 + + def send_command(self, cmd: np.ndarray): + for i in range(self.hand_dof): + # Build the bitfield mode (see your C++ example) + mode_val = make_hand_mode(i) + self.cmd.motor_cmd[i].mode = mode_val + self.cmd.motor_cmd[i].q = cmd[i] + self.cmd.motor_cmd[i].dq = 0.0 + self.cmd.motor_cmd[i].tau = 0.0 + self.cmd.motor_cmd[i].kp = self.kp[i] + self.cmd.motor_cmd[i].kd = self.kd[i] + + self.cmd_pub.Write(self.cmd) diff --git a/gr00t_wbc/control/envs/g1/utils/joint_safety.py b/gr00t_wbc/control/envs/g1/utils/joint_safety.py new file mode 100644 index 0000000..113bd8c --- /dev/null +++ b/gr00t_wbc/control/envs/g1/utils/joint_safety.py @@ -0,0 +1,534 @@ +"""Joint safety monitor for G1 robot. + +This module implements safety monitoring for arm and finger joint velocities using +joint groups defined in the robot model's supplemental info. Leg joints are not monitored. +""" + +from datetime import datetime +import sys +import time +from typing import Dict, List, Optional, Tuple + +import numpy as np + +from gr00t_wbc.data.viz.rerun_viz import RerunViz + + +class JointSafetyMonitor: + """Monitor joint velocities for G1 robot arms and hands.""" + + # Velocity limits in rad/s + ARM_VELOCITY_LIMIT = 6.0 # rad/s for arm joints + HAND_VELOCITY_LIMIT = 50.0 # rad/s for finger joints + + def __init__(self, robot_model, enable_viz: bool = False, env_type: str = "real"): + """Initialize joint safety monitor. + + Args: + robot_model: The robot model containing joint information + enable_viz: If True, enable rerun visualization (default False) + env_type: Environment type - "sim" or "real" (default "real") + """ + self.robot_model = robot_model + self.safety_margin = 1.0 # Hardcoded safety margin + self.enable_viz = enable_viz + self.env_type = env_type + + # Startup ramping parameters + self.control_frequency = 50 # Hz, hardcoded from run_g1_control_loop.py + self.ramp_duration_steps = int(2.0 * self.control_frequency) # 2 seconds * 50Hz = 100 steps + self.startup_counter = 0 + self.initial_positions = None + self.startup_complete = False + + # Initialize velocity and position limits for monitored joints + self.velocity_limits = {} + self.position_limits = {} + self._initialize_limits() + + # Track violations for reporting + self.violations = [] + + # Initialize visualization + self.right_arm_indices = None + self.right_arm_joint_names = [] + self.left_arm_indices = None + self.left_arm_joint_names = [] + self.right_hand_indices = None + self.right_hand_joint_names = [] + self.left_hand_indices = None + self.left_hand_joint_names = [] + try: + arm_indices = self.robot_model.get_joint_group_indices("arms") + all_joint_names = [self.robot_model.joint_names[i] for i in arm_indices] + # Filter for right and left arm joints + self.right_arm_joint_names = [ + name for name in all_joint_names if name.startswith("right_") + ] + self.right_arm_indices = [ + self.robot_model.joint_to_dof_index[name] for name in self.right_arm_joint_names + ] + self.left_arm_joint_names = [ + name for name in all_joint_names if name.startswith("left_") + ] + self.left_arm_indices = [ + self.robot_model.joint_to_dof_index[name] for name in self.left_arm_joint_names + ] + # Hand joints + hand_indices = self.robot_model.get_joint_group_indices("hands") + all_hand_names = [self.robot_model.joint_names[i] for i in hand_indices] + self.right_hand_joint_names = [ + name for name in all_hand_names if name.startswith("right_") + ] + self.right_hand_indices = [ + self.robot_model.joint_to_dof_index[name] for name in self.right_hand_joint_names + ] + self.left_hand_joint_names = [ + name for name in all_hand_names if name.startswith("left_") + ] + self.left_hand_indices = [ + self.robot_model.joint_to_dof_index[name] for name in self.left_hand_joint_names + ] + except ValueError as e: + print(f"[JointSafetyMonitor] Warning: Could not initialize arm/hand visualization: {e}") + except Exception: + pass + + # Use single tensor_key for each plot + self.right_arm_pos_key = "right_arm_qpos" + self.left_arm_pos_key = "left_arm_qpos" + self.right_arm_vel_key = "right_arm_dq" + self.left_arm_vel_key = "left_arm_dq" + self.right_hand_pos_key = "right_hand_qpos" + self.left_hand_pos_key = "left_hand_qpos" + self.right_hand_vel_key = "right_hand_dq" + self.left_hand_vel_key = "left_hand_dq" + + # Define a consistent color palette for up to 8 joints (tab10 + extra) + self.joint_colors = [ + [31, 119, 180], # blue + [255, 127, 14], # orange + [44, 160, 44], # green + [214, 39, 40], # red + [148, 103, 189], # purple + [140, 86, 75], # brown + [227, 119, 194], # pink + [127, 127, 127], # gray (for 8th joint if needed) + ] + + # Initialize Rerun visualization only if enabled + self.viz = None + if self.enable_viz: + try: + self.viz = RerunViz( + image_keys=[], + tensor_keys=[ + self.right_arm_pos_key, + self.left_arm_pos_key, + self.right_arm_vel_key, + self.left_arm_vel_key, + self.right_hand_pos_key, + self.left_hand_pos_key, + self.right_hand_vel_key, + self.left_hand_vel_key, + ], + window_size=10.0, + app_name="joint_safety_monitor", + ) + except Exception: + self.viz = None + + def _initialize_limits(self): + """Initialize velocity and position limits for arm and hand joints using robot model joint groups.""" + if self.robot_model.supplemental_info is None: + raise ValueError("Robot model must have supplemental_info to use joint groups") + + # Get arm joint indices from robot model joint groups + try: + arm_indices = self.robot_model.get_joint_group_indices("arms") + arm_joint_names = [self.robot_model.joint_names[i] for i in arm_indices] + + for joint_name in arm_joint_names: + # Set velocity limits + vel_limit = self.ARM_VELOCITY_LIMIT * self.safety_margin + self.velocity_limits[joint_name] = {"min": -vel_limit, "max": vel_limit} + + # Set position limits from robot model + if joint_name in self.robot_model.joint_to_dof_index: + joint_idx = self.robot_model.joint_to_dof_index[joint_name] + # Adjust index for floating base if present + limit_idx = joint_idx - (7 if self.robot_model.is_floating_base_model else 0) + + if 0 <= limit_idx < len(self.robot_model.lower_joint_limits): + pos_min = self.robot_model.lower_joint_limits[limit_idx] + pos_max = self.robot_model.upper_joint_limits[limit_idx] + + # Apply safety margin to position limits + pos_range = pos_max - pos_min + margin = pos_range * (1.0 - self.safety_margin) / 2.0 + + self.position_limits[joint_name] = { + "min": pos_min + margin, + "max": pos_max - margin, + } + except ValueError as e: + print(f"[JointSafetyMonitor] Warning: Could not find 'arms' joint group: {e}") + + # Get hand joint indices from robot model joint groups + try: + hand_indices = self.robot_model.get_joint_group_indices("hands") + hand_joint_names = [self.robot_model.joint_names[i] for i in hand_indices] + + for joint_name in hand_joint_names: + # Set velocity limits only for hands (no position limits for now) + vel_limit = self.HAND_VELOCITY_LIMIT * self.safety_margin + self.velocity_limits[joint_name] = {"min": -vel_limit, "max": vel_limit} + except ValueError as e: + print(f"[JointSafetyMonitor] Warning: Could not find 'hands' joint group: {e}") + + def check_safety(self, obs: Dict, action: Dict) -> Tuple[bool, List[Dict]]: + """Check if current velocities and positions are within safe bounds. + + Args: + obs: Observation dictionary containing joint positions and velocities + action: Action dictionary containing target positions + + Returns: + (is_safe, violations): Tuple of safety status and list of violations + Note: is_safe=False only for velocity violations (triggers shutdown) + Position violations are warnings only (don't affect is_safe) + """ + self.violations = [] + is_safe = True + joint_names = self.robot_model.joint_names + + # Check current joint velocities (critical - triggers shutdown) + if "dq" in obs: + joint_velocities = obs["dq"] + + for i, joint_name in enumerate(joint_names): + # Only check monitored joints + if joint_name not in self.velocity_limits: + continue + + if i < len(joint_velocities): + velocity = joint_velocities[i] + limits = self.velocity_limits[joint_name] + + if velocity < limits["min"] or velocity > limits["max"]: + violation = { + "joint": joint_name, + "type": "velocity", + "value": velocity, + "limit_min": limits["min"], + "limit_max": limits["max"], + "exceeded_by": self._calculate_exceeded_percentage( + velocity, limits["min"], limits["max"] + ), + "critical": True, # Velocity violations are critical + } + self.violations.append(violation) + is_safe = False + + # Check current joint positions (warning only - no shutdown) + if "q" in obs: + joint_positions = obs["q"] + + for i, joint_name in enumerate(joint_names): + # Only check joints with position limits (arms) + if joint_name not in self.position_limits: + continue + + if i < len(joint_positions): + position = joint_positions[i] + limits = self.position_limits[joint_name] + + if position < limits["min"] or position > limits["max"]: + violation = { + "joint": joint_name, + "type": "position", + "value": position, + "limit_min": limits["min"], + "limit_max": limits["max"], + "exceeded_by": self._calculate_exceeded_percentage( + position, limits["min"], limits["max"] + ), + "critical": False, # Position violations are warnings only + } + self.violations.append(violation) + # Don't set is_safe = False for position violations + + return is_safe, self.violations + + def _calculate_exceeded_percentage( + self, value: float, limit_min: float, limit_max: float + ) -> float: + """Calculate by how much percentage a value exceeds the limits.""" + if value < limit_min: + return abs((value - limit_min) / limit_min) * 100 + elif value > limit_max: + return abs((value - limit_max) / limit_max) * 100 + return 0.0 + + def get_safe_action(self, obs: Dict, original_action: Dict) -> Dict: + """Generate a safe action with startup ramping for smooth initialization. + + Args: + obs: Observation dictionary containing current joint positions + original_action: The original action that may cause violations + + Returns: + Safe action with startup ramping applied if within ramp duration + """ + safe_action = original_action.copy() + + # Handle startup ramping for arm joints + if not self.startup_complete: + if self.initial_positions is None and "q" in obs: + # Store initial positions from first observation + self.initial_positions = obs["q"].copy() + + if ( + self.startup_counter < self.ramp_duration_steps + and self.initial_positions is not None + and "q" in safe_action + ): + # Ramp factor: 0.0 at start → 1.0 at end + ramp_factor = self.startup_counter / self.ramp_duration_steps + + # Apply ramping only to monitored arm joints + for joint_name in self.velocity_limits: # Only monitored arm joints + if joint_name in self.robot_model.joint_to_dof_index: + joint_idx = self.robot_model.joint_to_dof_index[joint_name] + if joint_idx < len(safe_action["q"]) and joint_idx < len( + self.initial_positions + ): + initial_pos = self.initial_positions[joint_idx] + target_pos = original_action["q"][joint_idx] + # Linear interpolation: initial + ramp_factor * (target - initial) + safe_action["q"][joint_idx] = initial_pos + ramp_factor * ( + target_pos - initial_pos + ) + + # Increment counter for next iteration + self.startup_counter += 1 + else: + # Ramping complete - use original actions + self.startup_complete = True + + return safe_action + + def get_violation_report(self, violations: Optional[List[Dict]] = None) -> str: + """Generate a formatted error report for violations. + + Args: + violations: List of violations to report (uses self.violations if None) + + Returns: + Formatted error message string + """ + if violations is None: + violations = self.violations + + if not violations: + return "No violations detected." + + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] + + # Check if these are critical violations or warnings + critical_violations = [v for v in violations if v.get("critical", True)] + warning_violations = [v for v in violations if not v.get("critical", True)] + + if critical_violations and warning_violations: + report = f"Joint safety bounds exceeded!\nTimestamp: {timestamp}\nViolations:\n" + elif critical_violations: + report = f"Joint safety bounds exceeded!\nTimestamp: {timestamp}\nViolations:\n" + else: + report = f"Joint position warnings!\nTimestamp: {timestamp}\nWarnings:\n" + + for violation in violations: + joint = violation["joint"] + vtype = violation["type"] + value = violation["value"] + exceeded = violation["exceeded_by"] + limit_min = violation["limit_min"] + limit_max = violation["limit_max"] + + if vtype == "velocity": + report += f" - {joint}: {vtype}={value:.3f} rad/s " + report += f"(limit: ±{limit_max:.3f} rad/s) - " + report += f"EXCEEDED by {exceeded:.1f}%\n" + elif vtype == "position": + report += f" - {joint}: {vtype}={value:.3f} rad " + report += f"(limits: [{limit_min:.3f}, {limit_max:.3f}] rad) - " + report += f"EXCEEDED by {exceeded:.1f}%\n" + + # Add appropriate action message + if critical_violations: + report += "Action: Safe mode engaged (kp=0, tau=0). System shutdown initiated.\n" + report += "Please restart Docker container to resume operation." + else: + report += "Action: Position warning only. Robot continues operation." + + return report + + def handle_violations(self, obs: Dict, action: Dict) -> Dict: + """Check safety and handle violations appropriately. + + Args: + obs: Observation dictionary + action: Action dictionary + + Returns: + Dict with keys: + - 'safe_to_continue': bool - whether robot should continue operation + - 'action': Dict - potentially modified safe action + - 'shutdown_required': bool - whether system shutdown is needed + """ + is_safe, violations = self.check_safety(obs, action) + + # Apply startup ramping (always, regardless of violations) + safe_action = self.get_safe_action(obs, action) + + # Visualize arm and hand joint positions and velocities if enabled + if self.enable_viz: + if ( + self.right_arm_indices is not None + and self.left_arm_indices is not None + and self.right_hand_indices is not None + and self.left_hand_indices is not None + and "q" in obs + and "dq" in obs + and self.viz is not None + ): + try: + right_arm_positions = obs["q"][self.right_arm_indices] + left_arm_positions = obs["q"][self.left_arm_indices] + right_arm_velocities = obs["dq"][self.right_arm_indices] + left_arm_velocities = obs["dq"][self.left_arm_indices] + right_hand_positions = obs["q"][self.right_hand_indices] + left_hand_positions = obs["q"][self.left_hand_indices] + right_hand_velocities = obs["dq"][self.right_hand_indices] + left_hand_velocities = obs["dq"][self.left_hand_indices] + tensor_dict = { + self.right_arm_pos_key: right_arm_positions, + self.left_arm_pos_key: left_arm_positions, + self.right_arm_vel_key: right_arm_velocities, + self.left_arm_vel_key: left_arm_velocities, + self.right_hand_pos_key: right_hand_positions, + self.left_hand_pos_key: left_hand_positions, + self.right_hand_vel_key: right_hand_velocities, + self.left_hand_vel_key: left_hand_velocities, + } + self.viz.plot_tensors(tensor_dict, time.time()) + except Exception: + pass + + if not violations: + return {"safe_to_continue": True, "action": safe_action, "shutdown_required": False} + + # Separate critical (velocity) and warning (position) violations + critical_violations = [v for v in violations if v.get("critical", True)] + # warning_violations = [v for v in violations if not v.get('critical', True)] + + # Print warnings for position violations + # if warning_violations: + # warning_msg = self.get_violation_report(warning_violations) + # print(f"[SAFETY WARNING] {warning_msg}") + + # Handle critical violations (velocity) - trigger shutdown + if not is_safe and critical_violations: + error_msg = self.get_violation_report(critical_violations) + if self.env_type == "real": + print(f"[SAFETY VIOLATION] {error_msg}") + self.trigger_system_shutdown() + + return {"safe_to_continue": False, "action": safe_action, "shutdown_required": True} + + # Only position violations - continue with safe action + return {"safe_to_continue": True, "action": safe_action, "shutdown_required": False} + + def trigger_system_shutdown(self): + """Trigger system shutdown after safety violation.""" + print("\n[SAFETY] Initiating system shutdown due to safety violation...") + sys.exit(1) + + +def main(): + """Test the joint safety monitor with joint groups.""" + print("Testing joint safety monitor with joint groups...") + + try: + from gr00t_wbc.control.robot_model.instantiation.g1 import instantiate_g1_robot_model + + # Instantiate robot model + robot_model = instantiate_g1_robot_model() + print(f"Robot model created with {len(robot_model.joint_names)} joints") + + # Create safety monitor + safety_monitor = JointSafetyMonitor(robot_model) + print("Safety monitor created successfully!") + print(f"Monitoring {len(safety_monitor.velocity_limits)} joints") + + # Print monitored joints + print("\nVelocity limits:") + for joint_name, limits in safety_monitor.velocity_limits.items(): + print(f" - {joint_name}: ±{limits['max']:.2f} rad/s") + + print(f"\nPosition limits (arms only): {len(safety_monitor.position_limits)} joints") + for joint_name, limits in safety_monitor.position_limits.items(): + print(f" - {joint_name}: [{limits['min']:.3f}, {limits['max']:.3f}] rad") + + # Test safety checking with safe values + print("\n--- Testing Safety Checking ---") + + # Create mock observation with safe values + safe_obs = { + "q": np.zeros(robot_model.num_dofs), # All joints at zero position + "dq": np.zeros(robot_model.num_dofs), # All joints at zero velocity + } + safe_action = {"q": np.zeros(robot_model.num_dofs)} + + # Test handle_violations method + result = safety_monitor.handle_violations(safe_obs, safe_action) + print( + f"Safe values test: safe_to_continue={result['safe_to_continue']}, " + f"shutdown_required={result['shutdown_required']}" + ) + + # Test with unsafe velocity + unsafe_obs = safe_obs.copy() + unsafe_obs["dq"] = np.zeros(robot_model.num_dofs) + # Set left shoulder pitch velocity to exceed limit + left_shoulder_idx = robot_model.dof_index("left_shoulder_pitch_joint") + unsafe_obs["dq"][left_shoulder_idx] = 6.0 # Exceeds 5.0 rad/s limit + + print("\nUnsafe velocity test:") + result = safety_monitor.handle_violations(unsafe_obs, safe_action) + print( + f" safe_to_continue={result['safe_to_continue']}, shutdown_required={result['shutdown_required']}" + ) + + # Test with unsafe position only + unsafe_pos_obs = safe_obs.copy() + unsafe_pos_obs["q"] = np.zeros(robot_model.num_dofs) + # Set left shoulder pitch position to exceed limit + unsafe_pos_obs["q"][left_shoulder_idx] = -4.0 # Exceeds lower limit of -3.089 + + print("\nUnsafe position test:") + result = safety_monitor.handle_violations(unsafe_pos_obs, safe_action) + print( + f" safe_to_continue={result['safe_to_continue']}, shutdown_required={result['shutdown_required']}" + ) + + print("\nAll tests completed successfully!") + + except Exception as e: + print(f"Test failed with error: {e}") + import traceback + + traceback.print_exc() + + +if __name__ == "__main__": + main() diff --git a/gr00t_wbc/control/envs/g1/utils/state_processor.py b/gr00t_wbc/control/envs/g1/utils/state_processor.py new file mode 100644 index 0000000..f8c0548 --- /dev/null +++ b/gr00t_wbc/control/envs/g1/utils/state_processor.py @@ -0,0 +1,143 @@ +import time + +import numpy as np +from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import ( + MotionSwitcherClient, +) +from unitree_sdk2py.core.channel import ChannelSubscriber +from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_ as LowState_go +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import ( + HandState_, + IMUState_, + LowState_ as LowState_hg, + OdoState_, +) + + +class BodyStateProcessor: + def __init__(self, config): + self.config = config + + # Enter debug mode for real robot + if self.config["ENV_TYPE"] == "real": + msc = MotionSwitcherClient() + msc.SetTimeout(5.0) + msc.Init() + + status, result = msc.CheckMode() + print(status, result) + while result["name"]: + msc.ReleaseMode() + status, result = msc.CheckMode() + print(status, result) + time.sleep(1) + + if self.config["ROBOT_TYPE"] == "h1" or self.config["ROBOT_TYPE"] == "go2": + self.robot_lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_go) + self.robot_lowstate_subscriber.Init(None, 0) + self.robot_lowstate_subscriber.Init(None, 0) + elif ( + self.config["ROBOT_TYPE"] == "g1_29dof" + or self.config["ROBOT_TYPE"] == "h1-2_27dof" + or self.config["ROBOT_TYPE"] == "h1-2_21dof" + ): + self.robot_lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_hg) + self.robot_lowstate_subscriber.Init(None, 0) + + self.secondary_imu_subscriber = ChannelSubscriber("rt/secondary_imu", IMUState_) + self.secondary_imu_subscriber.Init(None, 0) + + # Subscribe to odo state (only available in simulation) + if self.config["ENV_TYPE"] == "sim": + self.odo_state_subscriber = ChannelSubscriber("rt/odostate", OdoState_) + self.odo_state_subscriber.Init(None, 0) + else: + raise NotImplementedError(f"Robot type {self.config['ROBOT_TYPE']} is not supported") + + self.num_dof = self.config["NUM_JOINTS"] + # 3 + 4 + 19 + self._init_q = np.zeros(3 + 4 + self.num_dof) + self.q = self._init_q + self.dq = np.zeros(3 + 3 + self.num_dof) + self.ddq = np.zeros(3 + 3 + self.num_dof) + self.tau_est = np.zeros(3 + 3 + self.num_dof) + self.torso_quat = np.zeros(4) + self.torso_ang_vel = np.zeros(3) + self.temp_first = np.zeros(self.num_dof) + self.temp_second = np.zeros(self.num_dof) + self.robot_low_state = None + self.secondary_imu_state = None + self.odo_state = None + + def _prepare_low_state(self) -> np.ndarray: + self.robot_low_state = self.robot_lowstate_subscriber.Read() + self.secondary_imu_state = self.secondary_imu_subscriber.Read() + + if not self.robot_low_state: + print("No low state received") + return + imu_state = self.robot_low_state.imu_state + + # Use odo_state for position and velocity if available, otherwise set to zero + if self.config["ENV_TYPE"] == "sim": + self.odo_state = self.odo_state_subscriber.Read() + self.q[0:3] = self.odo_state.position + self.dq[0:3] = self.odo_state.linear_velocity + else: + self.q[0:3] = [0.0, 0.0, 0.0] + self.dq[0:3] = [0.0, 0.0, 0.0] + + self.q[3:7] = imu_state.quaternion # w, x, y, z + self.dq[3:6] = imu_state.gyroscope + self.ddq[0:3] = imu_state.accelerometer + unitree_joint_state = self.robot_low_state.motor_state + self.torso_quat = self.secondary_imu_state.quaternion + self.torso_ang_vel = self.secondary_imu_state.gyroscope + + for i in range(self.num_dof): + self.q[7 + i] = unitree_joint_state[self.config["JOINT2MOTOR"][i]].q + self.dq[6 + i] = unitree_joint_state[self.config["JOINT2MOTOR"][i]].dq + self.tau_est[6 + i] = unitree_joint_state[self.config["JOINT2MOTOR"][i]].tau_est + + robot_state_data = np.concatenate( + [self.q, self.dq, self.tau_est, self.ddq, self.torso_quat, self.torso_ang_vel], axis=0 + ).reshape(1, -1) + # (7 + 29) + (6 + 29) + (6 + 29) + (6 + 29) = 141 dim + + return robot_state_data + + +class HandStateProcessor: + def __init__(self, is_left: bool = True): + self.is_left = is_left + if self.is_left: + self.state_sub = ChannelSubscriber("rt/dex3/left/state", HandState_) + else: + self.state_sub = ChannelSubscriber("rt/dex3/right/state", HandState_) + + self.state_sub.Init(None, 0) + self.state_sub.Init(None, 0) + self.state = None + self.num_dof = 7 # for single hand + + def _prepare_low_state(self) -> np.ndarray: + self.state = self.state_sub.Read() + + if not self.state: + print("No state received") + return + + state_data = ( + np.concatenate( + [ + [self.state.motor_state[i].q for i in range(self.num_dof)], + [self.state.motor_state[i].dq for i in range(self.num_dof)], + [self.state.motor_state[i].tau_est for i in range(self.num_dof)], + [self.state.motor_state[i].ddq for i in range(self.num_dof)], + ], + axis=0, + ) + .astype(np.float64) + .reshape(1, -1) + ) + return state_data diff --git a/gr00t_wbc/control/envs/robocasa/__init__.py b/gr00t_wbc/control/envs/robocasa/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gr00t_wbc/control/envs/robocasa/async_env_server.py b/gr00t_wbc/control/envs/robocasa/async_env_server.py new file mode 100644 index 0000000..b2bd746 --- /dev/null +++ b/gr00t_wbc/control/envs/robocasa/async_env_server.py @@ -0,0 +1,303 @@ +from abc import abstractmethod +import threading +import time +from typing import Any, Dict, Tuple + +import mujoco +import numpy as np +import rclpy + +from gr00t_wbc.control.envs.g1.sim.image_publish_utils import ImagePublishProcess +from gr00t_wbc.control.envs.robocasa.utils.robocasa_env import Gr00tLocomanipRoboCasaEnv # noqa: F401 +from gr00t_wbc.control.robot_model.robot_model import RobotModel +from gr00t_wbc.control.utils.keyboard_dispatcher import KeyboardListenerSubscriber + + +class RoboCasaEnvServer: + """ + This class is responsible for running the simulation environment loop in a separate thread. + It communicates with the main thread via the `publish_obs` and `get_action` methods through `channel_bridge`. + It will also handle the viewer sync when `onscreen` is True. + """ + + def __init__( + self, + env_name: str, + robot_name: str, + robot_model: RobotModel, + env_kwargs: Dict[str, Any], + **kwargs, + ): + # initialize environment + if env_kwargs.get("onscreen", False): + env_kwargs["onscreen"] = False + self.onscreen = True # onscreen render in the main thread + self.render_camera = env_kwargs.get("render_camera", None) + else: + self.onscreen = False + self.env_name = env_name + self.env = Gr00tLocomanipRoboCasaEnv(env_name, robot_name, robot_model, **env_kwargs) + self.init_caches() + self.cache_lock = threading.Lock() + + # initialize channel + self.init_channel() + + # initialize ROS2 node + if not rclpy.ok(): + rclpy.init() + self.node = rclpy.create_node("sim_robocasa") + self.thread = threading.Thread(target=rclpy.spin, args=(self.node,), daemon=True) + self.thread.start() + else: + self.thread = None + executor = rclpy.get_global_executor() + self.node = executor.get_nodes()[0] # will only take the first node + + self.control_freq = env_kwargs.get("control_freq", 1 / 0.02) + self.sim_freq = kwargs.get("sim_freq", 1 / 0.005) + self.control_rate = self.node.create_rate(self.control_freq) + + self.running = False + self.sim_thread = None + self.sync_lock = threading.Lock() + + self.sync_mode = kwargs.get("sync_mode", False) + self.steps_per_action = kwargs.get("steps_per_action", 1) + + self.image_dt = kwargs.get("image_dt", 0.04) + self.image_publish_process = None + self.viewer_freq = kwargs.get("viewer_freq", 1 / 0.02) + self.viewer = None + + self.verbose = kwargs.get("verbose", True) + + # Initialize keyboard listener for env reset + self.keyboard_listener = KeyboardListenerSubscriber() + + self.reset() + + @property + def base_env(self): + return self.env.env + + def start_image_publish_subprocess(self, start_method: str = "spawn", camera_port: int = 5555): + """Initialize image publishing subprocess if cameras are configured""" + if len(self.env.camera_names) == 0: + print( + "Warning: No camera configs provided, image publishing subprocess will not be started" + ) + return + + # Build camera configs from env camera settings + camera_configs = {} + for env_cam_name in self.env.camera_names: + camera_config = self.env.camera_key_mapper.get_camera_config(env_cam_name) + mapped_cam_name, cam_width, cam_height = camera_config + camera_configs[mapped_cam_name] = {"height": cam_height, "width": cam_width} + + self.image_publish_process = ImagePublishProcess( + camera_configs=camera_configs, + image_dt=self.image_dt, + zmq_port=camera_port, + start_method=start_method, + verbose=self.verbose, + ) + + self.image_publish_process.start_process() + + def update_render_caches(self, obs: Dict[str, Any]): + """Update render cache and shared memory for subprocess""" + if self.image_publish_process is None: + return + + # Extract image observations from obs dict + render_caches = { + k: v for k, v in obs.items() if k.endswith("_image") and isinstance(v, np.ndarray) + } + + # Update shared memory if image publishing process is available + if render_caches: + self.image_publish_process.update_shared_memory(render_caches) + + def init_caches(self): + self.caches = { + "obs": None, + "reward": None, + "terminated": None, + "truncated": None, + "info": None, + } + + def reset(self, **kwargs): + if self.viewer is not None: + self.viewer.close() + + obs, info = self.env.reset(**kwargs) + self.caches["obs"] = obs + self.caches["reward"] = 0 + self.caches["terminated"] = False + self.caches["truncated"] = False + self.caches["info"] = info + + # initialize viewer + if self.onscreen: + self.viewer = mujoco.viewer.launch_passive( + self.base_env.sim.model._model, + self.base_env.sim.data._data, + show_left_ui=False, + show_right_ui=False, + ) + self.viewer.opt.geomgroup[0] = 0 # disable collision visualization + if self.render_camera is not None: + self.viewer.cam.type = mujoco.mjtCamera.mjCAMERA_FIXED + self.viewer.cam.fixedcamid = self.base_env.sim.model._model.cam( + self.render_camera + ).id + + # self.episode_state.reset_state() + return obs, info + + @abstractmethod + def init_channel(self): + raise NotImplementedError("init_channel must be implemented by the subclass") + + @abstractmethod + def publish_obs(self): + raise NotImplementedError("publish_obs must be implemented by the subclass") + + @abstractmethod + def get_action(self) -> Tuple[Dict[str, Any], bool, bool]: + raise NotImplementedError("get_action must be implemented by the subclass") + + def start_as_thread(self): + """Start the simulation thread""" + if self.sim_thread is not None and self.sim_thread.is_alive(): + return + + self.sim_thread = threading.Thread(target=self.start) + self.sim_thread.daemon = True + self.sim_thread.start() + + def set_sync_mode(self, sync_mode: bool, steps_per_action: int = 4): + """Set the sync mode of the environment server""" + with self.sync_lock: + self.sync_mode = sync_mode + self.steps_per_action = steps_per_action + + def _check_keyboard_input(self): + """Check for keyboard input and handle state transitions""" + key = self.keyboard_listener.read_msg() + if key == "k": + print("\033[1;32m[Sim env]\033[0m Resetting sim environment") + self.reset() + + def start(self): + """Function executed by the simulation thread""" + iter_idx = 0 + steps_per_cur_action = 0 + t_start = time.monotonic() + + self.running = True + + while self.running: + # Check keyboard input for state transitions + self._check_keyboard_input() + + # Publish observations and get new action + self.publish_obs() + action, ready, is_new_action = self.get_action() + # ready is True if the action is received from the control loop + # is_new_action is True if the action is new (not the same as the previous action) + with self.sync_lock: + sync_mode = self.sync_mode + max_steps_per_action = self.steps_per_action + + # Process action if ready and within step limits + action_should_apply = ready and ( + (not sync_mode) or steps_per_cur_action < max_steps_per_action + ) + if action_should_apply: + obs, reward, terminated, truncated, info = self.env.step(action) + with self.cache_lock: + self.caches["obs"] = obs + self.caches["reward"] = reward + self.caches["terminated"] = terminated + self.caches["truncated"] = truncated + self.caches["info"] = info + + if reward == 1.0 and iter_idx % 50 == 0: + print("\033[92mTask successful. Can save data now.\033[0m") + + iter_idx += 1 + steps_per_cur_action += 1 + if self.verbose and sync_mode: + print("steps_per_cur_action: ", steps_per_cur_action) + + # Update render caches at image publishing rate + if action_should_apply and iter_idx % int(self.image_dt * self.control_freq) == 0: + with self.cache_lock: + obs_copy = self.caches["obs"].copy() + self.update_render_caches(obs_copy) + + # Reset step counter for new actions + if is_new_action: + steps_per_cur_action = 0 + + # Update viewer at specified frequency + if self.onscreen and iter_idx % (self.control_freq / self.viewer_freq) == 0: + self.viewer.sync() + + # Check if we're meeting the desired control frequency + if iter_idx % 100 == 0: + end_time = time.monotonic() + if self.verbose: + print( + f"sim FPS: {100.0 / (end_time - t_start) * (self.sim_freq / self.control_freq)}" + ) + if (end_time - t_start) > ((110.0 / self.control_freq)): # for tolerance + print( + f"Warning: Sim runs at " + "{100.0/(end_time - t_start) * (self.sim_freq / self.control_freq):.1f}Hz, " + f"but should run at {self.sim_freq:.1f}Hz" + ) + t_start = end_time + + # reset obj pos every 200 steps + if iter_idx % 200 == 0: + if hasattr(self.base_env, "reset_obj_pos"): + self.base_env.reset_obj_pos() + + self.control_rate.sleep() + + def get_privileged_obs(self): + """Get privileged observation. Should be implemented by subclasses.""" + obs = {} + with self.cache_lock: + if hasattr(self.base_env, "get_privileged_obs_keys"): + for key in self.base_env.get_privileged_obs_keys(): + obs[key] = self.caches["obs"][key] + + for key in self.caches["obs"].keys(): + if key.endswith("_image"): + obs[key] = self.caches["obs"][key] + + return obs + + def stop(self): + """Stop the simulation thread""" + self.running = False + if self.sim_thread is not None: + self.sim_thread.join(timeout=1.0) # Wait for thread to finish with timeout + self.sim_thread = None + + def close(self): + self.stop() + if self.image_publish_process is not None: + self.image_publish_process.stop() + if self.onscreen: + self.viewer.close() + self.env.close() + + def get_reward(self): + return self.base_env.reward() diff --git a/gr00t_wbc/control/envs/robocasa/sync_env.py b/gr00t_wbc/control/envs/robocasa/sync_env.py new file mode 100644 index 0000000..bdfe7a2 --- /dev/null +++ b/gr00t_wbc/control/envs/robocasa/sync_env.py @@ -0,0 +1,584 @@ +import sys +from typing import Any, Dict, Tuple + +import gymnasium as gym +from gymnasium.envs.registration import register +import numpy as np +from robocasa.environments.locomanipulation import REGISTERED_LOCOMANIPULATION_ENVS +from robocasa.models.robots import GR00T_LOCOMANIP_ENVS_ROBOTS +from robosuite.environments.robot_env import RobotEnv +from scipy.spatial.transform import Rotation as R + +from gr00t_wbc.control.envs.g1.utils.joint_safety import JointSafetyMonitor +from gr00t_wbc.control.envs.robocasa.utils.controller_utils import update_robosuite_controller_configs +from gr00t_wbc.control.envs.robocasa.utils.robocasa_env import ( # noqa: F401 + ALLOWED_LANGUAGE_CHARSET, + Gr00tLocomanipRoboCasaEnv, +) +from gr00t_wbc.control.robot_model.instantiation import get_robot_type_and_model +from gr00t_wbc.control.utils.n1_utils import ( + prepare_gym_space_for_eval, + prepare_observation_for_eval, +) +from gr00t_wbc.data.constants import RS_VIEW_CAMERA_HEIGHT, RS_VIEW_CAMERA_WIDTH + + +class SyncEnv(gym.Env): + MAX_MUJOCO_STATE_LEN = 800 + + def __init__(self, env_name, robot_name, **kwargs): + self.env_name = env_name + self.robot_name = robot_name + self.onscreen = kwargs.get("onscreen", True) + self.enable_gravity_compensation = kwargs.pop("enable_gravity_compensation", False) + self.gravity_compensation_joints = kwargs.pop("gravity_compensation_joints", ["arms"]) + _, self.robot_model = get_robot_type_and_model( + robot_name, enable_waist_ik=kwargs.pop("enable_waist", False) + ) + + env_kwargs = { + "onscreen": kwargs.get("onscreen", True), + "offscreen": kwargs.get("offscreen", False), + "renderer": kwargs.get("renderer", "mjviewer"), + "render_camera": kwargs.get("render_camera", "frontview"), + "camera_names": kwargs.get("camera_names", ["frontview"]), + "camera_heights": kwargs.get("camera_heights", None), + "camera_widths": kwargs.get("camera_widths", None), + "controller_configs": kwargs["controller_configs"], + "control_freq": kwargs.get("control_freq", 50), + "translucent_robot": kwargs.get("translucent_robot", True), + "ik_indicator": kwargs.get("ik_indicator", False), + "randomize_cameras": kwargs.get("randomize_cameras", True), + } + self.env = Gr00tLocomanipRoboCasaEnv( + env_name, robot_name, robot_model=self.robot_model, **env_kwargs + ) + self.init_cache() + + self.reset() + + @property + def base_env(self) -> RobotEnv: + return self.env.env + + def overwrite_floating_base_action(self, navigate_cmd): + if self.base_env.robots[0].robot_model.default_base == "FloatingLeggedBase": + self.env.unwrapped.overridden_floating_base_action = navigate_cmd + + def get_mujoco_state_info(self): + mujoco_state = self.base_env.sim.get_state().flatten() + assert len(mujoco_state) < SyncEnv.MAX_MUJOCO_STATE_LEN + padding_width = SyncEnv.MAX_MUJOCO_STATE_LEN - len(mujoco_state) + padded_mujoco_state = np.pad( + mujoco_state, (0, padding_width), mode="constant", constant_values=0 + ) + max_mujoco_state_len = SyncEnv.MAX_MUJOCO_STATE_LEN + mujoco_state_len = len(mujoco_state) + mujoco_state = padded_mujoco_state.copy() + return max_mujoco_state_len, mujoco_state_len, mujoco_state + + def reset_to(self, state: Dict[str, Any]) -> Dict[str, Any] | None: + if hasattr(self.base_env, "reset_to"): + self.base_env.reset_to(state) + else: + # todo: maybe update robosuite to have reset_to() + env = self.base_env + if "model_file" in state: + xml = env.edit_model_xml(state["model_file"]) + env.reset_from_xml_string(xml) + env.sim.reset() + if "states" in state: + env.sim.set_state_from_flattened(state["states"]) + env.sim.forward() + + obs = self.env.force_update_observation(timestep=0) + self.cache["obs"] = obs + return + + def get_state(self) -> Dict[str, Any]: + return self.base_env.get_state() + + def is_success(self): + """ + Check if the task condition(s) is reached. Should return a dictionary + { str: bool } with at least a "task" key for the overall task success, + and additional optional keys corresponding to other task criteria. + """ + # First, try to use the base environment's is_success method if it exists + if hasattr(self.base_env, "is_success"): + return self.base_env.is_success() + + # Fall back to using _check_success if available + elif hasattr(self.base_env, "_check_success"): + succ = self.base_env._check_success() + if isinstance(succ, dict): + assert "task" in succ + return succ + return {"task": succ} + + # If neither method exists, return failure + else: + return {"task": False} + + def init_cache(self): + self.cache = { + "obs": None, + "reward": None, + "terminated": None, + "truncated": None, + "info": None, + } + + def reset(self, seed=None, options=None) -> Tuple[Dict[str, any], Dict[str, any]]: + self.init_cache() + obs, info = self.env.reset(seed=seed, options=options) + self.cache["obs"] = obs + self.cache["reward"] = 0 + self.cache["terminated"] = False + self.cache["truncated"] = False + self.cache["info"] = info + return self.observe(), info + + def observe(self) -> Dict[str, any]: + # Get observations from body and hands + assert ( + self.cache["obs"] is not None + ), "Observation cache is not initialized, please reset the environment first" + raw_obs = self.cache["obs"] + + # Body and hand joint measurements come in actuator order, so we need to convert them to joint order + whole_q = self.robot_model.get_configuration_from_actuated_joints( + body_actuated_joint_values=raw_obs["body_q"], + left_hand_actuated_joint_values=raw_obs["left_hand_q"], + right_hand_actuated_joint_values=raw_obs["right_hand_q"], + ) + whole_dq = self.robot_model.get_configuration_from_actuated_joints( + body_actuated_joint_values=raw_obs["body_dq"], + left_hand_actuated_joint_values=raw_obs["left_hand_dq"], + right_hand_actuated_joint_values=raw_obs["right_hand_dq"], + ) + whole_ddq = self.robot_model.get_configuration_from_actuated_joints( + body_actuated_joint_values=raw_obs["body_ddq"], + left_hand_actuated_joint_values=raw_obs["left_hand_ddq"], + right_hand_actuated_joint_values=raw_obs["right_hand_ddq"], + ) + whole_tau_est = self.robot_model.get_configuration_from_actuated_joints( + body_actuated_joint_values=raw_obs["body_tau_est"], + left_hand_actuated_joint_values=raw_obs["left_hand_tau_est"], + right_hand_actuated_joint_values=raw_obs["right_hand_tau_est"], + ) + eef_obs = self.get_eef_obs(whole_q) + + obs = { + "q": whole_q, + "dq": whole_dq, + "ddq": whole_ddq, + "tau_est": whole_tau_est, + "floating_base_pose": raw_obs["floating_base_pose"], + "floating_base_vel": raw_obs["floating_base_vel"], + "floating_base_acc": raw_obs["floating_base_acc"], + "wrist_pose": np.concatenate([eef_obs["left_wrist_pose"], eef_obs["right_wrist_pose"]]), + } + + # Add state keys for model input + obs = prepare_observation_for_eval(self.robot_model, obs) + + obs["language.language_instruction"] = raw_obs["language.language_instruction"] + + if hasattr(self.base_env, "get_privileged_obs_keys"): + for key in self.base_env.get_privileged_obs_keys(): + obs[key] = raw_obs[key] + + for key in raw_obs.keys(): + if key.endswith("_image"): + obs[key] = raw_obs[key] + # TODO: add video.key without _image suffix for evaluation, convert to uint8, remove later + obs[f"video.{key.replace('_image', '')}"] = raw_obs[key] + return obs + + def step( + self, action: Dict[str, any] + ) -> Tuple[Dict[str, any], float, bool, bool, Dict[str, any]]: + self.queue_action(action) + return self.get_step_info() + + def get_observation(self): + return self.base_env._get_observations() # assumes base env is robosuite + + def get_step_info(self) -> Dict[str, any]: + return ( + self.observe(), + self.cache["reward"], + self.cache["terminated"], + self.cache["truncated"], + self.cache["info"], + ) + + def convert_q_to_actuated_joint_order(self, q: np.ndarray) -> np.ndarray: + body_q = self.robot_model.get_body_actuated_joints(q) + left_hand_q = self.robot_model.get_hand_actuated_joints(q, side="left") + right_hand_q = self.robot_model.get_hand_actuated_joints(q, side="right") + whole_q = np.zeros_like(q) + whole_q[self.robot_model.get_joint_group_indices("body")] = body_q + whole_q[self.robot_model.get_joint_group_indices("left_hand")] = left_hand_q + whole_q[self.robot_model.get_joint_group_indices("right_hand")] = right_hand_q + + return whole_q + + def set_ik_indicator(self, teleop_cmd): + """Set the IK indicators for the simulator""" + if "left_wrist" in teleop_cmd and "right_wrist" in teleop_cmd: + left_wrist_input_pose = teleop_cmd["left_wrist"] + right_wrist_input_pose = teleop_cmd["right_wrist"] + ik_wrapper = self.base_env + ik_wrapper.set_target_poses_outside_env([left_wrist_input_pose, right_wrist_input_pose]) + + def render(self): + if self.base_env.viewer is not None: + self.base_env.viewer.update() + if self.onscreen: + self.base_env.render() + + def queue_action(self, action: Dict[str, any]): + # action is in pinocchio joint order, we need to convert it to actuator order + action_q = self.convert_q_to_actuated_joint_order(action["q"]) + + # Compute gravity compensation torques if enabled + tau_q = np.zeros_like(action_q) + if self.enable_gravity_compensation and self.robot_model is not None: + try: + # Get current robot configuration from cache (more efficient than observe()) + raw_obs = self.cache["obs"] + + # Convert from actuated joint order to joint order for Pinocchio + current_q_joint_order = self.robot_model.get_configuration_from_actuated_joints( + body_actuated_joint_values=raw_obs["body_q"], + left_hand_actuated_joint_values=raw_obs["left_hand_q"], + right_hand_actuated_joint_values=raw_obs["right_hand_q"], + ) + + # Compute gravity compensation in joint order using current robot configuration + gravity_torques_joint_order = self.robot_model.compute_gravity_compensation_torques( + current_q_joint_order, joint_groups=self.gravity_compensation_joints + ) + + # Convert gravity torques to actuated joint order + gravity_torques_actuated = self.convert_q_to_actuated_joint_order( + gravity_torques_joint_order + ) + + # Add gravity compensation to torques + tau_q += gravity_torques_actuated + + except Exception as e: + print(f"Error applying gravity compensation in sync_env: {e}") + + obs, reward, terminated, truncated, info = self.env.step({"q": action_q, "tau": tau_q}) + self.cache["obs"] = obs + self.cache["reward"] = reward + self.cache["terminated"] = terminated + self.cache["truncated"] = truncated + self.cache["info"] = info + + def queue_state(self, state: Dict[str, any]): + # This function is for debugging or cross-playback between sim and real only. + state_q = self.convert_q_to_actuated_joint_order(state["q"]) + obs, reward, terminated, truncated, info = self.env.unwrapped.step_only_kinematics( + {"q": state_q} + ) + self.cache["obs"] = obs + self.cache["reward"] = reward + self.cache["terminated"] = terminated + self.cache["truncated"] = truncated + self.cache["info"] = info + + @property + def observation_space(self) -> gym.Space: + # @todo: check if the low and high bounds are correct for body_obs. + q_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(self.robot_model.num_dofs,)) + dq_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(self.robot_model.num_dofs,)) + ddq_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(self.robot_model.num_dofs,)) + tau_est_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(self.robot_model.num_dofs,)) + floating_base_pose_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(7,)) + floating_base_vel_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(6,)) + floating_base_acc_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(6,)) + wrist_pose_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(7 + 7,)) + + obs_space = gym.spaces.Dict( + { + "floating_base_pose": floating_base_pose_space, + "floating_base_vel": floating_base_vel_space, + "floating_base_acc": floating_base_acc_space, + "q": q_space, + "dq": dq_space, + "ddq": ddq_space, + "tau_est": tau_est_space, + "wrist_pose": wrist_pose_space, + } + ) + + obs_space = prepare_gym_space_for_eval(self.robot_model, obs_space) + + obs_space["language.language_instruction"] = gym.spaces.Text( + max_length=256, charset=ALLOWED_LANGUAGE_CHARSET + ) + + if hasattr(self.base_env, "get_privileged_obs_keys"): + for key, shape in self.base_env.get_privileged_obs_keys().items(): + space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=shape) + obs_space[key] = space + + robocasa_obs_space = self.env.observation_space + for key in robocasa_obs_space.keys(): + if key.endswith("_image"): + space = gym.spaces.Box( + low=-np.inf, high=np.inf, shape=robocasa_obs_space[key].shape + ) + obs_space[key] = space + # TODO: add video.key without _image suffix for evaluation, remove later + space_uint = gym.spaces.Box(low=0, high=255, shape=space.shape, dtype=np.uint8) + obs_space[f"video.{key.replace('_image', '')}"] = space_uint + + return obs_space + + def reset_obj_pos(self): + # For Tairan's goal-reaching task, a hacky way to reset the object position is needed. + if hasattr(self.base_env, "reset_obj_pos"): + self.base_env.reset_obj_pos() + + @property + def action_space(self) -> gym.Space: + return self.env.action_space + + def close(self): + self.env.close() + + def __repr__(self): + return ( + f"SyncEnv(env_name={self.env_name}, \n" + f" observation_space={self.observation_space}, \n" + f" action_space={self.action_space})" + ) + + def get_joint_gains(self): + controller = self.base_env.robots[0].composite_controller + + gains = {} + key_mapping = { + "left": "left_arm", + "right": "right_arm", + "legs": "legs", + "torso": "waist", + "head": "neck", + } + for k in controller.part_controllers.keys(): + if hasattr(controller.part_controllers[k], "kp"): + if k in key_mapping: + gains[key_mapping[k]] = controller.part_controllers[k].kp + else: + gains[k] = controller.part_controllers[k].kp + gains.update( + { + "left_hand": self.base_env.sim.model.actuator_gainprm[ + self.base_env.robots[0]._ref_actuators_indexes_dict["left_gripper"], 0 + ], + "right_hand": self.base_env.sim.model.actuator_gainprm[ + self.base_env.robots[0]._ref_actuators_indexes_dict["right_gripper"], 0 + ], + } + ) + joint_gains = np.zeros(self.robot_model.num_dofs) + for k in gains.keys(): + joint_gains[self.robot_model.get_joint_group_indices(k)] = gains[k] + return joint_gains + + def get_joint_damping(self): + controller = self.base_env.robots[0].composite_controller + damping = {} + key_mapping = { + "left": "left_arm", + "right": "right_arm", + "legs": "legs", + "torso": "waist", + "head": "neck", + } + for k in controller.part_controllers.keys(): + if hasattr(controller.part_controllers[k], "kd"): + if k in key_mapping: + damping[key_mapping[k]] = controller.part_controllers[k].kd + else: + damping[k] = controller.part_controllers[k].kd + damping.update( + { + "left_hand": -self.base_env.sim.model.actuator_biasprm[ + self.base_env.robots[0]._ref_actuators_indexes_dict["left_gripper"], 2 + ], + "right_hand": -self.base_env.sim.model.actuator_biasprm[ + self.base_env.robots[0]._ref_actuators_indexes_dict["right_gripper"], 2 + ], + } + ) + joint_damping = np.zeros(self.robot_model.num_dofs) + for k in damping.keys(): + joint_damping[self.robot_model.get_joint_group_indices(k)] = damping[k] + return joint_damping + + def get_eef_obs(self, q: np.ndarray) -> Dict[str, np.ndarray]: + self.robot_model.cache_forward_kinematics(q) + eef_obs = {} + for side in ["left", "right"]: + wrist_placement = self.robot_model.frame_placement( + self.robot_model.supplemental_info.hand_frame_names[side] + ) + wrist_pos, wrist_quat = wrist_placement.translation[:3], R.from_matrix( + wrist_placement.rotation + ).as_quat(scalar_first=True) + eef_obs[f"{side}_wrist_pose"] = np.concatenate([wrist_pos, wrist_quat]) + + return eef_obs + + +class G1SyncEnv(SyncEnv): + def __init__( + self, + env_name, + robot_name, + **kwargs, + ): + renderer = kwargs.get("renderer", "mjviewer") + if renderer == "mjviewer": + default_render_camera = ["robot0_oak_egoview"] + elif renderer in ["mujoco", "rerun"]: + default_render_camera = [ + "robot0_oak_egoview", + "robot0_oak_left_monoview", + "robot0_oak_right_monoview", + ] + else: + raise NotImplementedError + default_camera_names = [ + "robot0_oak_egoview", + "robot0_oak_left_monoview", + "robot0_oak_right_monoview", + ] + default_camera_heights = [ + RS_VIEW_CAMERA_HEIGHT, + RS_VIEW_CAMERA_HEIGHT, + RS_VIEW_CAMERA_HEIGHT, + ] + default_camera_widths = [ + RS_VIEW_CAMERA_WIDTH, + RS_VIEW_CAMERA_WIDTH, + RS_VIEW_CAMERA_WIDTH, + ] + + kwargs.update( + { + "onscreen": kwargs.get("onscreen", True), + "offscreen": kwargs.get("offscreen", False), + "render_camera": kwargs.get("render_camera", default_render_camera), + "camera_names": kwargs.get("camera_names", default_camera_names), + "camera_heights": kwargs.get("camera_heights", default_camera_heights), + "camera_widths": kwargs.get("camera_widths", default_camera_widths), + "translucent_robot": kwargs.get("translucent_robot", False), + } + ) + super().__init__(env_name=env_name, robot_name=robot_name, **kwargs) + + # Initialize safety monitor (visualization disabled) - G1 specific + self.safety_monitor = JointSafetyMonitor( + self.robot_model, + enable_viz=False, + env_type="sim", # G1SyncEnv is always simulation + ) + self.safety_monitor.ramp_duration_steps = 0 + self.safety_monitor.startup_complete = True + self.safety_monitor.LOWER_BODY_VELOCITY_LIMIT = ( + 1e10 # disable lower body velocity limits since it impacts WBC + ) + self.last_safety_ok = True # Track last safety status from queue_action + + @property + def observation_space(self): + obs_space = super().observation_space + obs_space["torso_quat"] = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(4,)) + obs_space["torso_ang_vel"] = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(3,)) + return obs_space + + def observe(self): + obs = super().observe() + obs["torso_quat"] = self.cache["obs"]["secondary_imu_quat"] + obs["torso_ang_vel"] = self.cache["obs"]["secondary_imu_vel"][3:6] + return obs + + def queue_action(self, action: Dict[str, any]): + # Safety check before queuing action + obs = self.observe() + safety_result = self.safety_monitor.handle_violations(obs, action) + action = safety_result["action"] + # Save safety status for efficient access + self.last_safety_ok = not safety_result.get("shutdown_required", False) + # Check if shutdown is required + if safety_result["shutdown_required"]: + self.safety_monitor.trigger_system_shutdown() + + # Call parent queue_action with potentially modified action + super().queue_action(action) + + def get_joint_safety_status(self) -> bool: + """Get current joint safety status from the last queue_action safety check. + + Returns: + bool: True if joints are safe (no shutdown required), False if unsafe + """ + return self.last_safety_ok + + +def create_gym_sync_env_class(env, robot, robot_alias, wbc_version): + class_name = f"{env}_{robot}_{wbc_version}" + id_name = f"gr00tlocomanip_{robot_alias}/{class_name}" + + if robot_alias.startswith("g1"): + env_class_type = G1SyncEnv + elif robot_alias.startswith("gr1"): + env_class_type = globals().get("GR1SyncEnv", SyncEnv) + else: + env_class_type = SyncEnv + + controller_configs = update_robosuite_controller_configs( + robot=robot, + wbc_version=wbc_version, + ) + + env_class_type = type( + class_name, + (env_class_type,), + { + "__init__": lambda self, **kwargs: super(self.__class__, self).__init__( + env_name=env, + robot_name=robot, + controller_configs=controller_configs, + **kwargs, + ) + }, + ) + + current_module = sys.modules["gr00t_wbc.control.envs.robocasa.sync_env"] + setattr(current_module, class_name, env_class_type) + register( + id=id_name, # Unique ID for the environment + entry_point=f"gr00t_wbc.control.envs.robocasa.sync_env:{class_name}", + ) + + +WBC_VERSION = "gear_wbc" + +for ENV in REGISTERED_LOCOMANIPULATION_ENVS: + for ROBOT, ROBOT_ALIAS in GR00T_LOCOMANIP_ENVS_ROBOTS.items(): + create_gym_sync_env_class(ENV, ROBOT, ROBOT_ALIAS, WBC_VERSION) + + +if __name__ == "__main__": + + env = gym.make("gr00tlocomanip_g1_sim/PnPBottle_g1_gear_wbc") + print(env.observation_space) diff --git a/gr00t_wbc/control/envs/robocasa/utils/__init__.py b/gr00t_wbc/control/envs/robocasa/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gr00t_wbc/control/envs/robocasa/utils/cam_key_converter.py b/gr00t_wbc/control/envs/robocasa/utils/cam_key_converter.py new file mode 100644 index 0000000..1c1aeaa --- /dev/null +++ b/gr00t_wbc/control/envs/robocasa/utils/cam_key_converter.py @@ -0,0 +1,73 @@ +from dataclasses import dataclass +from typing import Dict, Optional, Tuple + +from gr00t_wbc.data.constants import RS_VIEW_CAMERA_HEIGHT, RS_VIEW_CAMERA_WIDTH + + +@dataclass +class CameraConfig: + width: int + height: int + mapped_key: str + + +class CameraKeyMapper: + def __init__(self): + # Default camera dimensions + self.default_width = RS_VIEW_CAMERA_WIDTH + self.default_height = RS_VIEW_CAMERA_HEIGHT + + # Camera key mapping with custom dimensions + self.camera_configs: Dict[str, CameraConfig] = { + # GR1 + "egoview": CameraConfig(self.default_width, self.default_height, "ego_view"), + "frontview": CameraConfig(self.default_width, self.default_height, "front_view"), + # G1 + "robot0_rs_egoview": CameraConfig(self.default_width, self.default_height, "ego_view"), + "robot0_rs_tppview": CameraConfig(self.default_width, self.default_height, "tpp_view"), + "robot0_oak_egoview": CameraConfig(self.default_width, self.default_height, "ego_view"), + "robot0_oak_left_monoview": CameraConfig( + self.default_width, self.default_height, "ego_view_left_mono" + ), + "robot0_oak_right_monoview": CameraConfig( + self.default_width, self.default_height, "ego_view_right_mono" + ), + } + + def get_camera_config(self, key: str) -> Optional[Tuple[str, int, int]]: + """ + Get the mapped camera key and dimensions for a given camera key. + + Args: + key: The input camera key + + Returns: + Tuple of (mapped_key, width, height) if key exists, None otherwise + """ + config = self.camera_configs.get(key.lower()) + if config is None: + return None + return config.mapped_key, config.width, config.height + + def add_camera_config( + self, key: str, mapped_key: str, width: int = 256, height: int = 256 + ) -> None: + """ + Add a new camera configuration or update an existing one. + + Args: + key: The camera key to add/update + mapped_key: The actual camera key to map to + width: Camera width in pixels + height: Camera height in pixels + """ + self.camera_configs[key.lower()] = CameraConfig(width, height, mapped_key) + + def get_all_camera_keys(self) -> list: + """ + Get all available camera keys. + + Returns: + List of all camera keys + """ + return list(self.camera_configs.keys()) diff --git a/gr00t_wbc/control/envs/robocasa/utils/controller_utils.py b/gr00t_wbc/control/envs/robocasa/utils/controller_utils.py new file mode 100644 index 0000000..a8627b6 --- /dev/null +++ b/gr00t_wbc/control/envs/robocasa/utils/controller_utils.py @@ -0,0 +1,54 @@ +def get_body_ik_solver_settings_type(robot: str): + robot2body_ik_solver_settings_type = { + "G1FixedLowerBody": "sim_optimized", + "G1FixedBase": "sim_optimized", + "G1FloatingBody": "sim_optimized", + "G1ArmsOnly": "sim_optimized", + "G1ArmsOnlyFloating": "sim_optimized", + "G1": "default", + "GR1ArmsAndWaistFourierHands": "default", + "GR1FixedLowerBody": "default", + "GR1ArmsOnlyFourierHands": "default", + "GR1ArmsOnly": "default", + } + return robot2body_ik_solver_settings_type[robot] + + +def update_robosuite_controller_configs( + robot: str, + wbc_version: str = None, + enable_gravity_compensation: bool = False, +): + """ + Update the robosuite controller configs based on the robot type and wbc version. + """ + body_ik_solver_settings_type = get_body_ik_solver_settings_type(robot) + if robot.startswith("G1"): + if wbc_version == "gear_wbc": + if enable_gravity_compensation: + robosuite_controller_configs = ( + "robocasa/examples/third_party_controller/default_mink_ik_g1_gear_wbc_gc.json" + ) + else: + robosuite_controller_configs = ( + "robocasa/examples/third_party_controller/default_mink_ik_g1_gear_wbc.json" + ) + else: + if body_ik_solver_settings_type == "default": + robosuite_controller_configs = ( + "robocasa/examples/third_party_controller/default_mink_ik_g1_wbc.json" + ) + elif body_ik_solver_settings_type == "sim_optimized": + robosuite_controller_configs = ( + "robocasa/examples/third_party_controller/" + "default_mink_ik_g1_wbc_sim_optimized.json" + ) + else: + raise ValueError( + f"Invalid body_ik_solver_settings_type: {body_ik_solver_settings_type}" + ) + elif robot.startswith("GR1"): + return "robocasa/examples/third_party_controller/default_mink_ik_gr1_smallkd.json" + else: + raise ValueError(f"Invalid robot: {robot}") + return robosuite_controller_configs diff --git a/gr00t_wbc/control/envs/robocasa/utils/robocasa_env.py b/gr00t_wbc/control/envs/robocasa/utils/robocasa_env.py new file mode 100644 index 0000000..a3b5dfc --- /dev/null +++ b/gr00t_wbc/control/envs/robocasa/utils/robocasa_env.py @@ -0,0 +1,443 @@ +import os +from typing import Any, Dict, List, Tuple + +from gymnasium import spaces +import mujoco +import numpy as np +import robocasa +from robocasa.utils.gym_utils.gymnasium_basic import ( + RoboCasaEnv, + create_env_robosuite, +) +from robocasa.wrappers.ik_wrapper import IKWrapper +from robosuite.controllers import load_composite_controller_config +from robosuite.utils.log_utils import ROBOSUITE_DEFAULT_LOGGER + +from gr00t_wbc.control.envs.robocasa.utils.cam_key_converter import CameraKeyMapper +from gr00t_wbc.control.envs.robocasa.utils.robot_key_converter import Gr00tObsActionConverter +from gr00t_wbc.control.robot_model.robot_model import RobotModel + +ALLOWED_LANGUAGE_CHARSET = ( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ,.\n\t[]{}()!?'_:" +) + + +class Gr00tLocomanipRoboCasaEnv(RoboCasaEnv): + def __init__( + self, + env_name: str, + robots_name: str, + robot_model: RobotModel, # gr00t robot model + input_space: str = "JOINT_SPACE", # either "JOINT_SPACE" or "EEF_SPACE" + camera_names: List[str] = ["egoview"], + camera_heights: List[int] | None = None, + camera_widths: List[int] | None = None, + onscreen: bool = False, + offscreen: bool = False, + dump_rollout_dataset_dir: str | None = None, + rollout_hdf5: str | None = None, + rollout_trainset: int | None = None, + controller_configs: str | None = None, + ik_indicator: bool = False, + **kwargs, + ): + # ========= Create env ========= + if controller_configs is None: + if "G1" in robots_name: + controller_configs = ( + "robocasa/examples/third_party_controller/default_mink_ik_g1_wbc.json" + ) + elif "GR1" in robots_name: + controller_configs = ( + "robocasa/examples/third_party_controller/default_mink_ik_gr1_smallkd.json" + ) + else: + assert False, f"Unsupported robot name: {robots_name}" + controller_configs = os.path.join( + os.path.dirname(robocasa.__file__), + "../", + controller_configs, + ) + controller_configs = load_composite_controller_config( + controller=controller_configs, + robot=robots_name.split("_")[0], + ) + if input_space == "JOINT_SPACE": + controller_configs["type"] = "BASIC" + controller_configs["composite_controller_specific_configs"] = {} + controller_configs["control_delta"] = False + + self.camera_key_mapper = CameraKeyMapper() + self.camera_names = camera_names + + if camera_widths is None: + self.camera_widths = [ + self.camera_key_mapper.get_camera_config(name)[1] for name in camera_names + ] + else: + self.camera_widths = camera_widths + if camera_heights is None: + self.camera_heights = [ + self.camera_key_mapper.get_camera_config(name)[2] for name in camera_names + ] + else: + self.camera_heights = camera_heights + + self.env, self.env_kwargs = create_env_robosuite( + env_name=env_name, + robots=robots_name.split("_"), + controller_configs=controller_configs, + camera_names=camera_names, + camera_widths=self.camera_widths, + camera_heights=self.camera_heights, + enable_render=offscreen, + onscreen=onscreen, + **kwargs, # Forward kwargs to create_env_robosuite + ) + + if ik_indicator: + self.env = IKWrapper(self.env, ik_indicator=True) + + # ========= create converters first to get total DOFs ========= + # For now, assume single robot (multi-robot support can be added later) + self.obs_action_converter: List[Gr00tObsActionConverter] = [ + Gr00tObsActionConverter( + robot_model=robot_model, + robosuite_robot_model=self.env.robots[i], + ) + for i in range(len(self.env.robots)) + ] + + self.body_dofs = sum(converter.body_dof for converter in self.obs_action_converter) + self.gripper_dofs = sum(converter.gripper_dof for converter in self.obs_action_converter) + self.total_dofs = self.body_dofs + self.gripper_dofs + self.body_nu = sum(converter.body_nu for converter in self.obs_action_converter) + self.gripper_nu = sum(converter.gripper_nu for converter in self.obs_action_converter) + self.total_nu = self.body_nu + self.gripper_nu + + # ========= create spaces to match total DOFs ========= + self.get_observation_space() + self.get_action_space() + + self.enable_render = offscreen + self.render_obs_key = f"{camera_names[0]}_image" + self.render_cache = None + + self.dump_rollout_dataset_dir = dump_rollout_dataset_dir + self.gr00t_exporter = None + self.np_exporter = None + + self.rollout_hdf5 = rollout_hdf5 + self.rollout_trainset = rollout_trainset + self.rollout_initial_state = {} + + self.verbose = False + for k, v in self.observation_space.items(): + self.verbose and print("{OBS}", k, v) + for k, v in self.action_space.items(): + self.verbose and print("{ACTION}", k, v) + + self.overridden_floating_base_action = None + + def get_observation_space(self): + self.observation_space = spaces.Dict({}) + + # Add all the observation spaces + self.observation_space["time"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(1,), dtype=np.float32 + ) + self.observation_space["floating_base_pose"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(7,), dtype=np.float32 + ) + self.observation_space["floating_base_vel"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(6,), dtype=np.float32 + ) + self.observation_space["floating_base_acc"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(6,), dtype=np.float32 + ) + self.observation_space["body_q"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(self.body_dofs,), dtype=np.float32 + ) + self.observation_space["body_dq"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(self.body_dofs,), dtype=np.float32 + ) + self.observation_space["body_ddq"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(self.body_dofs,), dtype=np.float32 + ) + self.observation_space["body_tau_est"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(self.body_nu,), dtype=np.float32 + ) + self.observation_space["left_hand_q"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(self.gripper_dofs // 2,), dtype=np.float32 + ) + self.observation_space["left_hand_dq"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(self.gripper_dofs // 2,), dtype=np.float32 + ) + self.observation_space["left_hand_ddq"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(self.gripper_dofs // 2,), dtype=np.float32 + ) + self.observation_space["left_hand_tau_est"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(self.gripper_nu // 2,), dtype=np.float32 + ) + self.observation_space["right_hand_q"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(self.gripper_dofs // 2,), dtype=np.float32 + ) + self.observation_space["right_hand_dq"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(self.gripper_dofs // 2,), dtype=np.float32 + ) + self.observation_space["right_hand_ddq"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(self.gripper_dofs // 2,), dtype=np.float32 + ) + self.observation_space["right_hand_tau_est"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(self.gripper_nu // 2,), dtype=np.float32 + ) + + self.observation_space["language.language_instruction"] = spaces.Text( + max_length=256, charset=ALLOWED_LANGUAGE_CHARSET + ) + + # Add camera observation spaces + for camera_name, w, h in zip(self.camera_names, self.camera_widths, self.camera_heights): + k = self.camera_key_mapper.get_camera_config(camera_name)[0] + self.observation_space[f"{k}_image"] = spaces.Box( + low=0, high=255, shape=(h, w, 3), dtype=np.uint8 + ) + + # Add extra privileged observation spaces + if hasattr(self.env, "get_privileged_obs_keys"): + for key, shape in self.env.get_privileged_obs_keys().items(): + self.observation_space[key] = spaces.Box( + low=-np.inf, high=np.inf, shape=shape, dtype=np.float32 + ) + + # Add robot-specific observation spaces + if hasattr(self.env.robots[0].robot_model, "torso_body"): + self.observation_space["secondary_imu_quat"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(4,), dtype=np.float32 + ) + self.observation_space["secondary_imu_vel"] = spaces.Box( + low=-np.inf, high=np.inf, shape=(6,), dtype=np.float32 + ) + + def get_action_space(self): + self.action_space = spaces.Dict( + {"q": spaces.Box(low=-np.inf, high=np.inf, shape=(self.total_dofs,), dtype=np.float32)} + ) + + def reset(self, seed=None, options=None): + raw_obs, info = super().reset(seed=seed, options=options) + obs = self.get_gr00t_observation(raw_obs) + + lang = self.env.get_ep_meta().get("lang", "") + ROBOSUITE_DEFAULT_LOGGER.info(f"Instruction: {lang}") + + return obs, info + + def step( + self, action: Dict[str, Any] + ) -> Tuple[Dict[str, Any], float, bool, bool, Dict[str, Any]]: + # action={"q": xxx, "tau": xxx} + for k, v in action.items(): + self.verbose and print("", k, v) + + joint_actoin_vec = action["q"] + action_dict = {} + for ii, robot in enumerate(self.env.robots): + pf = robot.robot_model.naming_prefix + _action_dict = self.obs_action_converter[ii].gr00t_to_robocasa_action_dict( + joint_actoin_vec + ) + action_dict.update({f"{pf}{k}": v for k, v in _action_dict.items()}) + if action.get("tau", None) is not None: + _torque_dict = self.obs_action_converter[ii].gr00t_to_robocasa_action_dict( + action["tau"] + ) + action_dict.update({f"{pf}{k}_tau": v for k, v in _torque_dict.items()}) + if self.overridden_floating_base_action is not None: + action_dict["robot0_base"] = self.overridden_floating_base_action + raw_obs, reward, terminated, truncated, info = super().step(action_dict) + obs = self.get_gr00t_observation(raw_obs) + + for k, v in obs.items(): + self.verbose and print("", k, v.shape if k.startswith("video.") else v) + self.verbose = False + + return obs, reward, terminated, truncated, info + + def step_only_kinematics( + self, action: Dict[str, Any] + ) -> Tuple[Dict[str, Any], float, bool, bool, Dict[str, Any]]: + joint_actoin_vec = action["q"] + for ii, robot in enumerate(self.env.robots): + joint_names = np.array(self.env.sim.model.joint_names)[robot._ref_joint_indexes] + body_q = self.obs_action_converter[ii].gr00t_to_robocasa_joint_order( + joint_names, joint_actoin_vec + ) + self.env.sim.data.qpos[robot._ref_joint_pos_indexes] = body_q + + for side in ["left", "right"]: + joint_names = np.array(self.env.sim.model.joint_names)[ + robot._ref_joints_indexes_dict[side + "_gripper"] + ] + gripper_q = self.obs_action_converter[ii].gr00t_to_robocasa_joint_order( + joint_names, joint_actoin_vec + ) + self.env.sim.data.qpos[robot._ref_gripper_joint_pos_indexes[side]] = gripper_q + + mujoco.mj_forward(self.env.sim.model._model, self.env.sim.data._data) + + obs = self.force_update_observation() + return obs, 0, False, False, {"success": False} + + def force_update_observation(self, timestep=0): + raw_obs = self.env._get_observations(force_update=True, timestep=timestep) + obs = self.get_basic_observation(raw_obs) + obs = self.get_gr00t_observation(obs) + return obs + + def get_basic_observation(self, raw_obs): + # this function takes a lot of time, so we disable it for now + # raw_obs.update(gather_robot_observations(self.env, format_gripper_space=False)) + + # Image are in (H, W, C), flip it upside down + def process_img(img): + return np.copy(img[::-1, :, :]) + + for obs_name, obs_value in raw_obs.items(): + if obs_name.endswith("_image"): + # image observations + raw_obs[obs_name] = process_img(obs_value) + else: + # non-image observations + raw_obs[obs_name] = obs_value.astype(np.float32) + + # Return black image if rendering is disabled + if not self.enable_render: + for ii, name in enumerate(self.camera_names): + raw_obs[f"{name}_image"] = np.zeros( + (self.camera_heights[ii], self.camera_widths[ii], 3), dtype=np.uint8 + ) + + self.render_cache = raw_obs[self.render_obs_key] + raw_obs["language"] = self.env.get_ep_meta().get("lang", "") + + return raw_obs + + def convert_body_q(self, q: np.ndarray) -> np.ndarray: + # q is in the order of the joints + robot = self.env.robots[0] + joint_names = np.array(self.env.sim.model.joint_names)[robot._ref_joint_indexes] + # this joint names are in the order of the obs_vec + actuated_q = self.obs_action_converter[0].robocasa_to_gr00t_actuated_order( + joint_names, q, "body" + ) + return actuated_q + + def convert_gripper_q(self, q: np.ndarray, side: str = "left") -> np.ndarray: + # q is in the order of the joints + robot = self.env.robots[0] + joint_names = np.array(self.env.sim.model.joint_names)[ + robot._ref_joints_indexes_dict[side + "_gripper"] + ] + actuated_q = self.obs_action_converter[0].robocasa_to_gr00t_actuated_order( + joint_names, q, side + "_gripper" + ) + return actuated_q + + def convert_gripper_tau(self, tau: np.ndarray, side: str = "left") -> np.ndarray: + # tau is in the order of the actuators + robot = self.env.robots[0] + actuator_idx = robot._ref_actuators_indexes_dict[side + "_gripper"] + actuated_joint_names = [ + self.env.sim.model.joint_id2name(self.env.sim.model.actuator_trnid[i][0]) + for i in actuator_idx + ] + actuated_tau = self.obs_action_converter[0].robocasa_to_gr00t_actuated_order( + actuated_joint_names, tau, side + "_gripper" + ) + return actuated_tau + + def get_gr00t_observation(self, raw_obs: Dict[str, Any]) -> Dict[str, Any]: + obs = {} + + if self.env.sim.model.jnt_type[0] == mujoco.mjtJoint.mjJNT_FREE: + # If the first joint is a free joint, use this way to get the floating base data + obs["floating_base_pose"] = self.env.sim.data.qpos[:7] + obs["floating_base_vel"] = self.env.sim.data.qvel[:6] + obs["floating_base_acc"] = self.env.sim.data.qacc[:6] + else: + # Otherwise, use self.env.sim.model to fetch the floating base pose + root_body_id = self.env.sim.model.body_name2id("robot0_base") + + # Get position and orientation from body state + root_pos = self.env.sim.data.body_xpos[root_body_id] + root_quat = self.env.sim.data.body_xquat[root_body_id] # quaternion in wxyz format + + # Combine position and quaternion to form 7-DOF pose + obs["floating_base_pose"] = np.concatenate([root_pos, root_quat]) + # set vel and acc to 0 + obs["floating_base_vel"] = np.zeros(6) + obs["floating_base_acc"] = np.zeros(6) + + obs["body_q"] = self.convert_body_q(raw_obs["robot0_joint_pos"]) + obs["body_dq"] = self.convert_body_q(raw_obs["robot0_joint_vel"]) + obs["body_ddq"] = self.convert_body_q(raw_obs["robot0_joint_acc"]) + + obs["left_hand_q"] = self.convert_gripper_q(raw_obs["robot0_left_gripper_qpos"], "left") + obs["left_hand_dq"] = self.convert_gripper_q(raw_obs["robot0_left_gripper_qvel"], "left") + obs["left_hand_ddq"] = self.convert_gripper_q(raw_obs["robot0_left_gripper_qacc"], "left") + obs["right_hand_q"] = self.convert_gripper_q(raw_obs["robot0_right_gripper_qpos"], "right") + obs["right_hand_dq"] = self.convert_gripper_q(raw_obs["robot0_right_gripper_qvel"], "right") + obs["right_hand_ddq"] = self.convert_gripper_q( + raw_obs["robot0_right_gripper_qacc"], "right" + ) + + robot = self.env.robots[0] + body_tau_idx_list = [] + left_gripper_tau_idx_list = [] + right_gripper_tau_idx_list = [] + for part_name, actuator_idx in robot._ref_actuators_indexes_dict.items(): + if "left_gripper" in part_name: + left_gripper_tau_idx_list.extend(actuator_idx) + elif "right_gripper" in part_name: + right_gripper_tau_idx_list.extend(actuator_idx) + elif "base" in part_name: + assert ( + len(actuator_idx) == 0 or robot.robot_model.default_base == "FloatingLeggedBase" + ) + else: + body_tau_idx_list.extend(actuator_idx) + + body_tau_idx_list = sorted(body_tau_idx_list) + left_gripper_tau_idx_list = sorted(left_gripper_tau_idx_list) + right_gripper_tau_idx_list = sorted(right_gripper_tau_idx_list) + obs["body_tau_est"] = self.convert_body_q( + self.env.sim.data.actuator_force[body_tau_idx_list] + ) + obs["right_hand_tau_est"] = self.convert_gripper_tau( + self.env.sim.data.actuator_force[right_gripper_tau_idx_list], "right" + ) + obs["left_hand_tau_est"] = self.convert_gripper_tau( + self.env.sim.data.actuator_force[left_gripper_tau_idx_list], "left" + ) + + obs["time"] = self.env.sim.data.time + + # Add camera images + for ii, camera_name in enumerate(self.camera_names): + mapped_camera_name = self.camera_key_mapper.get_camera_config(camera_name)[0] + obs[f"{mapped_camera_name}_image"] = raw_obs[f"{camera_name}_image"] + + # Add privileged observations + if hasattr(self.env, "get_privileged_obs_keys"): + for key in self.env.get_privileged_obs_keys(): + obs[key] = raw_obs[key] + + # Add robot-specific observations + if hasattr(self.env.robots[0].robot_model, "torso_body"): + obs["secondary_imu_quat"] = raw_obs["robot0_torso_link_imu_quat"] + obs["secondary_imu_vel"] = raw_obs["robot0_torso_link_imu_vel"] + + obs["language.language_instruction"] = raw_obs["language"] + + return obs diff --git a/gr00t_wbc/control/envs/robocasa/utils/robot_key_converter.py b/gr00t_wbc/control/envs/robocasa/utils/robot_key_converter.py new file mode 100644 index 0000000..617bfb5 --- /dev/null +++ b/gr00t_wbc/control/envs/robocasa/utils/robot_key_converter.py @@ -0,0 +1,301 @@ +from dataclasses import dataclass +from typing import Any, Dict, List, Tuple + +import numpy as np +from robocasa.models.robots import remove_mimic_joints +from robosuite.models.robots import RobotModel as RobosuiteRobotModel + +from gr00t_wbc.control.robot_model import RobotModel + + +class Gr00tJointInfo: + """ + Mapping from gr00t_wbc actuated joint names to robocasa joint names. + """ + + def __init__(self, robot_model: RobosuiteRobotModel): + self.robocasa_body_prefix = "robot0_" + self.robocasa_gripper_prefix = "gripper0_" + + self.robot_model: RobotModel = robot_model + self.body_actuated_joint_names: List[str] = ( + self.robot_model.supplemental_info.body_actuated_joints + ) + self.left_hand_actuated_joint_names: List[str] = ( + self.robot_model.supplemental_info.left_hand_actuated_joints + ) + self.right_hand_actuated_joint_names: List[str] = ( + self.robot_model.supplemental_info.right_hand_actuated_joints + ) + + self.actuated_joint_names: List[str] = self._get_gr00t_actuated_joint_names() + self.body_actuated_joint_to_index: Dict[str, int] = ( + self._get_gr00t_body_actuated_joint_name_to_index() + ) + self.gripper_actuated_joint_to_index: Tuple[Dict[str, int], Dict[str, int]] = ( + self._get_gr00t_gripper_actuated_joint_name_to_index() + ) + self.actuated_joint_name_to_index: Dict[str, int] = ( + self._get_gr00t_actuated_joint_name_to_index() + ) + + def _get_gr00t_actuated_joint_names(self) -> List[str]: + """Get list of gr00t actuated joint names ordered by their indices.""" + if self.robot_model.supplemental_info is None: + raise ValueError("Robot model must have supplemental_info") + + # Get joint names and indices + body_names = self.robot_model.supplemental_info.body_actuated_joints + left_hand_names = self.robot_model.supplemental_info.left_hand_actuated_joints + right_hand_names = self.robot_model.supplemental_info.right_hand_actuated_joints + + body_indices = self.robot_model.get_joint_group_indices("body") + left_hand_indices = self.robot_model.get_joint_group_indices("left_hand") + right_hand_indices = self.robot_model.get_joint_group_indices("right_hand") + + # Create a dictionary mapping index to name + index_to_name = {} + for name, idx in zip(body_names, body_indices): + index_to_name[idx] = self.robocasa_body_prefix + name + for name, idx in zip(left_hand_names, left_hand_indices): + index_to_name[idx] = self.robocasa_gripper_prefix + "left_" + name + for name, idx in zip(right_hand_names, right_hand_indices): + index_to_name[idx] = self.robocasa_gripper_prefix + "right_" + name + sorted_indices = sorted(index_to_name.keys()) + all_actuated_joint_names = [index_to_name[idx] for idx in sorted_indices] + return all_actuated_joint_names + + def _get_gr00t_body_actuated_joint_name_to_index(self) -> Dict[str, int]: + """Get dictionary mapping gr00t actuated joint names to indices.""" + if self.robot_model.supplemental_info is None: + raise ValueError("Robot model must have supplemental_info") + body_names = self.robot_model.supplemental_info.body_actuated_joints + body_indices = self.robot_model.get_joint_group_indices("body") + sorted_indices = np.argsort(body_indices) + sorted_names = [body_names[i] for i in sorted_indices] + return {self.robocasa_body_prefix + name: ii for ii, name in enumerate(sorted_names)} + + def _get_gr00t_gripper_actuated_joint_name_to_index( + self, + ) -> Tuple[Dict[str, int], Dict[str, int]]: + """Get dictionary mapping gr00t actuated joint names to indices.""" + if self.robot_model.supplemental_info is None: + raise ValueError("Robot model must have supplemental_info") + left_hand_names = self.robot_model.supplemental_info.left_hand_actuated_joints + right_hand_names = self.robot_model.supplemental_info.right_hand_actuated_joints + left_hand_indices = self.robot_model.get_joint_group_indices("left_hand") + right_hand_indices = self.robot_model.get_joint_group_indices("right_hand") + sorted_left_hand_indices = np.argsort(left_hand_indices) + sorted_right_hand_indices = np.argsort(right_hand_indices) + sorted_left_hand_names = [left_hand_names[i] for i in sorted_left_hand_indices] + sorted_right_hand_names = [right_hand_names[i] for i in sorted_right_hand_indices] + return ( + { + self.robocasa_gripper_prefix + "left_" + name: ii + for ii, name in enumerate(sorted_left_hand_names) + }, + { + self.robocasa_gripper_prefix + "right_" + name: ii + for ii, name in enumerate(sorted_right_hand_names) + }, + ) + + def _get_gr00t_actuated_joint_name_to_index(self) -> Dict[str, int]: + """Get dictionary mapping gr00t actuated joint names to indices.""" + return {name: ii for ii, name in enumerate(self.actuated_joint_names)} + + +@dataclass +class Gr00tObsActionConverter: + """ + Converter to align simulation environment joint action space with real environment joint action space. + Handles joint order and range conversion. + """ + + robot_model: RobotModel + robosuite_robot_model: RobosuiteRobotModel + robocasa_body_prefix: str = "robot0_" + robocasa_gripper_prefix: str = "gripper0_" + + def __post_init__(self): + """Initialize converter with robot configuration.""" + + self.robot_key = self.robot_model.supplemental_info.name + self.gr00t_joint_info = Gr00tJointInfo(self.robot_model) + self.robocasa_joint_names_for_each_part: Dict[str, List[str]] = ( + self._get_robocasa_joint_names_for_each_part() + ) + self.robocasa_actuator_names_for_each_part: Dict[str, List[str]] = ( + self._get_robotcasa_actuator_names_for_each_part() + ) + + # Store mappings directly as class attributes + self.gr00t_joint_name_to_index = self.gr00t_joint_info.actuated_joint_name_to_index + self.gr00t_body_joint_name_to_index = self.gr00t_joint_info.body_actuated_joint_to_index + self.gr00t_gripper_joint_name_to_index = { + "left": self.gr00t_joint_info.gripper_actuated_joint_to_index[0], + "right": self.gr00t_joint_info.gripper_actuated_joint_to_index[1], + } + self.gr00t_to_robocasa_actuator_indices = self._get_actuator_mapping() + + if self.robot_key == "GR1_Fourier": + self.joint_multiplier = ( + lambda x: np.array([-1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1]) * x + ) + self.actuator_multiplier = ( + lambda x: np.array([-1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1]) * x + ) + else: + self.joint_multiplier = lambda x: x + self.actuator_multiplier = lambda x: x + + # Store DOF counts directly + self.body_dof = len(self.gr00t_joint_info.body_actuated_joint_names) + self.gripper_dof = len(self.gr00t_joint_info.left_hand_actuated_joint_names) + len( + self.gr00t_joint_info.right_hand_actuated_joint_names + ) + self.whole_dof = self.body_dof + self.gripper_dof + self.body_nu = len(self.gr00t_joint_info.body_actuated_joint_names) + self.gripper_nu = len(self.gr00t_joint_info.left_hand_actuated_joint_names) + len( + self.gr00t_joint_info.right_hand_actuated_joint_names + ) + self.whole_nu = self.body_nu + self.gripper_nu + + def _get_robocasa_joint_names_for_each_part(self) -> Dict[str, List[str]]: + part_names = self.robosuite_robot_model._ref_joints_indexes_dict.keys() + robocasa_joint_names_for_each_part = {} + for part_name in part_names: + joint_indices = self.robosuite_robot_model._ref_joints_indexes_dict[part_name] + joint_names = [ + self.robosuite_robot_model.sim.model.joint_id2name(j) for j in joint_indices + ] + robocasa_joint_names_for_each_part[part_name] = joint_names + return robocasa_joint_names_for_each_part + + def _get_robotcasa_actuator_names_for_each_part(self) -> Dict[str, List[str]]: + part_names = self.robosuite_robot_model._ref_actuators_indexes_dict.keys() + robocasa_actuator_names_for_each_part = {} + for part_name in part_names: + if part_name == "base": + continue + actuator_indices = self.robosuite_robot_model._ref_actuators_indexes_dict[part_name] + actuator_names = [ + self.robosuite_robot_model.sim.model.actuator_id2name(j) for j in actuator_indices + ] + robocasa_actuator_names_for_each_part[part_name] = actuator_names + return robocasa_actuator_names_for_each_part + + def _get_actuator_mapping(self) -> Dict[str, List[int]]: + """Get mapping from gr00t_wbc actuatored joint order to robocasa actuatored joint order for whole body.""" + return { + part_name: [ + self.gr00t_joint_info.actuated_joint_name_to_index[j] + for j in self.robocasa_actuator_names_for_each_part[part_name] + ] + for part_name in self.robocasa_actuator_names_for_each_part.keys() + } + + def check_action_dim_match(self, vec_dim: int) -> bool: + """ + Check if input vector dimension matches expected dimension. + + Args: + vec_dim: Dimension of input vector + + Returns: + bool: True if dimensions match + """ + return vec_dim == self.whole_dof + + def gr00t_to_robocasa_action_dict(self, action_vec: np.ndarray) -> Dict[str, Any]: + """ + Convert gr00t flat action vector to robocasa dictionary mapping part names to actions. + + Args: + robot: Robocasa robot model instance + action_vec: Full action vector array in gr00t actuated joint order + + Returns: + dict: Mapping from part names to action vectors for robocasa + """ + if not self.check_action_dim_match(len(action_vec)): + raise ValueError( + f"Action vector dimension mismatch: {len(action_vec)} != {self.whole_dof}" + ) + + action_dict = {} + cc = self.robosuite_robot_model.composite_controller + + for part_name, controller in cc.part_controllers.items(): + if "gripper" in part_name: + robocasa_action = action_vec[self.gr00t_to_robocasa_actuator_indices[part_name]] + if self.actuator_multiplier is not None: + robocasa_action = self.actuator_multiplier(robocasa_action) + action_dict[part_name] = remove_mimic_joints( + cc.grippers[part_name], robocasa_action + ) + elif "base" in part_name: + assert ( + len(self.gr00t_to_robocasa_actuator_indices.get(part_name, [])) == 0 + or self.robosuite_robot_model.default_base == "FloatingLeggedBase" + ) + else: + action_dict[part_name] = action_vec[ + self.gr00t_to_robocasa_actuator_indices[part_name] + ] + + return action_dict + + def robocasa_to_gr00t_actuated_order( + self, joint_names: List[str], q: np.ndarray, obs_type: str = "body" + ) -> np.ndarray: + """ + Convert observation from robocasa joint order to gr00t actuated joint order. + + Args: + joint_names: List of joint names in robocasa order (with prefixes) + q: Joint positions corresponding to joint_names + obs_type: Type of observation ("body", "left_gripper", "right_gripper", or "whole") + + Returns: + Joint positions in gr00t actuated joint order + """ + assert len(joint_names) == len(q), "Joint names and q must have the same length" + + if obs_type == "body": + actuated_q = np.zeros(self.body_dof) + for i, jn in enumerate(joint_names): + actuated_q[self.gr00t_body_joint_name_to_index[jn]] = q[i] + elif obs_type == "left_gripper": + actuated_q = np.zeros(self.gripper_dof // 2) + for i, jn in enumerate(joint_names): + actuated_q[self.gr00t_gripper_joint_name_to_index["left"][jn]] = q[i] + elif obs_type == "right_gripper": + actuated_q = np.zeros(self.gripper_dof // 2) + for i, jn in enumerate(joint_names): + actuated_q[self.gr00t_gripper_joint_name_to_index["right"][jn]] = q[i] + elif obs_type == "whole": + actuated_q = np.zeros(self.whole_dof) + for i, jn in enumerate(joint_names): + actuated_q[self.gr00t_joint_name_to_index[jn]] = q[i] + else: + raise ValueError(f"Unknown observation type: {obs_type}") + return actuated_q + + def gr00t_to_robocasa_joint_order( + self, joint_names: List[str], q_in_actuated_order: np.ndarray + ) -> np.ndarray: + """ + Convert gr00t actuated joint order to robocasa joint order. + + Args: + joint_names: List of joint names in robocasa order (with prefixes) + q_in_actuated_order: Joint positions corresponding to joint_names in gr00t actuated joint order + + Returns: + Joint positions in robocasa joint order + """ + q = np.zeros(len(joint_names)) + for i, jn in enumerate(joint_names): + q[i] = q_in_actuated_order[self.gr00t_joint_name_to_index[jn]] + return q diff --git a/gr00t_wbc/control/envs/robocasa/utils/sim_utils.py b/gr00t_wbc/control/envs/robocasa/utils/sim_utils.py new file mode 100644 index 0000000..5250f25 --- /dev/null +++ b/gr00t_wbc/control/envs/robocasa/utils/sim_utils.py @@ -0,0 +1,8 @@ +try: + import robosuite.macros_private as macros +except ImportError: + import robosuite.macros as macros + + +def change_simulation_timestep(timestep: float): + macros.SIMULATION_TIMESTEP = timestep diff --git a/gr00t_wbc/control/main/__init__.py b/gr00t_wbc/control/main/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gr00t_wbc/control/main/config_template.py b/gr00t_wbc/control/main/config_template.py new file mode 100644 index 0000000..8da84d5 --- /dev/null +++ b/gr00t_wbc/control/main/config_template.py @@ -0,0 +1,45 @@ +from dataclasses import asdict, dataclass +from typing import Any + + +@dataclass +class ArgsConfig: + """Args Config for running the data collection loop.""" + + def update( + self, + config_dict: dict, + strict: bool = False, + skip_keys: list[str] = [], + allowed_keys: list[str] | None = None, + ): + for k, v in config_dict.items(): + if k in skip_keys: + continue + if allowed_keys is not None and k not in allowed_keys: + continue + if strict and not hasattr(self, k): + raise ValueError(f"Config {k} not found in {self.__class__.__name__}") + if not strict and not hasattr(self, k): + continue + setattr(self, k, v) + + @classmethod + def from_dict( + cls, + config_dict: dict, + strict: bool = False, + skip_keys: list[str] = [], + allowed_keys: list[str] | None = None, + ): + instance = cls() + instance.update( + config_dict=config_dict, strict=strict, skip_keys=skip_keys, allowed_keys=allowed_keys + ) + return instance + + def to_dict(self): + return asdict(self) + + def get(self, key: str, default: Any = None): + return getattr(self, key) if hasattr(self, key) else default diff --git a/gr00t_wbc/control/main/constants.py b/gr00t_wbc/control/main/constants.py new file mode 100644 index 0000000..8d54f25 --- /dev/null +++ b/gr00t_wbc/control/main/constants.py @@ -0,0 +1,16 @@ +IMAGE_TOPIC_NAME = "realsense/color/image_raw" +STATE_TOPIC_NAME = "G1Env/env_state_act" +CONTROL_GOAL_TOPIC = "ControlPolicy/upper_body_pose" +ROBOT_CONFIG_TOPIC = "WBCPolicy/robot_config" +KEYBOARD_INPUT_TOPIC = "/keyboard_input" +LOCO_MANIP_TASK_STATUS_TOPIC = "LocoManipPolicy/task_status" +LOCO_NAV_TASK_STATUS_TOPIC = "NavigationPolicy/task_status" +LOWER_BODY_POLICY_STATUS_TOPIC = "ControlPolicy/lower_body_policy_status" +JOINT_SAFETY_STATUS_TOPIC = "ControlPolicy/joint_safety_status" + + +DEFAULT_NAV_CMD = [0.0, 0.0, 0.0] +DEFAULT_BASE_HEIGHT = 0.74 +DEFAULT_WRIST_POSE = [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0] * 2 # x, y, z + w, x, y, z + +DEFAULT_MODEL_SERVER_PORT = 5555 # port used to host the model server diff --git a/gr00t_wbc/control/main/teleop/__init__.py b/gr00t_wbc/control/main/teleop/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gr00t_wbc/control/main/teleop/configs/configs.py b/gr00t_wbc/control/main/teleop/configs/configs.py new file mode 100644 index 0000000..b1f6251 --- /dev/null +++ b/gr00t_wbc/control/main/teleop/configs/configs.py @@ -0,0 +1,484 @@ +from dataclasses import dataclass +import os +from pathlib import Path +from typing import Literal, Optional + +import yaml + +import gr00t_wbc +from gr00t_wbc.control.main.config_template import ArgsConfig as ArgsConfigTemplate +from gr00t_wbc.control.policy.wbc_policy_factory import WBC_VERSIONS +from gr00t_wbc.control.utils.network_utils import resolve_interface + + +def override_wbc_config( + wbc_config: dict, config: "BaseConfig", missed_keys_only: bool = False +) -> dict: + """Override WBC YAML values with dataclass values. + + Args: + wbc_config: The loaded WBC YAML configuration dictionary + config: The BaseConfig dataclass instance with override values + missed_keys_only: If True, only add keys that don't exist in wbc_config. + If False, validate all keys exist and override all. + + Returns: + Updated wbc_config dictionary with overridden values + + Raises: + KeyError: If any required keys are missing from the WBC YAML configuration + (only when missed_keys_only=False) + """ + # Override yaml values with dataclass values + key_to_value = { + "INTERFACE": config.interface, + "ENV_TYPE": config.env_type, + "VERSION": config.wbc_version, + "SIMULATOR": config.simulator, + "SIMULATE_DT": 1 / float(config.sim_frequency), + "ENABLE_OFFSCREEN": config.enable_offscreen, + "ENABLE_ONSCREEN": config.enable_onscreen, + "model_path": config.wbc_model_path, + "enable_waist": config.enable_waist, + "with_hands": config.with_hands, + "verbose": config.verbose, + "verbose_timing": config.verbose_timing, + "upper_body_max_joint_speed": config.upper_body_joint_speed, + "keyboard_dispatcher_type": config.keyboard_dispatcher_type, + "enable_gravity_compensation": config.enable_gravity_compensation, + "gravity_compensation_joints": config.gravity_compensation_joints, + "high_elbow_pose": config.high_elbow_pose, + } + + if missed_keys_only: + # Only add keys that don't exist in wbc_config + for key in key_to_value: + if key not in wbc_config: + wbc_config[key] = key_to_value[key] + else: + # Set all keys (overwrite existing) + for key in key_to_value: + wbc_config[key] = key_to_value[key] + + # g1 kp, kd, sim2real gap + if config.env_type == "real": + # update waist pitch damping, index 14 + wbc_config["MOTOR_KD"][14] = wbc_config["MOTOR_KD"][14] - 10 + + return wbc_config + + +@dataclass +class BaseConfig(ArgsConfigTemplate): + """Base config inherited by all G1 control loops""" + + # WBC Configuration + wbc_version: Literal[tuple(WBC_VERSIONS)] = "gear_wbc" + """Version of the whole body controller.""" + + wbc_model_path: str = ( + "policy/GR00T-WholeBodyControl-Balance.onnx," + "policy/GR00T-WholeBodyControl-Walk.onnx" + ) + """Path to WBC model file (relative to gr00t_wbc/sim2mujoco/resources/robots/g1)""" + """gear_wbc model path: policy/GR00T-WholeBodyControl-Balance.onnx,policy/GR00T-WholeBodyControl-Walk.onnx""" + + wbc_policy_class: str = "G1DecoupledWholeBodyPolicy" + """Whole body policy class.""" + + # System Configuration + interface: str = "sim" + """Interface to use for the control loop. [sim, real, lo, enxe8ea6a9c4e09]""" + + simulator: str = "mujoco" + """Simulator to use.""" + + sim_sync_mode: bool = False + """Whether to run the control loop in sync mode.""" + + control_frequency: int = 50 + """Frequency of the control loop.""" + + sim_frequency: int = 200 + """Frequency of the simulation loop.""" + + # Robot Configuration + enable_waist: bool = False + """Whether to include waist joints in IK.""" + + with_hands: bool = True + """Enable hand functionality. When False, robot operates without hands.""" + + high_elbow_pose: bool = False + """Enable high elbow pose configuration for default joint positions.""" + + verbose: bool = True + """Whether to print verbose output.""" + + # Additional common fields + enable_offscreen: bool = False + """Whether to enable offscreen rendering.""" + + enable_onscreen: bool = True + """Whether to enable onscreen rendering.""" + + upper_body_joint_speed: float = 1000 + """Upper body joint speed.""" + + env_name: str = "default" + """Environment name.""" + + ik_indicator: bool = False + """Whether to draw IK indicators.""" + + verbose_timing: bool = False + """Enable verbose timing output every iteration.""" + + keyboard_dispatcher_type: str = "raw" + """Keyboard dispatcher to use. [raw, ros]""" + + # Gravity Compensation Configuration + enable_gravity_compensation: bool = False + """Enable gravity compensation using pinocchio dynamics.""" + + gravity_compensation_joints: Optional[list[str]] = None + """Joint groups to apply gravity compensation to (e.g., ['arms', 'left_arm', 'right_arm']).""" + # Teleop/Device Configuration + body_control_device: str = "dummy" + """Device to use for body control. Options: dummy, vive, iphone, leapmotion, joycon.""" + + hand_control_device: Optional[str] = "dummy" + """Device to use for hand control. Options: None, manus, joycon, iphone.""" + + body_streamer_ip: str = "10.112.210.229" + """IP address for body streamer (vive only).""" + + body_streamer_keyword: str = "knee" + """Body streamer keyword (vive only).""" + + enable_visualization: bool = False + """Whether to enable visualization.""" + + enable_real_device: bool = True + """Whether to enable real device.""" + + teleop_frequency: int = 20 + """Teleoperation frequency (Hz).""" + + teleop_replay_path: Optional[str] = None + """Path to teleop replay data.""" + + # Deployment/Camera Configuration + robot_ip: str = "192.168.123.164" + """Robot IP address""" + # Data collection settings + data_collection: bool = True + """Enable data collection""" + + data_collection_frequency: int = 20 + """Data collection frequency (Hz)""" + + root_output_dir: str = "outputs" + """Root output directory""" + + # Policy settings + enable_upper_body_operation: bool = True + """Enable upper body operation""" + + upper_body_operation_mode: Literal["teleop", "inference"] = "teleop" + """Upper body operation mode""" + + def __post_init__(self): + # Resolve interface (handles sim/real shortcuts, platform differences, and error handling) + self.interface, self.env_type = resolve_interface(self.interface) + + def load_wbc_yaml(self) -> dict: + """Load and merge wbc yaml with dataclass overrides""" + # Get the base path to gr00t_wbc and convert to Path object + package_path = Path(os.path.dirname(gr00t_wbc.__file__)) + + if self.wbc_version == "gear_wbc": + config_path = str(package_path / "control/main/teleop/configs/g1_29dof_gear_wbc.yaml") + else: + raise ValueError( + f"Invalid wbc_version: {self.wbc_version}, please use one of: " f"gear_wbc" + ) + + with open(config_path) as file: + wbc_config = yaml.load(file, Loader=yaml.FullLoader) + + # Override yaml values with dataclass values + wbc_config = override_wbc_config(wbc_config, self) + + return wbc_config + + +@dataclass +class ControlLoopConfig(BaseConfig): + """Config for running the G1 control loop.""" + + pass + + +@dataclass +class TeleopConfig(BaseConfig): + """Config for running the G1 teleop policy loop.""" + + robot: Literal["g1"] = "g1" + """Name of the robot to use, e.g., 'g1'.""" + + lerobot_replay_path: Optional[str] = None + """Path to lerobot replay data.""" + + # Override defaults for teleop-specific values + body_streamer_ip: str = "10.110.67.24" + """IP address for body streamer (vive only).""" + + body_streamer_keyword: str = "foot" + """Keyword for body streamer (vive only).""" + + teleop_frequency: float = 20 # Override to be float instead of int + """Frequency of the teleop loop.""" + + binary_hand_ik: bool = True + """Whether to use binary IK.""" + + +@dataclass +class ComposedCameraClientConfig: + """Config for running the composed camera client.""" + + camera_port: int = 5555 + """Port number""" + + camera_host: str = "localhost" + """Host IP address""" + + fps: float = 20.0 + """FPS of the camera viewer""" + + +@dataclass +class DataExporterConfig(BaseConfig, ComposedCameraClientConfig): + """Config for running the G1 data exporter.""" + + dataset_name: Optional[str] = None + """Name of the dataset to save the data to. If the dataset already exists, + the new episodes will be appended to existing dataset. If the dataset does not exist, + episodes will be saved under root_output_dir/dataset_name. + """ + + task_prompt: str = "demo" + """Language Task prompt for the dataset.""" + + state_dim: int = 43 + """Size of the state.""" + + action_dim: int = 43 + """Size of the action.""" + + teleoperator_username: Optional[str] = None + """Teleoperator username.""" + + support_operator_username: Optional[str] = None + """Support operator username.""" + + robot_id: Optional[str] = None + """Robot ID.""" + + lower_body_policy: Optional[str] = None + """Lower body policy.""" + + img_stream_viewer: bool = False + """Whether to open a matplot lib window to view the camera images.""" + + text_to_speech: bool = True + """Whether to use text-to-speech for voice feedback.""" + + add_stereo_camera: bool = True + """Whether to add stereo camera for data collection. If False, only use a signle ego view camera.""" + + +@dataclass +class SyncSimDataCollectionConfig(ControlLoopConfig, TeleopConfig): + """Args Config for running the data collection loop.""" + + robot: str = "G1" + """Name of the robot to collect data for (e.g., G1 variants).""" + + task_name: str = "GroundOnly" + """Name of the task to collect data for. [PnPBottle, GroundOnly, ...]""" + + body_control_device: str = "dummy" + """Device to use for body control. Options: dummy, vive, iphone, leapmotion, joycon.""" + + hand_control_device: Optional[str] = "dummy" + """Device to use for hand control. Options: None, manus, joycon, iphone.""" + + remove_existing_dir: bool = False + """Whether to remove existing output directory if it exists.""" + + hardcode_teleop_cmd: bool = False + """Whether to hardcode the teleop command for testing purposes.""" + + ik_indicator: bool = False + """Whether to draw IK indicators.""" + + enable_onscreen: bool = True + """Whether to show the onscreen rendering.""" + + save_img_obs: bool = False + """Whether to save image observations.""" + + success_hold_steps: int = 50 + """Number of steps to collect after task completion before saving.""" + + renderer: Literal["mjviewer", "mujoco", "rerun"] = "mjviewer" + """Renderer to use for the environment. """ + + replay_data_path: str | None = None + """Path to the data (.pkl) to replay. If None, will not replay. Used for CI/CD.""" + + replay_speed: float = 2.5 + """Speed multiplier for replay data. Higher values make replay slower (e.g., 2.5 for sync sim tests).""" + + ci_test: bool = False + """Whether to run the CI test.""" + + ci_test_mode: Literal["unit", "pre_merge"] = "pre_merge" + """'unit' for fast 50-step tests, 'pre_merge' for 500-step test with tracking checks.""" + + manual_control: bool = False + """Enable manual control of data collection start/save. When True, use toggle_data_collection + to manually control episode states (idle -> recording -> need_to_save -> idle). + When False (default), automatically starts and stops data collection based on task completion.""" + + +@dataclass +class SyncSimPlaybackConfig(SyncSimDataCollectionConfig): + """Configuration class for playback script arguments.""" + + enable_real_device: bool = False + """Whether to enable real device""" + + dataset: str | None = None + """Path to the demonstration dataset, either an HDF5 file or a LeRobot folder path.""" + + use_actions: bool = False + """Whether to use actions for playback""" + + use_wbc_goals: bool = False + """Whether to use WBC goals for control""" + + use_teleop_cmd: bool = False + """Whether to use teleop IK for action generation""" + + # Video recording arguments. + # Warning: enabling this key will leads to divergence between playback and recording. + save_video: bool = False + """Whether to save video of the playback""" + + # Saving to LeRobot dataset. + # Warning: enabling this key will leads to divergence between playback and recording. + save_lerobot: bool = False + """Whether to save the playback as a new LeRobot dataset""" + + video_path: str | None = None + """Path to save the output video. If not specified, + will use the nearest folder to dataset and save as playback_video.mp4""" + + num_episodes: int = 1 + """Number of episodes to load and playback/record (loads only the first N episodes from dataset)""" + + intervention: bool = False + """Whether to denote intervention timesteps with colored borders in video frames""" + + ci_test: bool = False + """Whether this is a CI test run, which limits the number of steps for testing purposes""" + + def validate_args(self): + # Validate argument combinations + if self.use_teleop_cmd and not self.use_actions: + raise ValueError("--use-teleop-cmd requires --use-actions to be set") + + # Note: using teleop cmd has playback divergence unlike using wbc goals, as TeleopPolicy has a warmup loop + if self.use_teleop_cmd and self.use_wbc_goals: + raise ValueError("--use-teleop-cmd and --use-wbc-goals are mutually exclusive") + + if (self.use_teleop_cmd or self.use_wbc_goals) and not self.use_actions: + raise ValueError( + "You are using --use-teleop-cmd or --use-wbc-goals but not --use-actions. " + "This will not play back actions whether via teleop or wbc goals. " + "Instead, it'll play back states only." + ) + + if self.save_img_obs and not self.save_lerobot: + raise ValueError("--save-img-obs is only supported with --save-lerobot") + + if self.intervention and not self.save_video: + raise ValueError("--intervention requires --save-video to be enabled for visualization") + + +@dataclass +class WebcamRecorderConfig(BaseConfig): + """Config for running the webcam recorder.""" + + output_dir: str = "logs_experiment" + """Output directory for webcam recordings""" + + device_id: int = 0 + """Camera device ID""" + + fps: int = 30 + """Recording frame rate""" + + duration: Optional[int] = None + """Recording duration in seconds (None for continuous)""" + + +@dataclass +class SimLoopConfig(BaseConfig): + """Config for running the simulation loop.""" + + mp_start_method: str = "spawn" + """Multiprocessing start method""" + + enable_image_publish: bool = False + """Enable image publishing in simulation""" + + camera_port: int = 5555 + """Camera port for image publishing""" + + verbose: bool = False + """Verbose output, override the base config verbose""" + + +@dataclass +class DeploymentConfig(BaseConfig, ComposedCameraClientConfig): + """G1 Robot Deployment Configuration + + Simplified deployment config that inherits all common fields from G1BaseConfig. + All deployment settings are now available in the base config. + """ + + camera_publish_rate: float = 30.0 + """Camera publish rate (Hz)""" + + view_camera: bool = True + """Enable camera viewer""" + # Webcam recording settings + enable_webcam_recording: bool = True + """Enable webcam recording for real robot deployment monitoring""" + + webcam_output_dir: str = "logs_experiment" + """Output directory for webcam recordings""" + + skip_img_transform: bool = False + """Skip image transformation in the model (for faster internet)""" + + sim_in_single_process: bool = False + """Run simulator in a separate process. When True, sets simulator to None in main control loop + and launches run_sim_loop.py separately.""" + + image_publish: bool = False + """Enable image publishing in simulation loop (passed to run_sim_loop.py)""" diff --git a/gr00t_wbc/control/main/teleop/configs/g1_29dof_gear_wbc.yaml b/gr00t_wbc/control/main/teleop/configs/g1_29dof_gear_wbc.yaml new file mode 100644 index 0000000..8583066 --- /dev/null +++ b/gr00t_wbc/control/main/teleop/configs/g1_29dof_gear_wbc.yaml @@ -0,0 +1,421 @@ +GEAR_WBC_CONFIG: "gr00t_wbc/sim2mujoco/resources/robots/g1/g1_gear_wbc.yaml" + +# copy from g1_43dof_hist.yaml +ROBOT_TYPE: 'g1_29dof' # Robot name, "go2", "b2", "b2w", "h1", "go2w", "g1" +ROBOT_SCENE: "gr00t_wbc/control/robot_model/model_data/g1/scene_43dof.xml" # Robot scene, for Sim2Sim +# ROBOT_SCENE: "gr00t_wbc/control/robot_model/model_data/g1/scene_29dof_activated3dex.xml" + +DOMAIN_ID: 0 # Domain id +# Network Interface, "lo" for simulation and the one with "192.168.123.222" for real robot +# INTERFACE: "enxe8ea6a9c4e09" +# INTERFACE: "enxc8a3623c9cb7" +INTERFACE: "lo" +SIMULATOR: "mujoco" # "robocasa" + +USE_JOYSTICK: 0 # Simulate Unitree WirelessController using a gamepad (0: disable, 1: enable) +JOYSTICK_TYPE: "xbox" # support "xbox" and "switch" gamepad layout +JOYSTICK_DEVICE: 0 # Joystick number + +FREE_BASE: False + +PRINT_SCENE_INFORMATION: True # Print link, joint and sensors information of robot +ENABLE_ELASTIC_BAND: True # Virtual spring band, used for lifting h1 + +SIMULATE_DT: 0.005 # Need to be larger than the runtime of viewer.sync() +VIEWER_DT: 0.02 # Viewer update time +REWARD_DT: 0.02 +USE_SENSOR: False +USE_HISTORY: True +USE_HISTORY_LOCO: True +USE_HISTORY_MIMIC: True + +GAIT_PERIOD: 0.9 # 1.25 + +MOTOR2JOINT: [0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, + 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28] + +JOINT2MOTOR: [0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, + 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28] + + +UNITREE_LEGGED_CONST: + HIGHLEVEL: 0xEE + LOWLEVEL: 0xFF + TRIGERLEVEL: 0xF0 + PosStopF: 2146000000.0 + VelStopF: 16000.0 + MODE_MACHINE: 5 + MODE_PR: 0 + +JOINT_KP: [ + 100, 100, 100, 200, 20, 20, + 100, 100, 100, 200, 20, 20, + 400, 400, 400, + 90, 60, 20, 60, 4, 4, 4, + 90, 60, 20, 60, 4, 4, 4 +] + + +JOINT_KD: [ + 2.5, 2.5, 2.5, 5, 0.2, 0.1, + 2.5, 2.5, 2.5, 5, 0.2, 0.1, + 5.0, 5.0, 5.0, + 2.0, 1.0, 0.4, 1.0, 0.2, 0.2, 0.2, + 2.0, 1.0, 0.4, 1.0, 0.2, 0.2, 0.2 +] + +# arm kp +# soft kp, safe, test it first +# 50, 50, 20, 20, 10, 10, 10 +# hard kp, use only if policy is safe +# 200, 200, 80, 80, 50, 50, 50, + +# MOTOR_KP: [ +# 100, 100, 100, 200, 20, 20, +# 100, 100, 100, 200, 20, 20, +# 400, 400, 400, +# 50, 50, 20, 20, 10, 10, 10, +# 50, 50, 20, 20, 10, 10, 10 +# ] + +MOTOR_KP: [ + 150, 150, 150, 200, 40, 40, + 150, 150, 150, 200, 40, 40, + 250, 250, 250, + 100, 100, 40, 40, 20, 20, 20, + 100, 100, 40, 40, 20, 20, 20 +] + +MOTOR_KD: [ + 2, 2, 2, 4, 2, 2, + 2, 2, 2, 4, 2, 2, + 5, 5, 5, + 5, 5, 2, 2, 2, 2, 2, + 5, 5, 2, 2, 2, 2, 2 +] + +# MOTOR_KP: [ +# 100, 100, 100, 200, 20, 20, +# 100, 100, 100, 200, 20, 20, +# 400, 400, 400, +# 90, 60, 20, 60, 4, 4, 4, +# 90, 60, 20, 60, 4, 4, 4 +# ] + + +# MOTOR_KD: [ +# 2.5, 2.5, 2.5, 5, 0.2, 0.1, +# 2.5, 2.5, 2.5, 5, 0.2, 0.1, +# 5.0, 5.0, 5.0, +# 2.0, 1.0, 0.4, 1.0, 0.2, 0.2, 0.2, +# 2.0, 1.0, 0.4, 1.0, 0.2, 0.2, 0.2 +# ] + + +WeakMotorJointIndex: + left_hip_yaw_joint: 0 + left_hip_roll_joint: 1 + left_hip_pitch_joint: 2 + left_knee_joint: 3 + left_ankle_pitch_joint: 4 + left_ankle_roll_joint: 5 + right_hip_yaw_joint: 6 + right_hip_roll_joint: 7 + right_hip_pitch_joint: 8 + right_knee_joint: 9 + right_ankle_pitch_joint: 10 + right_ankle_roll_joint: 11 + waist_yaw_joint : 12 + waist_roll_joint : 13 + waist_pitch_joint : 14 + left_shoulder_pitch_joint: 15 + left_shoulder_roll_joint: 16 + left_shoulder_yaw_joint: 17 + left_elbow_joint: 18 + left_wrist_roll_joint: 19 + left_wrist_pitch_joint: 20 + left_wrist_yaw_joint: 21 + right_shoulder_pitch_joint: 22 + right_shoulder_roll_joint: 23 + right_shoulder_yaw_joint: 24 + right_elbow_joint: 25 + right_wrist_roll_joint: 26 + right_wrist_pitch_joint: 27 + right_wrist_yaw_joint: 28 + +NUM_MOTORS: 29 +NUM_JOINTS: 29 +NUM_HAND_MOTORS: 7 +NUM_HAND_JOINTS: 7 +NUM_UPPER_BODY_JOINTS: 17 + +DEFAULT_DOF_ANGLES: [ + -0.1, # left_hip_pitch_joint + 0.0, # left_hip_roll_joint + 0.0, # left_hip_yaw_joint + 0.3, # left_knee_joint + -0.2, # left_ankle_pitch_joint + 0.0, # left_ankle_roll_joint + -0.1, # right_hip_pitch_joint + 0.0, # right_hip_roll_joint + 0.0, # right_hip_yaw_joint + 0.3, # right_knee_joint + -0.2, # right_ankle_pitch_joint + 0.0, # right_ankle_roll_joint + 0.0, # waist_yaw_joint + 0.0, # waist_roll_joint + 0.0, # waist_pitch_joint + 0.0, # left_shoulder_pitch_joint + 0.0, # left_shoulder_roll_joint + 0.0, # left_shoulder_yaw_joint + 0.0, # left_elbow_joint + 0.0, # left_wrist_roll_joint + 0.0, # left_wrist_pitch_joint + 0.0, # left_wrist_yaw_joint + 0.0, # right_shoulder_pitch_joint + 0.0, # right_shoulder_roll_joint + 0.0, # right_shoulder_yaw_joint + 0.0, # right_elbow_joint + 0.0, # right_wrist_roll_joint + 0.0, # right_wrist_pitch_joint + 0.0 # right_wrist_yaw_joint +] + +DEFAULT_MOTOR_ANGLES: [ + -0.1, # left_hip_pitch_joint + 0.0, # left_hip_roll_joint + 0.0, # left_hip_yaw_joint + 0.3, # left_knee_joint + -0.2, # left_ankle_pitch_joint + 0.0, # left_ankle_roll_joint + -0.1, # right_hip_pitch_joint + 0.0, # right_hip_roll_joint + 0.0, # right_hip_yaw_joint + 0.3, # right_knee_joint + -0.2, # right_ankle_pitch_joint + 0.0, # right_ankle_roll_joint + 0.0, # waist_yaw_joint + 0.0, # waist_roll_joint + 0.0, # waist_pitch_joint + 0.0, # left_shoulder_pitch_joint + 0.0, # left_shoulder_roll_joint + 0.0, # left_shoulder_yaw_joint + 0.0, # left_elbow_joint + 0.0, # left_wrist_roll_joint + 0.0, # left_wrist_pitch_joint + 0.0, # left_wrist_yaw_joint + 0.0, # right_shoulder_pitch_joint + 0.0, # right_shoulder_roll_joint + 0.0, # right_shoulder_yaw_joint + 0.0, # right_elbow_joint + 0.0, # right_wrist_roll_joint + 0.0, # right_wrist_pitch_joint + 0.0 # right_wrist_yaw_joint +] + +motor_pos_lower_limit_list: [-2.5307, -0.5236, -2.7576, -0.087267, -0.87267, -0.2618, + -2.5307, -2.9671, -2.7576, -0.087267, -0.87267, -0.2618, + -2.618, -0.52, -0.52, + -3.0892, -1.5882, -2.618, -1.0472, + -1.972222054, -1.61443, -1.61443, + -3.0892, -2.2515, -2.618, -1.0472, + -1.972222054, -1.61443, -1.61443] +motor_pos_upper_limit_list: [2.8798, 2.9671, 2.7576, 2.8798, 0.5236, 0.2618, + 2.8798, 0.5236, 2.7576, 2.8798, 0.5236, 0.2618, + 2.618, 0.52, 0.52, + 2.6704, 2.2515, 2.618, 2.0944, + 1.972222054, 1.61443, 1.61443, + 2.6704, 1.5882, 2.618, 2.0944, + 1.972222054, 1.61443, 1.61443] +motor_vel_limit_list: [32.0, 32.0, 32.0, 20.0, 37.0, 37.0, + 32.0, 32.0, 32.0, 20.0, 37.0, 37.0, + 32.0, 37.0, 37.0, + 37.0, 37.0, 37.0, 37.0, + 37.0, 22.0, 22.0, + 37.0, 37.0, 37.0, 37.0, + 37.0, 22.0, 22.0] +motor_effort_limit_list: [88.0, 88.0, 88.0, 139.0, 50.0, 50.0, + 88.0, 88.0, 88.0, 139.0, 50.0, 50.0, + 88.0, 50.0, 50.0, + 25.0, 25.0, 25.0, 25.0, + 25.0, 5.0, 5.0, + 2.45, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, + 25.0, 25.0, 25.0, 25.0, + 25.0, 5.0, 5.0, + 2.45, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7] +history_config: { + base_ang_vel: 4, + projected_gravity: 4, + command_lin_vel: 4, + command_ang_vel: 4, + command_base_height: 4, + command_stand: 4, + ref_upper_dof_pos: 4, + dof_pos: 4, + dof_vel: 4, + actions: 4, + # phase_time: 4, + ref_motion_phase: 4, + sin_phase: 4, + cos_phase: 4 + } +history_loco_config: { + base_ang_vel: 4, + projected_gravity: 4, + command_lin_vel: 4, + command_ang_vel: 4, + # command_base_height: 4, + command_stand: 4, + ref_upper_dof_pos: 4, + dof_pos: 4, + dof_vel: 4, + actions: 4, + # phase_time: 4, + sin_phase: 4, + cos_phase: 4 + } +history_loco_height_config: { + base_ang_vel: 4, + projected_gravity: 4, + command_lin_vel: 4, + command_ang_vel: 4, + command_base_height: 4, + command_stand: 4, + ref_upper_dof_pos: 4, + dof_pos: 4, + dof_vel: 4, + actions: 4, + # phase_time: 4, + sin_phase: 4, + cos_phase: 4 + } +history_mimic_config: { + base_ang_vel: 4, + projected_gravity: 4, + dof_pos: 4, + dof_vel: 4, + actions: 4, + ref_motion_phase: 4, + } +obs_dims: { + base_lin_vel: 3, + base_ang_vel: 3, + projected_gravity: 3, + command_lin_vel: 2, + command_ang_vel: 1, + command_stand: 1, + command_base_height: 1, + ref_upper_dof_pos: 17, # upper body actions + dof_pos: 29, + dof_vel: 29, + # actions: 12, # lower body actions + actions: 29, # full body actions + phase_time: 1, + ref_motion_phase: 1, # mimic motion phase + sin_phase: 1, + cos_phase: 1, + } +obs_loco_dims: { + base_lin_vel: 3, + base_ang_vel: 3, + projected_gravity: 3, + command_lin_vel: 2, + command_ang_vel: 1, + command_stand: 1, + command_base_height: 1, + ref_upper_dof_pos: 17, # upper body actions + dof_pos: 29, + dof_vel: 29, + actions: 12, # lower body actions + phase_time: 1, + sin_phase: 1, + cos_phase: 1, + } +obs_mimic_dims: { + base_lin_vel: 3, + base_ang_vel: 3, + projected_gravity: 3, + dof_pos: 29, + dof_vel: 29, + actions: 29, # full body actions + ref_motion_phase: 1, # mimic motion phase + } +obs_scales: { + base_lin_vel: 2.0, + base_ang_vel: 0.25, + projected_gravity: 1.0, + command_lin_vel: 1, + command_ang_vel: 1, + command_stand: 1, + command_base_height: 2, # Yuanhang: it's 2, not 1! + ref_upper_dof_pos: 1.0, + dof_pos: 1.0, + dof_vel: 0.05, + history: 1.0, + history_loco: 1.0, + history_mimic: 1.0, + actions: 1.0, + phase_time: 1.0, + ref_motion_phase: 1.0, + sin_phase: 1.0, + cos_phase: 1.0 + } + +loco_upper_body_dof_pos: [ + 0.0, 0.0, 0.0, # waist + 0.0, 0.3, 0.0, 1.0, # left shoulder and elbow + 0.0, 0.0, 0.0, # left wrist + 0.0, -0.3, 0.0, 1.0, # right shoulder and elbow + 0.0, 0.0, 0.0 # right wrist +] + +robot_dofs: { + "g1_29dof": [1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, + 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1], + "g1_29dof_anneal_23dof": [1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, + 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 0], +} + +mimic_robot_types: { + + "APT_level1": "g1_29dof_anneal_23dof", +} + + + + +# 01281657 +mimic_models: { + "APT_level1": "20250116_225127-TairanTestbed_G129dofANNEAL23dof_dm_APT_video_APT_level1_MinimalFriction-0.3_RfiTrue_Far0.325_RESUME_LARGENOISE-motion_tracking-g1_29dof_anneal_23dof/exported/model_176500.onnx", + +} + + + +start_upper_body_dof_pos: { + + "APT_level1": + [0.19964170455932617, 0.07710712403059006, -0.2882401943206787, + 0.21672365069389343, 0.15629297494888306, -0.5167576670646667, 0.5782126784324646, + 0.0, 0.0, 0.0, + 0.25740593671798706, -0.2504104673862457, 0.22500675916671753, 0.5127624273300171, + 0.0, 0.0, 0.0], + +} + +motion_length_s: { + "APT_level1": 7.66, + +} diff --git a/gr00t_wbc/control/main/teleop/configs/g1_gear_wbc.yaml b/gr00t_wbc/control/main/teleop/configs/g1_gear_wbc.yaml new file mode 100644 index 0000000..4014fae --- /dev/null +++ b/gr00t_wbc/control/main/teleop/configs/g1_gear_wbc.yaml @@ -0,0 +1,45 @@ + +# Simulation parameters +simulation_duration: 60.0 +simulation_dt: 0.002 +control_decimation: 10 + +# PD gains +kps: [ + 150, 150, 150, 300, 40, 40, + 150, 150, 150, 300, 40, 40, + 250, 250, 250, + 100, 100, 40, 40, 20, 20, 20, + 100, 100, 40, 40, 20, 20, 20 +] +kds: [ + 2, 2, 2, 4, 2, 2, + 2, 2, 2, 4, 2, 2, + 5, 5, 5, + 5, 5, 2, 2, 2, 2, 2, + 5, 5, 2, 2, 2, 2, 2 +] + +# Default joint angles for legs +default_angles: [-0.1, 0.0, 0.0, 0.3, -0.2, 0.0, + -0.1, 0.0, 0.0, 0.3, -0.2, 0.0, + 0.0, 0.0, 0.0] +# Scaling factors +ang_vel_scale: 0.25 +dof_pos_scale: 1.0 +dof_vel_scale: 0.05 +action_scale: 0.25 +cmd_scale: [2.0, 2.0, 0.25] + +# Number of actions and observations +num_actions: 15 +num_obs: 570 # 76 * 6 (observation dimension * history length) +obs_history_len: 6 + +# Initial commands +cmd_init: [0.0, 0.0, 0.0] +height_cmd: 0.74 +freq_cmd: 1.50 +roll_cmd: 0.0 +pitch_cmd: 0.0 +yaw_cmd: 0.0 \ No newline at end of file diff --git a/gr00t_wbc/control/main/teleop/configs/identifiers.py b/gr00t_wbc/control/main/teleop/configs/identifiers.py new file mode 100644 index 0000000..0f9b3ad --- /dev/null +++ b/gr00t_wbc/control/main/teleop/configs/identifiers.py @@ -0,0 +1,14 @@ +""" +Constants required during data collection, such as operator usernames and robot IDs. +Additional constants needed for data collection can be added to this file. +""" + +OPERATOR_USERNAMES = [ + "NEW_USER", +] + +G1_ROBOT_IDS = [ + "sim", + "0001", + "0002", +] diff --git a/gr00t_wbc/control/main/teleop/playback_sync_sim_data.py b/gr00t_wbc/control/main/teleop/playback_sync_sim_data.py new file mode 100644 index 0000000..9afbb20 --- /dev/null +++ b/gr00t_wbc/control/main/teleop/playback_sync_sim_data.py @@ -0,0 +1,627 @@ +""" +A convenience script to playback random demonstrations using the gr00t_wbc controller from +a set of demonstrations stored in a hdf5 file. + +Arguments: + --dataset (str): Path to demonstrations + --use-actions (optional): If this flag is provided, the actions are played back + through the MuJoCo simulator, instead of loading the simulator states + one by one. + --use-wbc-goals (optional): If set, will use the stored WBC goals to control the robot, + otherwise will use the actions directly. Only relevant if --use-actions is set. + --use-teleop-cmd (optional): If set, will use teleop IK directly with WBC timing + for action generation. Only relevant if --use-actions is set. + --visualize-gripper (optional): If set, will visualize the gripper site + --save-video (optional): If set, will save video of the playback using offscreen rendering + --video-path (optional): Path to save the output video. If not specified, will use the nearest + folder to dataset and save as playback_video.mp4 + --num-episodes (optional): Number of episodes to playback/record (if None, plays random episodes) + +Example: + $ python gr00t_wbc/control/main/teleop/playback_sync_sim_data.py --dataset output/robocasa_datasets/ + --use-actions --use-wbc-goals + + $ python gr00t_wbc/control/main/teleop/playback_sync_sim_data.py --dataset output/robocasa_datasets/ + --use-actions --use-teleop-cmd + + # Record video of the first 5 episodes using WBC goals + $ python gr00t_wbc/control/main/teleop/playback_sync_sim_data.py --dataset output/robocasa_datasets/ + --use-actions --use-wbc-goals --save-video --num-episodes 5 +""" + +import json +import os +from pathlib import Path +import time +from typing import Optional + +import cv2 +import numpy as np +import rclpy +from robosuite.environments.robot_env import RobotEnv +from tqdm import tqdm +import tyro + +from gr00t_wbc.control.main.teleop.configs.configs import SyncSimPlaybackConfig +from gr00t_wbc.control.robot_model.instantiation import get_robot_type_and_model +from gr00t_wbc.control.utils.sync_sim_utils import ( + generate_frame, + get_data_exporter, + get_env, + get_policies, +) +from gr00t_wbc.data.constants import RS_VIEW_CAMERA_HEIGHT, RS_VIEW_CAMERA_WIDTH +from gr00t_wbc.data.exporter import TypedLeRobotDataset + +CONTROL_NODE_NAME = "playback_node" +GREEN_BOLD = "\033[1;32m" +RED_BOLD = "\033[1;31m" +RESET = "\033[0m" + + +def load_lerobot_dataset(root_path, max_episodes=None): + task_name = None + episodes = [] + start_index = 0 + with open(Path(root_path) / "meta/episodes.jsonl", "r") as f: + for line in f: + episode = json.loads(line) + episode["start_index"] = start_index + start_index += episode["length"] + assert ( + task_name is None or task_name == episode["tasks"][0] + ), "All episodes should have the same task name" + task_name = episode["tasks"][0] + episodes.append(episode) + + dataset = TypedLeRobotDataset( + repo_id="tmp/test", + root=root_path, + load_video=False, + ) + + script_config = dataset.meta.info["script_config"] + + assert len(dataset) == start_index, "Dataset length does not match expected length" + + # Limit episodes if specified + if max_episodes is not None: + episodes = episodes[:max_episodes] + print( + f"Loading only first {len(episodes)} episodes (limited by max_episodes={max_episodes})" + ) + + f = {} + seeds = [] + for ep in tqdm(range(len(episodes))): + seed = None + f[f"data/demo_{ep + 1}/states"] = [] + f[f"data/demo_{ep + 1}/actions"] = [] + f[f"data/demo_{ep + 1}/teleop_cmd"] = [] + f[f"data/demo_{ep + 1}/wbc_goal"] = [] + start_index = episodes[ep]["start_index"] + end_index = start_index + episodes[ep]["length"] + for i in tqdm(range(start_index, end_index)): + frame = dataset[i] + # load the seed + assert ( + seed is None or seed == np.array(frame["observation.sim.seed"]).item() + ), "All observations in an episode should have the same seed" + seed = np.array(frame["observation.sim.seed"]).item() + # load the state + mujoco_state_len = frame["observation.sim.mujoco_state_len"] + mujoco_state = frame["observation.sim.mujoco_state"] + f[f"data/demo_{ep + 1}/states"].append(np.array(mujoco_state[:mujoco_state_len])) + # load the action + action = frame["action"] + f[f"data/demo_{ep + 1}/actions"].append(np.array(action)) + + # load the teleop command + teleop_cmd = { + "left_wrist": np.array(frame["observation.sim.left_wrist"].reshape(4, 4)), + "right_wrist": np.array(frame["observation.sim.right_wrist"].reshape(4, 4)), + "left_fingers": { + "position": np.array(frame["observation.sim.left_fingers"].reshape(25, 4, 4)), + }, + "right_fingers": { + "position": np.array(frame["observation.sim.right_fingers"].reshape(25, 4, 4)), + }, + "target_upper_body_pose": np.array(frame["observation.sim.target_upper_body_pose"]), + "base_height_command": np.array(frame["teleop.base_height_command"]), + "navigate_cmd": np.array(frame["teleop.navigate_command"]), + } + f[f"data/demo_{ep + 1}/teleop_cmd"].append(teleop_cmd) + # load the WBC goal + wbc_goal = { + "wrist_pose": np.array(frame["action.eef"]), + "target_upper_body_pose": np.array(frame["observation.sim.target_upper_body_pose"]), + "navigate_cmd": np.array(frame["teleop.navigate_command"]), + "base_height_command": np.array(frame["teleop.base_height_command"]), + } + f[f"data/demo_{ep + 1}/wbc_goal"].append(wbc_goal) + + seeds.append(seed) + + return seeds, f, script_config + + +def validate_state(recorded_state, playback_state, ep, step, tolerance=1e-5): + """Validate that playback state matches recorded state within tolerance.""" + if not np.allclose(recorded_state, playback_state, atol=tolerance): + err = np.linalg.norm(recorded_state - playback_state) + print(f"[warning] state diverged by {err:.12f} for ep {ep} at step {step}") + return False + return True + + +def generate_and_save_frame( + config, sync_env, obs, wbc_action, seed, teleop_cmd, wbc_goal, gr00t_exporter +): + """Generate and save a frame to LeRobot dataset if enabled.""" + if config.save_lerobot: + max_mujoco_state_len, mujoco_state_len, mujoco_state = sync_env.get_mujoco_state_info() + frame = generate_frame( + obs, + wbc_action, + seed, + mujoco_state, + mujoco_state_len, + max_mujoco_state_len, + teleop_cmd, + wbc_goal, + config.save_img_obs, + ) + gr00t_exporter.add_frame(frame) + + +def playback_wbc_goals( + sync_env, + wbc_policy, + wbc_goals, + teleop_cmds, + states, + env, + onscreen, + config, + video_writer, + ep, + seed, + gr00t_exporter, + end_steps, +): + """Playback using WBC goals to control the robot.""" + ret = True + num_wbc_goals = len(wbc_goals) if end_steps == -1 else min(end_steps, len(wbc_goals)) + + for jj in range(num_wbc_goals): + wbc_goal = wbc_goals[jj] + obs = sync_env.observe() + wbc_policy.set_observation(obs) + wbc_policy.set_goal(wbc_goal) + wbc_action = wbc_policy.get_action() + sync_env.queue_action(wbc_action) + + # Save frame if needed + if config.save_lerobot: + teleop_cmd = teleop_cmds[jj] + generate_and_save_frame( + config, sync_env, obs, wbc_action, seed, teleop_cmd, wbc_goal, gr00t_exporter + ) + + capture_or_render_frame(env, onscreen, config, video_writer) + + if jj < len(states) - 1: + state_playback = env.sim.get_state().flatten() + if not validate_state(states[jj + 1], state_playback, ep, jj): + ret = False + + return ret + + +def playback_teleop_cmd( + sync_env, + wbc_policy, + teleop_policy, + wbc_goals, + teleop_cmds, + states, + env, + onscreen, + config, + video_writer, + ep, + seed, + gr00t_exporter, + end_steps, +): + """Playback using teleop commands to control the robot.""" + ret = True + num_steps = len(wbc_goals) if end_steps == -1 else min(end_steps, len(wbc_goals)) + + for jj in range(num_steps): + wbc_goal = wbc_goals[jj] + teleop_cmd = teleop_cmds[jj] + + # Set IK goal from teleop command + ik_data = { + "body_data": { + teleop_policy.retargeting_ik.body.supplemental_info.hand_frame_names[ + "left" + ]: teleop_cmd["left_wrist"], + teleop_policy.retargeting_ik.body.supplemental_info.hand_frame_names[ + "right" + ]: teleop_cmd["right_wrist"], + }, + "left_hand_data": teleop_cmd["left_fingers"], + "right_hand_data": teleop_cmd["right_fingers"], + } + teleop_policy.retargeting_ik.set_goal(ik_data) + + # Store original and get new upper body pose + target_upper_body_pose = wbc_goal["target_upper_body_pose"].copy() + wbc_goal["target_upper_body_pose"] = teleop_policy.retargeting_ik.get_action() + + # Execute WBC policy + obs = sync_env.observe() + wbc_policy.set_observation(obs) + wbc_policy.set_goal(wbc_goal) + wbc_action = wbc_policy.get_action() + sync_env.queue_action(wbc_action) + + # Save frame if needed + generate_and_save_frame( + config, sync_env, obs, wbc_action, seed, teleop_cmd, wbc_goal, gr00t_exporter + ) + + # Render or capture frame + capture_or_render_frame(env, onscreen, config, video_writer) + + # Validate states + if jj < len(states) - 1: + if not np.allclose( + target_upper_body_pose, wbc_goal["target_upper_body_pose"], atol=1e-5 + ): + err = np.linalg.norm(target_upper_body_pose - wbc_goal["target_upper_body_pose"]) + print( + f"[warning] target_upper_body_pose diverged by {err:.12f} for ep {ep} at step {jj}" + ) + ret = False + + state_playback = env.sim.get_state().flatten() + if not validate_state(states[jj + 1], state_playback, ep, jj): + ret = False + + return ret + + +def playback_actions( + sync_env, + actions, + teleop_cmds, + wbc_goals, + states, + env, + onscreen, + config, + video_writer, + ep, + seed, + gr00t_exporter, + end_steps, +): + """Playback using actions directly.""" + ret = True + num_actions = len(actions) if end_steps == -1 else min(end_steps, len(actions)) + + for j in range(num_actions): + sync_env.queue_action({"q": actions[j]}) + + # Save frame if needed + if config.save_lerobot: + obs = sync_env.observe() + teleop_cmd = teleop_cmds[j] + wbc_goal = wbc_goals[j] + wbc_action = {"q": actions[j]} + generate_and_save_frame( + config, sync_env, obs, wbc_action, seed, teleop_cmd, wbc_goal, gr00t_exporter + ) + + capture_or_render_frame(env, onscreen, config, video_writer) + + if j < len(states) - 1: + state_playback = env.sim.get_state().flatten() + if not validate_state(states[j + 1], state_playback, ep, j): + ret = False + + return ret + + +def playback_states( + sync_env, + states, + actions, + teleop_cmds, + wbc_goals, + env, + onscreen, + config, + video_writer, + seed, + gr00t_exporter, + end_steps, + ep, +): + """Playback by forcing mujoco states directly.""" + ret = True + num_states = len(states) if end_steps == -1 else min(end_steps, len(states)) + + for i in range(num_states): + sync_env.reset_to({"states": states[i]}) + sync_env.render() + + # Validate that the state was set correctly + if i < len(states): + state_playback = env.sim.get_state().flatten() + if not validate_state(states[i], state_playback, ep, i): + ret = False + + # Save frame if needed + if config.save_lerobot: + obs = sync_env.observe() + teleop_cmd = teleop_cmds[i] + wbc_goal = wbc_goals[i] + wbc_action = {"q": actions[i]} + generate_and_save_frame( + config, sync_env, obs, wbc_action, seed, teleop_cmd, wbc_goal, gr00t_exporter + ) + + capture_or_render_frame(env, onscreen, config, video_writer) + + return ret + + +def main(config: SyncSimPlaybackConfig): + ret = True + start_time = time.time() + + np.set_printoptions(precision=5, suppress=True, linewidth=120) + + assert config.dataset is not None, "Folder must be specified for playback" + + seeds, f, script_config = load_lerobot_dataset(config.dataset) + + config.update( + script_config, + allowed_keys=[ + "wbc_version", + "wbc_model_path", + "wbc_policy_class", + "control_frequency", + "enable_waist", + "with_hands", + "env_name", + "robot", + "task_name", + "teleop_frequency", + "data_collection_frequency", + "enable_gravity_compensation", + "gravity_compensation_joints", + ], + ) + config.validate_args() + + robot_type, robot_model = get_robot_type_and_model(config.robot, config.enable_waist) + + # Setup rendering + if config.save_video or config.save_img_obs: + onscreen = False + offscreen = True + else: + onscreen = True + offscreen = False + + # Set default video path if not specified + if config.save_video and config.video_path is None: + if os.path.isfile(config.dataset): + video_folder = Path(config.dataset).parent + else: + video_folder = Path(config.dataset) + video_folder.mkdir(parents=True, exist_ok=True) + config.video_path = str(video_folder / "playback_video.mp4") + print(f"Video recording enabled. Output: {config.video_path}") + + sync_env = get_env(config, onscreen=onscreen, offscreen=offscreen) + + gr00t_exporter = None + if config.save_lerobot: + obs = sync_env.observe() + gr00t_exporter = get_data_exporter(config, obs, robot_model) + + # Initialize policies + wbc_policy, teleop_policy = get_policies( + config, robot_type, robot_model, activate_keyboard_listener=False + ) + + # List of all demonstrations episodes + demos = [f"demo_{i + 1}" for i in range(len(seeds))] + print(f"Loaded and will playback {len(demos)} episodes") + env = sync_env.base_env + + # Setup video writer + video_writer = None + fourcc = None + if config.save_video: + fourcc = cv2.VideoWriter_fourcc(*"mp4v") + video_writer = cv2.VideoWriter( + config.video_path, fourcc, 20, (RS_VIEW_CAMERA_WIDTH, RS_VIEW_CAMERA_HEIGHT) + ) + + print("Loaded {} episodes from {}".format(len(demos), config.dataset)) + print("seeds:", seeds) + print("demos:", demos, "\n\n") + + # Handle episode selection - either limited number or infinite random + max_episodes = len(demos) + episode_count = 0 + while True: + if episode_count >= max_episodes: + break + ep = demos[episode_count] + print(f"Playing back episode: {ep}") + episode_count += 1 + + # read the model xml, using the metadata stored in the attribute for this episode + seed = seeds[int(ep.split("_")[-1]) - 1] + sync_env.reset(seed=seed) + + # load the actions and states + states = f["data/{}/states".format(ep)] + actions = f["data/{}/actions".format(ep)] + teleop_cmds = f["data/{}/teleop_cmd".format(ep)] + wbc_goals = f["data/{}/wbc_goal".format(ep)] + + # reset the policies + wbc_policy, teleop_policy, _ = get_policies( + config, robot_type, robot_model, activate_keyboard_listener=False + ) + end_steps = 20 if config.ci_test else -1 + + if config.use_actions: + # load the initial state + sync_env.reset_to({"states": states[0]}) + # load the actions and play them back open-loop + if config.use_wbc_goals: + # use the wbc_goals to control the robot + episode_ret = playback_wbc_goals( + sync_env, + wbc_policy, + wbc_goals, + teleop_cmds, + states, + env, + onscreen, + config, + video_writer, + ep, + seed, + gr00t_exporter, + end_steps, + ) + ret = ret and episode_ret + elif config.use_teleop_cmd: + # use the teleop commands to control the robot + episode_ret = playback_teleop_cmd( + sync_env, + wbc_policy, + teleop_policy, + wbc_goals, + teleop_cmds, + states, + env, + onscreen, + config, + video_writer, + ep, + seed, + gr00t_exporter, + end_steps, + ) + ret = ret and episode_ret + else: + episode_ret = playback_actions( + sync_env, + actions, + teleop_cmds, + wbc_goals, + states, + env, + onscreen, + config, + video_writer, + ep, + seed, + gr00t_exporter, + end_steps, + ) + ret = ret and episode_ret + else: + # force the sequence of internal mujoco states one by one + episode_ret = playback_states( + sync_env, + states, + actions, + teleop_cmds, + wbc_goals, + env, + onscreen, + config, + video_writer, + seed, + gr00t_exporter, + end_steps, + ep, + ) + ret = ret and episode_ret + + if config.save_lerobot: + gr00t_exporter.save_episode() + + print(f"Episode {ep} playback finished.\n\n") + + # close the env + sync_env.close() + + # Cleanup + if video_writer is not None: + video_writer.release() + print(f"Video saved to: {config.video_path}") + + end_time = time.time() + elapsed_time = end_time - start_time + + if config.save_lerobot: + print(f"LeRobot dataset saved to: {gr00t_exporter.root}") + + print( + f"{GREEN_BOLD}Playback with WBC version: {config.wbc_version}, {config.wbc_model_path}, " + f"{config.wbc_policy_class}, use_actions: {config.use_actions}, use_wbc_goals: {config.use_wbc_goals}, " + f"use_teleop_cmd: {config.use_teleop_cmd}{RESET}" + ) + if ret: + print(f"{GREEN_BOLD}Playback completed successfully in {elapsed_time:.2f} seconds!{RESET}") + else: + print(f"{RED_BOLD}Playback encountered an error in {elapsed_time:.2f} seconds!{RESET}") + + return ret + + +def capture_or_render_frame( + env: RobotEnv, + onscreen: bool, + config: SyncSimPlaybackConfig, + video_writer: Optional[cv2.VideoWriter], +): + """Capture frame for video recording if enabled, or render the environment.""" + if config.save_video: + if hasattr(env, "sim") and hasattr(env.sim, "render"): + img = env.sim.render( + width=RS_VIEW_CAMERA_WIDTH, + height=RS_VIEW_CAMERA_HEIGHT, + camera_name=env.render_camera[0], + ) + img_bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) + img_bgr = np.flipud(img_bgr) + video_writer.write(img_bgr) + elif onscreen: + env.render() + + +if __name__ == "__main__": + config = tyro.cli(SyncSimPlaybackConfig) + + rclpy.init(args=None) + node = rclpy.create_node("playback_gr00t_wbc_control") + + main(config) + + rclpy.shutdown() diff --git a/gr00t_wbc/control/main/teleop/run_camera_viewer.py b/gr00t_wbc/control/main/teleop/run_camera_viewer.py new file mode 100644 index 0000000..298bebb --- /dev/null +++ b/gr00t_wbc/control/main/teleop/run_camera_viewer.py @@ -0,0 +1,249 @@ +""" +Camera viewer with manual recording support. + +This script provides a camera viewer that can display multiple camera streams +and record them to video files with manual start/stop controls. + +Features: +- Onscreen mode: Display camera feeds with optional recording +- Offscreen mode: No display, recording only when triggered +- Manual recording control with keyboard (R key to start/stop) + +Usage Examples: + +1. Basic onscreen viewing (with recording capability): + python run_camera_viewer.py --camera-host localhost --camera-port 5555 + +2. Offscreen mode (no display, recording only): + python run_camera_viewer.py --offscreen --camera-host localhost --camera-port 5555 + +3. Custom output directory: + python run_camera_viewer.py --output-path ./my_recordings --camera-host localhost + +Controls: +- R key: Start/Stop recording +- Q key: Quit application + +Output Structure: +camera_output_20241211_143052/ +├── rec_143205/ +│ ├── ego_view_color_image.mp4 +│ ├── head_left_color_image.mp4 +│ └── head_right_color_image.mp4 +└── rec_143410/ + ├── ego_view_color_image.mp4 + └── head_left_color_image.mp4 +""" + +from dataclasses import dataclass +from pathlib import Path +import threading +import time +from typing import Any, Optional + +import cv2 +import rclpy +from sshkeyboard import listen_keyboard, stop_listening +import tyro + +from gr00t_wbc.control.main.teleop.configs.configs import ComposedCameraClientConfig +from gr00t_wbc.control.sensor.composed_camera import ComposedCameraClientSensor +from gr00t_wbc.control.utils.img_viewer import ImageViewer + + +@dataclass +class CameraViewerConfig(ComposedCameraClientConfig): + """Config for running the camera viewer with recording support.""" + + offscreen: bool = False + """Run in offscreen mode (no display, manual recording with R key).""" + + output_path: Optional[str] = None + """Output path for saving videos. If None, auto-generates path.""" + + codec: str = "mp4v" + """Video codec to use for saving (e.g., 'mp4v', 'XVID').""" + + +ArgsConfig = CameraViewerConfig + + +def _get_camera_titles(image_data: dict[str, Any]) -> list[str]: + """ + Detect all the individual camera streams from the image data. + + schema format: + { + "timestamps": {"ego_view": 123.45, "ego_view_left_mono": 123.46}, + "images": {"ego_view": np.ndarray, "ego_view_left_mono": np.ndarray} + } + + Returns list of camera keys (e.g., ["ego_view", "ego_view_left_mono", "ego_view_right_mono"]) + """ + # Extract all camera keys from the images dictionary + camera_titles = list(image_data.get("images", {}).keys()) + return camera_titles + + +def main(config: ArgsConfig): + """Main function to run the camera viewer.""" + # Initialize ROS + rclpy.init(args=None) + node = rclpy.create_node("camera_viewer") + + # Start ROS spin in a separate thread + thread = threading.Thread(target=rclpy.spin, args=(node,), daemon=True) + thread.start() + + image_sub = ComposedCameraClientSensor(server_ip=config.camera_host, port=config.camera_port) + + # pre-fetch a sample image to get the number of camera angles + retry_count = 0 + while True: + _sample_image = image_sub.read() + if _sample_image: + break + retry_count += 1 + time.sleep(0.1) + if retry_count > 10: + raise Exception("Failed to get sample image") + + camera_titles = _get_camera_titles(_sample_image) + + # Setup output directory + if config.output_path is None: + output_dir = Path("camera_recordings") + else: + output_dir = Path(config.output_path) + + # Recording state + is_recording = False + video_writers = {} + frame_count = 0 + recording_start_time = None + should_quit = False + + def on_press(key): + nonlocal is_recording, video_writers, frame_count, recording_start_time, should_quit + + if key == "r": + if not is_recording: + # Start recording + recording_dir = output_dir / f"rec_{time.strftime('%Y%m%d_%H%M%S')}" + recording_dir.mkdir(parents=True, exist_ok=True) + + # Create video writers + fourcc = cv2.VideoWriter_fourcc(*config.codec) + video_writers = {} + + for title in camera_titles: + img = _sample_image["images"].get(title) + if img is not None: + height, width = img.shape[:2] + video_path = recording_dir / f"{title}.mp4" + writer = cv2.VideoWriter( + str(video_path), fourcc, config.fps, (width, height) + ) + video_writers[title] = writer + + is_recording = True + recording_start_time = time.time() + frame_count = 0 + print(f"🔴 Recording started: {recording_dir}") + else: + # Stop recording + is_recording = False + for title, writer in video_writers.items(): + writer.release() + video_writers = {} + + duration = time.time() - recording_start_time if recording_start_time else 0 + print(f"⏹️ Recording stopped - {duration:.1f}s, {frame_count} frames") + elif key == "q": + should_quit = True + stop_listening() + + # Setup keyboard listener in a separate thread + keyboard_thread = threading.Thread( + target=lambda: listen_keyboard(on_press=on_press), daemon=True + ) + keyboard_thread.start() + + # Setup viewer for onscreen mode + viewer = None + if not config.offscreen: + viewer = ImageViewer( + title="Camera Viewer", + figsize=(10, 8), + num_images=len(camera_titles), + image_titles=camera_titles, + ) + + # Print instructions + mode = "Offscreen" if config.offscreen else "Onscreen" + print(f"{mode} mode - Target FPS: {config.fps}") + print(f"Videos will be saved to: {output_dir}") + print("Controls: R key to start/stop recording, Q key to quit, Ctrl+C to exit") + + # Create ROS rate controller + rate = node.create_rate(config.fps) + + try: + while rclpy.ok() and not should_quit: + # Get images from all subscribers + images = [] + image_data = image_sub.read() + if image_data: + for title in camera_titles: + img = image_data["images"].get(title) + images.append(img) + + # Save frame if recording + if is_recording and img is not None and title in video_writers: + # Convert from RGB to BGR for OpenCV + if len(img.shape) == 3 and img.shape[2] == 3: + img_bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) + else: + img_bgr = img + video_writers[title].write(img_bgr) + + # Display images if not offscreen + if not config.offscreen and viewer and any(img is not None for img in images): + status = "🔴 REC" if is_recording else "⏸️ Ready" + viewer._fig.suptitle(f"Camera Viewer - {status}") + viewer.show_multiple(images) + + # Progress feedback + if is_recording: + frame_count += 1 + if frame_count % 100 == 0: + duration = time.time() - recording_start_time + print(f"Recording: {frame_count} frames ({duration:.1f}s)") + + rate.sleep() + + except KeyboardInterrupt: + print("\nExiting...") + finally: + # Cleanup + try: + stop_listening() + except Exception: + pass + + if video_writers: + for title, writer in video_writers.items(): + writer.release() + if is_recording: + duration = time.time() - recording_start_time + print(f"Final: {duration:.1f}s, {frame_count} frames") + + if viewer: + viewer.close() + + rclpy.shutdown() + + +if __name__ == "__main__": + config = tyro.cli(ArgsConfig) + main(config) diff --git a/gr00t_wbc/control/main/teleop/run_g1_control_loop.py b/gr00t_wbc/control/main/teleop/run_g1_control_loop.py new file mode 100644 index 0000000..17cf2b1 --- /dev/null +++ b/gr00t_wbc/control/main/teleop/run_g1_control_loop.py @@ -0,0 +1,236 @@ +from copy import deepcopy +import time + +import tyro + +from gr00t_wbc.control.envs.g1.g1_env import G1Env +from gr00t_wbc.control.main.constants import ( + CONTROL_GOAL_TOPIC, + DEFAULT_BASE_HEIGHT, + DEFAULT_NAV_CMD, + DEFAULT_WRIST_POSE, + JOINT_SAFETY_STATUS_TOPIC, + LOWER_BODY_POLICY_STATUS_TOPIC, + ROBOT_CONFIG_TOPIC, + STATE_TOPIC_NAME, +) +from gr00t_wbc.control.main.teleop.configs.configs import ControlLoopConfig +from gr00t_wbc.control.policy.wbc_policy_factory import get_wbc_policy +from gr00t_wbc.control.robot_model.instantiation.g1 import ( + instantiate_g1_robot_model, +) +from gr00t_wbc.control.utils.keyboard_dispatcher import ( + KeyboardDispatcher, + KeyboardEStop, + KeyboardListenerPublisher, + ROSKeyboardDispatcher, +) +from gr00t_wbc.control.utils.ros_utils import ( + ROSManager, + ROSMsgPublisher, + ROSMsgSubscriber, + ROSServiceServer, +) +from gr00t_wbc.control.utils.telemetry import Telemetry + +CONTROL_NODE_NAME = "ControlPolicy" + + +def main(config: ControlLoopConfig): + ros_manager = ROSManager(node_name=CONTROL_NODE_NAME) + node = ros_manager.node + + # start the robot config server + ROSServiceServer(ROBOT_CONFIG_TOPIC, config.to_dict()) + + wbc_config = config.load_wbc_yaml() + + data_exp_pub = ROSMsgPublisher(STATE_TOPIC_NAME) + lower_body_policy_status_pub = ROSMsgPublisher(LOWER_BODY_POLICY_STATUS_TOPIC) + joint_safety_status_pub = ROSMsgPublisher(JOINT_SAFETY_STATUS_TOPIC) + + # Initialize telemetry + telemetry = Telemetry(window_size=100) + + waist_location = "lower_and_upper_body" if config.enable_waist else "lower_body" + robot_model = instantiate_g1_robot_model( + waist_location=waist_location, high_elbow_pose=config.high_elbow_pose + ) + + env = G1Env( + env_name=config.env_name, + robot_model=robot_model, + config=wbc_config, + wbc_version=config.wbc_version, + ) + if env.sim and not config.sim_sync_mode: + env.start_simulator() + + wbc_policy = get_wbc_policy("g1", robot_model, wbc_config, config.upper_body_joint_speed) + + keyboard_listener_pub = KeyboardListenerPublisher() + keyboard_estop = KeyboardEStop() + if config.keyboard_dispatcher_type == "raw": + dispatcher = KeyboardDispatcher() + elif config.keyboard_dispatcher_type == "ros": + dispatcher = ROSKeyboardDispatcher() + else: + raise ValueError( + f"Invalid keyboard dispatcher: {config.keyboard_dispatcher_type}, please use 'raw' or 'ros'" + ) + dispatcher.register(env) + dispatcher.register(wbc_policy) + dispatcher.register(keyboard_listener_pub) + dispatcher.register(keyboard_estop) + dispatcher.start() + + rate = node.create_rate(config.control_frequency) + + upper_body_policy_subscriber = ROSMsgSubscriber(CONTROL_GOAL_TOPIC) + + last_teleop_cmd = None + try: + while ros_manager.ok(): + t_start = time.monotonic() + with telemetry.timer("total_loop"): + # Step simulator if in sync mode + with telemetry.timer("step_simulator"): + if env.sim and config.sim_sync_mode: + env.step_simulator() + + # Measure observation time + with telemetry.timer("observe"): + obs = env.observe() + wbc_policy.set_observation(obs) + + # Measure policy setup time + with telemetry.timer("policy_setup"): + upper_body_cmd = upper_body_policy_subscriber.get_msg() + + t_now = time.monotonic() + + wbc_goal = {} + if upper_body_cmd: + wbc_goal = upper_body_cmd.copy() + last_teleop_cmd = upper_body_cmd.copy() + if config.ik_indicator: + env.set_ik_indicator(upper_body_cmd) + # Send goal to policy + if wbc_goal: + wbc_goal["interpolation_garbage_collection_time"] = t_now - 2 * ( + 1 / config.control_frequency + ) + wbc_policy.set_goal(wbc_goal) + + # Measure policy action calculation time + with telemetry.timer("policy_action"): + wbc_action = wbc_policy.get_action(time=t_now) + + # Measure action queue time + with telemetry.timer("queue_action"): + env.queue_action(wbc_action) + + # Publish status information for InteractiveModeController + with telemetry.timer("publish_status"): + # Get policy status - check if the lower body policy has use_policy_action enabled + policy_use_action = False + try: + # Access the lower body policy through the decoupled whole body policy + if hasattr(wbc_policy, "lower_body_policy"): + policy_use_action = getattr( + wbc_policy.lower_body_policy, "use_policy_action", False + ) + except (AttributeError, TypeError): + policy_use_action = False + + policy_status_msg = {"use_policy_action": policy_use_action, "timestamp": t_now} + lower_body_policy_status_pub.publish(policy_status_msg) + + # Get joint safety status from G1Env (which already runs the safety monitor) + joint_safety_ok = env.get_joint_safety_status() + + joint_safety_status_msg = { + "joint_safety_ok": joint_safety_ok, + "timestamp": t_now, + } + joint_safety_status_pub.publish(joint_safety_status_msg) + + # Start or Stop data collection + if wbc_goal.get("toggle_data_collection", False): + dispatcher.handle_key("c") + + # Abort the current episode + if wbc_goal.get("toggle_data_abort", False): + dispatcher.handle_key("x") + + if env.use_sim and wbc_goal.get("reset_env_and_policy", False): + print("Resetting sim environment and policy") + # Reset teleop policy & sim env + dispatcher.handle_key("k") + + # Clear upper body commands + upper_body_policy_subscriber._msg = None + upper_body_cmd = { + "target_upper_body_pose": obs["q"][ + robot_model.get_joint_group_indices("upper_body") + ], + "wrist_pose": DEFAULT_WRIST_POSE, + "base_height_command": DEFAULT_BASE_HEIGHT, + "navigate_cmd": DEFAULT_NAV_CMD, + } + last_teleop_cmd = upper_body_cmd.copy() + + time.sleep(0.5) + + msg = deepcopy(obs) + for key in obs.keys(): + if key.endswith("_image"): + del msg[key] + + # exporting data + if last_teleop_cmd: + msg.update( + { + "action": wbc_action["q"], + "action.eef": last_teleop_cmd.get("wrist_pose", DEFAULT_WRIST_POSE), + "base_height_command": last_teleop_cmd.get( + "base_height_command", DEFAULT_BASE_HEIGHT + ), + "navigate_command": last_teleop_cmd.get( + "navigate_cmd", DEFAULT_NAV_CMD + ), + "timestamps": { + "main_loop": time.time(), + "proprio": time.time(), + }, + } + ) + data_exp_pub.publish(msg) + end_time = time.monotonic() + + if env.sim and (not env.sim.sim_thread or not env.sim.sim_thread.is_alive()): + raise RuntimeError("Simulator thread is not alive") + + rate.sleep() + + # Log timing information every 100 iterations (roughly every 2 seconds at 50Hz) + if config.verbose_timing: + # When verbose timing is enabled, always show timing + telemetry.log_timing_info(context="G1 Control Loop", threshold=0.0) + elif (end_time - t_start) > (1 / config.control_frequency) and not config.sim_sync_mode: + # Only show timing when loop is slow and verbose_timing is disabled + telemetry.log_timing_info(context="G1 Control Loop Missed", threshold=0.001) + + except ros_manager.exceptions() as e: + print(f"ROSManager interrupted by user: {e}") + finally: + print("Cleaning up...") + # the order of the following is important + dispatcher.stop() + ros_manager.shutdown() + env.close() + + +if __name__ == "__main__": + config = tyro.cli(ControlLoopConfig) + main(config) diff --git a/gr00t_wbc/control/main/teleop/run_g1_data_exporter.py b/gr00t_wbc/control/main/teleop/run_g1_data_exporter.py new file mode 100644 index 0000000..5bd95d9 --- /dev/null +++ b/gr00t_wbc/control/main/teleop/run_g1_data_exporter.py @@ -0,0 +1,364 @@ +from collections import deque +from datetime import datetime +import threading +import time + +import numpy as np +import rclpy +import tyro + +from gr00t_wbc.control.main.constants import ROBOT_CONFIG_TOPIC, STATE_TOPIC_NAME +from gr00t_wbc.control.main.teleop.configs.configs import DataExporterConfig +from gr00t_wbc.control.robot_model.instantiation import g1 +from gr00t_wbc.control.sensor.composed_camera import ComposedCameraClientSensor +from gr00t_wbc.control.utils.episode_state import EpisodeState +from gr00t_wbc.control.utils.keyboard_dispatcher import KeyboardListenerSubscriber +from gr00t_wbc.control.utils.ros_utils import ROSMsgSubscriber, ROSServiceClient +from gr00t_wbc.control.utils.telemetry import Telemetry +from gr00t_wbc.control.utils.text_to_speech import TextToSpeech +from gr00t_wbc.data.constants import BUCKET_BASE_PATH +from gr00t_wbc.data.exporter import DataCollectionInfo, Gr00tDataExporter +from gr00t_wbc.data.utils import get_dataset_features, get_modality_config + + +class TimeDeltaException(Exception): + def __init__(self, failure_count: int, reset_timeout_sec: float): + """ + Exception raised when the time delta between two messages exceeds + a threshold for a consecutive number of times + """ + self.failure_count = failure_count + self.reset_timeout_sec = reset_timeout_sec + self.message = f"{self.failure_count} failures in {self.reset_timeout_sec} seconds" + super().__init__(self.message) + + +class TimingThresholdMonitor: + def __init__(self, max_failures=3, reset_timeout_sec=5, time_delta=0.2, raise_exception=False): + """ + Monitor the time diff (between two messages) and optionally raise an exception + if there is a consistent violations + """ + self.max_failures = max_failures + self.reset_timeout_sec = reset_timeout_sec + self.failure_count = 0 + self.last_failure_time = 0 + self.time_delta = time_delta + self.raise_exception = raise_exception + + def reset(self): + self.failure_count = 0 + self.last_failure_time = 0 + + def log_time_delta(self, time_delta_sec: float): + time_delta = abs(time_delta_sec) + if time_delta > self.time_delta: + self.failure_count += 1 + self.last_failure_time = time.monotonic() + + if self.is_threshold_exceeded(): + print( + f"Time delta exception: {self.failure_count} failures in {self.reset_timeout_sec} seconds" + f", time delta: {time_delta}" + ) + if self.raise_exception: + raise TimeDeltaException(self.failure_count, self.reset_timeout_sec) + + def is_threshold_exceeded(self): + if self.failure_count >= self.max_failures: + return True + if time.monotonic() - self.last_failure_time > self.reset_timeout_sec: + self.reset() + return False + + +class Gr00tDataCollector: + def __init__( + self, + node, + camera_host: str, + camera_port: int, + state_topic_name: str, + data_exporter: Gr00tDataExporter, + text_to_speech=None, + frequency=20, + state_act_msg_frequency=50, + ): + + self.text_to_speech = text_to_speech + self.frequency = frequency + self.data_exporter = data_exporter + + self.node = node + + thread = threading.Thread(target=rclpy.spin, args=(self.node,), daemon=True) + thread.start() + time.sleep(0.5) + + self._episode_state = EpisodeState() + self._keyboard_listener = KeyboardListenerSubscriber() + self._state_subscriber = ROSMsgSubscriber(state_topic_name) + self._image_subscriber = ComposedCameraClientSensor(server_ip=camera_host, port=camera_port) + self.rate = self.node.create_rate(self.frequency) + + self.obs_act_buffer = deque(maxlen=100) + self.latest_image_msg = None + self.latest_proprio_msg = None + + self.state_polling_rate = 1 / state_act_msg_frequency + self.last_state_poll_time = time.monotonic() + + self.telemetry = Telemetry(window_size=100) + self.timing_threshold_monitor = TimingThresholdMonitor() + + print(f"Recording to {self.data_exporter.meta.root}") + + @property + def current_episode_index(self): + return self.data_exporter.episode_buffer["episode_index"] + + def _print_and_say(self, message: str, say: bool = True): + """Helper to use TextToSpeech print_and_say or fallback to print.""" + if self.text_to_speech is not None: + self.text_to_speech.print_and_say(message, say) + else: + print(message) + + def _check_keyboard_input(self): + key = self._keyboard_listener.read_msg() + if key == "c": + self._episode_state.change_state() + if self._episode_state.get_state() == self._episode_state.RECORDING: + self._print_and_say(f"Started recording {self.current_episode_index}") + elif self._episode_state.get_state() == self._episode_state.NEED_TO_SAVE: + self._print_and_say("Stopping recording, preparing to save") + elif self._episode_state.get_state() == self._episode_state.IDLE: + self._print_and_say("Saved episode and back to idle state") + elif key == "x": + if self._episode_state.get_state() == self._episode_state.RECORDING: + self.data_exporter.save_episode_as_discarded() + self._episode_state.reset_state() + self._print_and_say("Discarded episode") + + def _add_data_frame(self): + t_start = time.monotonic() + + if self.latest_proprio_msg is None or self.latest_image_msg is None: + self._print_and_say( + f"Waiting for message. " + f"Avail msg: proprio {self.latest_proprio_msg is not None} | " + f"image {self.latest_image_msg is not None}", + say=False, + ) + return False + + if self._episode_state.get_state() == self._episode_state.RECORDING: + + # Calculate max time delta between images and proprio + max_time_delta = 0 + for _, image_time in self.latest_image_msg["timestamps"].items(): + time_delta = abs(image_time - self.latest_proprio_msg["timestamps"]["proprio"]) + max_time_delta = max(max_time_delta, time_delta) + + self.timing_threshold_monitor.log_time_delta(max_time_delta) + if (self.timing_threshold_monitor.failure_count + 1) % 100 == 0: + self._print_and_say("Image state delta too high, please discard data") + + frame_data = { + "observation.state": self.latest_proprio_msg["q"], + "observation.eef_state": self.latest_proprio_msg["wrist_pose"], + "action": self.latest_proprio_msg["action"], + "action.eef": self.latest_proprio_msg["action.eef"], + "observation.img_state_delta": ( + np.array( + [max_time_delta], + dtype=np.float32, + ) + ), # lerobot only supports adding numpy arrays + "teleop.navigate_command": np.array( + self.latest_proprio_msg["navigate_command"], dtype=np.float64 + ), + "teleop.base_height_command": np.array( + [self.latest_proprio_msg["base_height_command"]], dtype=np.float64 + ), + } + + # Add images based on dataset features + images = self.latest_image_msg["images"] + for feature_name, feature_info in self.data_exporter.features.items(): + if feature_info.get("dtype") in ["image", "video"]: + # Extract image key from feature name (e.g., "observation.images.ego_view" -> "ego_view") + image_key = feature_name.split(".")[-1] + + if image_key not in images: + raise ValueError( + f"Required image '{image_key}' for feature '{feature_name}' " + f"not found in image message. Available images: {list(images.keys())}" + ) + frame_data[feature_name] = images[image_key] + + self.data_exporter.add_frame(frame_data) + + t_end = time.monotonic() + if t_end - t_start > (1 / self.frequency): + print(f"DataExporter Missed: {t_end - t_start} sec") + + if self._episode_state.get_state() == self._episode_state.NEED_TO_SAVE: + self.data_exporter.save_episode() + self.timing_threshold_monitor.reset() + self._print_and_say("Finished saving episode") + self._episode_state.change_state() + + return True + + def save_and_cleanup(self): + try: + self._print_and_say("saving episode done") + # save on going episode if any + buffer_size = self.data_exporter.episode_buffer.get("size", 0) + if buffer_size > 0: + self.data_exporter.save_episode() + self._print_and_say(f"Recording complete: {self.data_exporter.meta.root}", say=False) + except Exception as e: + self._print_and_say(f"Error saving episode: {e}") + + self.node.destroy_node() + rclpy.shutdown() + self._print_and_say("Shutting down data exporter...", say=False) + + def run(self): + try: + while rclpy.ok(): + t_start = time.monotonic() + with self.telemetry.timer("total_loop"): + # 1. poll proprio msg + with self.telemetry.timer("poll_state"): + msg = self._state_subscriber.get_msg() + if msg is not None: + self.latest_proprio_msg = msg + + # 2. poll image msg + with self.telemetry.timer("poll_image"): + msg = self._image_subscriber.read() + if msg is not None: + self.latest_image_msg = msg + + # 3. check keyboard input + with self.telemetry.timer("check_keyboard"): + self._check_keyboard_input() + + # 4. add frame + with self.telemetry.timer("add_frame"): + self._add_data_frame() + + end_time = time.monotonic() + + self.rate.sleep() + + # Log timing information if we missed our target frequency + if (end_time - t_start) > (1 / self.frequency): + self.telemetry.log_timing_info( + context="Data Exporter Loop Missed", threshold=0.001 + ) + + except KeyboardInterrupt: + print("Data exporter terminated by user") + # The user will trigger a keyboard interrupt if there's something wrong, + # so we flag the ongoing episode as discarded + buffer_size = self.data_exporter.episode_buffer.get("size", 0) + if buffer_size > 0: + self.data_exporter.save_episode_as_discarded() + + finally: + self.save_and_cleanup() + + +def main(config: DataExporterConfig): + + rclpy.init(args=None) + node = rclpy.create_node("data_exporter") + + waist_location = "lower_and_upper_body" if config.enable_waist else "lower_body" + g1_rm = g1.instantiate_g1_robot_model( + waist_location=waist_location, high_elbow_pose=config.high_elbow_pose + ) + + dataset_features = get_dataset_features(g1_rm, config.add_stereo_camera) + modality_config = get_modality_config(g1_rm, config.add_stereo_camera) + + text_to_speech = TextToSpeech() if config.text_to_speech else None + + # Only set DataCollectionInfo if we're creating a new dataset + # When adding to existing dataset, DataCollectionInfo will be ignored + if config.robot_id is not None: + data_collection_info = DataCollectionInfo( + teleoperator_username=config.teleoperator_username, + support_operator_username=config.support_operator_username, + robot_type="g1", + robot_id=config.robot_id, + lower_body_policy=config.lower_body_policy, + wbc_model_path=config.wbc_model_path, + ) + else: + # Use default DataCollectionInfo when adding to existing dataset + # This will be ignored if the dataset already exists + data_collection_info = DataCollectionInfo() + + robot_config_client = ROSServiceClient(ROBOT_CONFIG_TOPIC) + robot_config = robot_config_client.get_config() + + data_exporter = Gr00tDataExporter.create( + save_root=f"{config.root_output_dir}/{config.dataset_name}", + fps=config.data_collection_frequency, + features=dataset_features, + modality_config=modality_config, + task=config.task_prompt, + upload_bucket_path=BUCKET_BASE_PATH, + data_collection_info=data_collection_info, + script_config=robot_config, + ) + + data_collector = Gr00tDataCollector( + node=node, + frequency=config.data_collection_frequency, + data_exporter=data_exporter, + state_topic_name=STATE_TOPIC_NAME, + camera_host=config.camera_host, + camera_port=config.camera_port, + text_to_speech=text_to_speech, + ) + data_collector.run() + + +if __name__ == "__main__": + config = tyro.cli(DataExporterConfig) + config.task_prompt = input("Enter the task prompt: ").strip().lower() + add_to_existing_dataset = input("Add to existing dataset? (y/n): ").strip().lower() + + if add_to_existing_dataset == "y": + config.dataset_name = input("Enter the dataset name: ").strip().lower() + # When adding to existing dataset, we don't need robot_id or operator usernames + # as they should already be set in the existing dataset + elif add_to_existing_dataset == "n": + # robot_id = input("Enter the robot ID: ").strip().lower() + # if robot_id not in G1_ROBOT_IDS: + # raise ValueError(f"Invalid robot ID: {robot_id}. Available robot IDs: {G1_ROBOT_IDS}") + config.robot_id = "sim" + config.dataset_name = f"{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}-G1-{config.robot_id}" + + # Only ask for operator usernames when creating a new dataset + # print("Available teleoperator usernames:") + # for i, username in enumerate(OPERATOR_USERNAMES): + # print(f"{i}: {username}") + # teleop_idx = int(input("Select teleoperator username index: ")) + # config.teleoperator_username = OPERATOR_USERNAMES[teleop_idx] + config.teleoperator_username = "NEW_USER" + + # print("\nAvailable support operator usernames:") + # for i, username in enumerate(OPERATOR_USERNAMES): + # print(f"{i}: {username}") + # support_idx = int(input("Select support operator username index: ")) + # config.support_operator_username = OPERATOR_USERNAMES[support_idx] + config.support_operator_username = "NEW_USER" + + main(config) diff --git a/gr00t_wbc/control/main/teleop/run_navigation_policy_loop.py b/gr00t_wbc/control/main/teleop/run_navigation_policy_loop.py new file mode 100644 index 0000000..cf672a6 --- /dev/null +++ b/gr00t_wbc/control/main/teleop/run_navigation_policy_loop.py @@ -0,0 +1,68 @@ +import threading +import time + +import rclpy + +from gr00t_wbc.control.main.constants import NAV_CMD_TOPIC +from gr00t_wbc.control.policy.keyboard_navigation_policy import KeyboardNavigationPolicy +from gr00t_wbc.control.utils.keyboard_dispatcher import KeyboardListenerSubscriber +from gr00t_wbc.control.utils.ros_utils import ROSMsgPublisher + +FREQUENCY = 10 +NAV_NODE_NAME = "NavigationPolicy" + + +def main(): + rclpy.init(args=None) + node = rclpy.create_node(NAV_NODE_NAME) + + # Start ROS spin in a separate thread + thread = threading.Thread(target=rclpy.spin, args=(node,), daemon=True) + thread.start() + time.sleep(0.5) + + dict_publisher = ROSMsgPublisher(NAV_CMD_TOPIC) + keyboard_listener = KeyboardListenerSubscriber() + + # Initialize navigation policy + navigation_policy = KeyboardNavigationPolicy() + + # Create rate controller + rate = node.create_rate(FREQUENCY) + + try: + while rclpy.ok(): + t_now = time.monotonic() + # get keyboard input + + navigation_policy.handle_keyboard_button(keyboard_listener.read_msg()) + # Get action from navigation policy + action = navigation_policy.get_action(time=t_now) + + # Add timestamp to the data + action["timestamp"] = t_now + + # Create and publish ByteMultiArray message + dict_publisher.publish(action) + + # Print status periodically (optional) + if int(t_now * 10) % 10 == 0: + nav_cmd = action["navigate_cmd"] + node.get_logger().info( + f"Nav cmd: linear=({nav_cmd[0]:.2f}, {nav_cmd[1]:.2f}), " + f"angular={nav_cmd[2]:.2f}" + ) + + rate.sleep() + + except KeyboardInterrupt: + print("Navigation control loop terminated by user") + + finally: + # Clean shutdown + node.destroy_node() + rclpy.shutdown() + + +if __name__ == "__main__": + main() diff --git a/gr00t_wbc/control/main/teleop/run_sim_loop.py b/gr00t_wbc/control/main/teleop/run_sim_loop.py new file mode 100644 index 0000000..c907988 --- /dev/null +++ b/gr00t_wbc/control/main/teleop/run_sim_loop.py @@ -0,0 +1,61 @@ +from typing import Dict + +import tyro + +from gr00t_wbc.control.envs.g1.sim.simulator_factory import SimulatorFactory, init_channel +from gr00t_wbc.control.main.teleop.configs.configs import SimLoopConfig +from gr00t_wbc.control.robot_model.instantiation.g1 import ( + instantiate_g1_robot_model, +) +from gr00t_wbc.control.robot_model.robot_model import RobotModel + +ArgsConfig = SimLoopConfig + + +class SimWrapper: + def __init__(self, robot_model: RobotModel, env_name: str, config: Dict[str, any], **kwargs): + self.robot_model = robot_model + self.config = config + + init_channel(config=self.config) + + # Create simulator using factory + self.sim = SimulatorFactory.create_simulator( + config=self.config, + env_name=env_name, + **kwargs, + ) + + +def main(config: ArgsConfig): + wbc_config = config.load_wbc_yaml() + # NOTE: we will override the interface to local if it is not specified + wbc_config["ENV_NAME"] = config.env_name + + if config.enable_image_publish: + assert ( + config.enable_offscreen + ), "enable_offscreen must be True when enable_image_publish is True" + + robot_model = instantiate_g1_robot_model() + + sim_wrapper = SimWrapper( + robot_model=robot_model, + env_name=config.env_name, + config=wbc_config, + onscreen=wbc_config.get("ENABLE_ONSCREEN", True), + offscreen=wbc_config.get("ENABLE_OFFSCREEN", False), + ) + # Start simulator as independent process + SimulatorFactory.start_simulator( + sim_wrapper.sim, + as_thread=False, + enable_image_publish=config.enable_image_publish, + mp_start_method=config.mp_start_method, + camera_port=config.camera_port, + ) + + +if __name__ == "__main__": + config = tyro.cli(ArgsConfig) + main(config) diff --git a/gr00t_wbc/control/main/teleop/run_sync_sim_data_collection.py b/gr00t_wbc/control/main/teleop/run_sync_sim_data_collection.py new file mode 100644 index 0000000..fc4ca5d --- /dev/null +++ b/gr00t_wbc/control/main/teleop/run_sync_sim_data_collection.py @@ -0,0 +1,213 @@ +from pathlib import Path +import time + +import tyro + +from gr00t_wbc.control.main.teleop.configs.configs import SyncSimDataCollectionConfig +from gr00t_wbc.control.robot_model.instantiation import get_robot_type_and_model +from gr00t_wbc.control.utils.keyboard_dispatcher import ( + KeyboardDispatcher, + KeyboardListener, +) +from gr00t_wbc.control.utils.ros_utils import ROSManager +from gr00t_wbc.control.utils.sync_sim_utils import ( + COLLECTION_KEY, + SKIP_KEY, + CITestManager, + EpisodeManager, + generate_frame, + get_data_exporter, + get_env, + get_policies, +) +from gr00t_wbc.control.utils.telemetry import Telemetry + +CONTROL_NODE_NAME = "ControlPolicy" + + +CONTROL_CMD_TOPIC = CONTROL_NODE_NAME + "/q_target" +ENV_NODE_NAME = "SyncEnv" +ENV_OBS_TOPIC = ENV_NODE_NAME + "/obs" + + +def display_controls(config: SyncSimDataCollectionConfig): + """ + Method to pretty print controls. + """ + + def print_command(char, info): + char += " " * (30 - len(char)) + print("{}\t{}".format(char, info)) + + print("") + print_command("Keys", "Command") + if config.manual_control: + print_command(COLLECTION_KEY, "start/stop data collection") + print_command(SKIP_KEY, "skip and collect new episodes") + print_command("w-s-a-d", "move horizontally in x-y plane (press '=' first to enable)") + print_command("q", "rotate (counter-clockwise)") + print_command("e", "rotate (clockwise)") + print_command("space", "reset all velocity to zero") + print("") + + +def main(config: SyncSimDataCollectionConfig): + ros_manager = ROSManager(node_name=CONTROL_NODE_NAME) + node = ros_manager.node + + # Initialize telemetry + telemetry = Telemetry(window_size=100) + + # Initialize robot model + robot_type, robot_model = get_robot_type_and_model( + config.robot, + enable_waist_ik=config.enable_waist, + ) + + # Initialize sim env + env = get_env(config, onscreen=config.enable_onscreen, offscreen=config.save_img_obs) + seed = int(time.time()) + env.reset(seed) + env.render() + obs = env.observe() + robot_model.set_initial_body_pose(obs["q"]) + + # Initialize data exporter + exporter = get_data_exporter( + config, + obs, + robot_model, + save_path=Path("./outputs/ci_test/") if config.ci_test else None, + ) + + # Display control signals + display_controls(config) + + # Initialize policies + wbc_policy, teleop_policy = get_policies(config, robot_type, robot_model) + + dispatcher = KeyboardDispatcher() + keyboard_listener = KeyboardListener() # for data collection keys + dispatcher.register(keyboard_listener) + dispatcher.register(wbc_policy) + dispatcher.register(teleop_policy) + + dispatcher.start() + + rate = node.create_rate(config.control_frequency) + + # Initialize episode manager to handle state transitions and data collection + episode_manager = EpisodeManager(config) + + # Initialize CI test manager + ci_test_manager = CITestManager(config) if config.ci_test else None + + try: + while ros_manager.ok(): + + need_reset = False + keyboard_input = keyboard_listener.pop_key() + + with telemetry.timer("total_loop"): + max_mujoco_state_len, mujoco_state_len, mujoco_state = env.get_mujoco_state_info() + + # Measure observation time + with telemetry.timer("observe"): + obs = env.observe() + wbc_policy.set_observation(obs) + + # Measure policy setup time + with telemetry.timer("policy_setup"): + teleop_cmd = teleop_policy.get_action() + + wbc_goal = {} + + # Note that wbc_goal["navigation_cmd'] could be overwritten by teleop_cmd + if teleop_cmd: + for key, value in teleop_cmd.items(): + wbc_goal[key] = value + # Draw IK indicators + if config.ik_indicator: + env.set_ik_indicator(teleop_cmd) + if wbc_goal: + wbc_policy.set_goal(wbc_goal) + + # Measure policy action calculation time + with telemetry.timer("policy_action"): + wbc_action = wbc_policy.get_action() + + if config.ci_test: + ci_test_manager.check_upper_body_motion(robot_model, wbc_action, config) + + # Measure action queue time + with telemetry.timer("step"): + obs, _, _, _, step_info = env.step(wbc_action) + env.render() + episode_manager.increment_step() + + if config.ci_test and config.ci_test_mode == "pre_merge": + ci_test_manager.check_end_effector_tracking( + teleop_cmd, obs, config, episode_manager.get_step_count() + ) + + # Handle data collection trigger + episode_manager.handle_collection_trigger(wbc_goal, keyboard_input, step_info) + + # Collect data frame + if episode_manager.should_collect_data(): + frame = generate_frame( + obs, + wbc_action, + seed, + mujoco_state, + mujoco_state_len, + max_mujoco_state_len, + teleop_cmd, + wbc_goal, + config.save_img_obs, + ) + # exporting data + exporter.add_frame(frame) + + # if done and task_completion_hold_count is 0, save the episode + need_reset = episode_manager.check_export_and_completion(exporter) + + # check data abort + if episode_manager.handle_skip(wbc_goal, keyboard_input, exporter): + need_reset = True + + if need_reset: + if config.ci_test: + print("CI test: Completed...") + raise KeyboardInterrupt + + seed = int(time.time()) + env.reset(seed) + env.render() + + print("Sleeping for 3 seconds before resetting teleop policy...") + for j in range(3, 0, -1): + print(f"Starting in {j}...") + time.sleep(1) + + wbc_policy, teleop_policy = get_policies(config, robot_type, robot_model) + episode_manager.reset_step_count() + + rate.sleep() + + except ros_manager.exceptions() as e: + print(f"ROSManager interrupted by user: {e}") + finally: + # Cleanup resources + teleop_policy.close() + dispatcher.stop() + ros_manager.shutdown() + env.close() + print("Sync sim data collection loop terminated.") + + return True + + +if __name__ == "__main__": + config = tyro.cli(SyncSimDataCollectionConfig) + main(config) diff --git a/gr00t_wbc/control/main/teleop/run_teleop_policy_loop.py b/gr00t_wbc/control/main/teleop/run_teleop_policy_loop.py new file mode 100644 index 0000000..3c6650b --- /dev/null +++ b/gr00t_wbc/control/main/teleop/run_teleop_policy_loop.py @@ -0,0 +1,110 @@ +import time + +import rclpy +import tyro + +from gr00t_wbc.control.main.constants import CONTROL_GOAL_TOPIC +from gr00t_wbc.control.main.teleop.configs.configs import TeleopConfig +from gr00t_wbc.control.policy.lerobot_replay_policy import LerobotReplayPolicy +from gr00t_wbc.control.policy.teleop_policy import TeleopPolicy +from gr00t_wbc.control.robot_model.instantiation.g1 import instantiate_g1_robot_model +from gr00t_wbc.control.teleop.solver.hand.instantiation.g1_hand_ik_instantiation import ( + instantiate_g1_hand_ik_solver, +) +from gr00t_wbc.control.teleop.teleop_retargeting_ik import TeleopRetargetingIK +from gr00t_wbc.control.utils.ros_utils import ROSManager, ROSMsgPublisher +from gr00t_wbc.control.utils.telemetry import Telemetry + +TELEOP_NODE_NAME = "TeleopPolicy" + + +def main(config: TeleopConfig): + ros_manager = ROSManager(node_name=TELEOP_NODE_NAME) + node = ros_manager.node + + if config.robot == "g1": + waist_location = "lower_and_upper_body" if config.enable_waist else "lower_body" + robot_model = instantiate_g1_robot_model( + waist_location=waist_location, high_elbow_pose=config.high_elbow_pose + ) + left_hand_ik_solver, right_hand_ik_solver = instantiate_g1_hand_ik_solver() + else: + raise ValueError(f"Unsupported robot name: {config.robot}") + + if config.lerobot_replay_path: + teleop_policy = LerobotReplayPolicy( + robot_model=robot_model, parquet_path=config.lerobot_replay_path + ) + else: + print("running teleop policy, waiting teleop policy to be initialized...") + retargeting_ik = TeleopRetargetingIK( + robot_model=robot_model, + left_hand_ik_solver=left_hand_ik_solver, + right_hand_ik_solver=right_hand_ik_solver, + enable_visualization=config.enable_visualization, + body_active_joint_groups=["upper_body"], + ) + teleop_policy = TeleopPolicy( + robot_model=robot_model, + retargeting_ik=retargeting_ik, + body_control_device=config.body_control_device, + hand_control_device=config.hand_control_device, + body_streamer_ip=config.body_streamer_ip, # vive tracker, leap motion does not require + body_streamer_keyword=config.body_streamer_keyword, + enable_real_device=config.enable_real_device, + replay_data_path=config.teleop_replay_path, + ) + + # Create a publisher for the navigation commands + control_publisher = ROSMsgPublisher(CONTROL_GOAL_TOPIC) + + # Create rate controller + rate = node.create_rate(config.teleop_frequency) + iteration = 0 + time_to_get_to_initial_pose = 2 # seconds + + telemetry = Telemetry(window_size=100) + + try: + while rclpy.ok(): + with telemetry.timer("total_loop"): + t_start = time.monotonic() + # Get the current teleop action + with telemetry.timer("get_action"): + data = teleop_policy.get_action() + + # Add timing information to the message + t_now = time.monotonic() + data["timestamp"] = t_now + + # Set target completion time - longer for initial pose, then match control frequency + if iteration == 0: + data["target_time"] = t_now + time_to_get_to_initial_pose + else: + data["target_time"] = t_now + (1 / config.teleop_frequency) + + # Publish the teleop command + with telemetry.timer("publish_teleop_command"): + control_publisher.publish(data) + + # For the initial pose, wait the full duration before continuing + if iteration == 0: + print(f"Moving to initial pose for {time_to_get_to_initial_pose} seconds") + time.sleep(time_to_get_to_initial_pose) + iteration += 1 + end_time = time.monotonic() + if (end_time - t_start) > (1 / config.teleop_frequency): + telemetry.log_timing_info(context="Teleop Policy Loop Missed", threshold=0.001) + rate.sleep() + + except ros_manager.exceptions() as e: + print(f"ROSManager interrupted by user: {e}") + + finally: + print("Cleaning up...") + ros_manager.shutdown() + + +if __name__ == "__main__": + config = tyro.cli(TeleopConfig) + main(config) diff --git a/gr00t_wbc/control/policy/__init__.py b/gr00t_wbc/control/policy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gr00t_wbc/control/policy/g1_decoupled_whole_body_policy.py b/gr00t_wbc/control/policy/g1_decoupled_whole_body_policy.py new file mode 100644 index 0000000..ddfee45 --- /dev/null +++ b/gr00t_wbc/control/policy/g1_decoupled_whole_body_policy.py @@ -0,0 +1,157 @@ +import time as time_module +from typing import Optional + +import numpy as np +from pinocchio import rpy + +from gr00t_wbc.control.base.policy import Policy +from gr00t_wbc.control.main.constants import DEFAULT_NAV_CMD + + +class G1DecoupledWholeBodyPolicy(Policy): + """ + This class implements a whole-body policy for the G1 robot by combining an upper-body + policy and a lower-body RL-based policy. + It is designed to work with the G1 robot's specific configuration and control requirements. + """ + + def __init__( + self, + robot_model, + lower_body_policy: Policy, + upper_body_policy: Policy, + ): + self.robot_model = robot_model + self.lower_body_policy = lower_body_policy + self.upper_body_policy = upper_body_policy + self.last_goal_time = time_module.monotonic() + self.is_in_teleop_mode = False # Track if lower body is in teleop mode + + def set_observation(self, observation): + # Upper body policy is open loop (just interpolation), so we don't need to set the observation + self.lower_body_policy.set_observation(observation) + + def set_goal(self, goal): + """ + Set the goal for both upper and lower body policies. + + Args: + goal: Command from the planners + goal["target_upper_body_pose"]: Target pose for the upper body policy + goal["target_time"]: Target goal time + goal["interpolation_garbage_collection_time"]: Waypoints earlier than this time are removed + goal["navigate_cmd"]: Target navigation velocities for the lower body policy + goal["base_height_command"]: Target base height for both upper and lower body policies + """ + # Update goal timestamp for timeout safety + self.last_goal_time = time_module.monotonic() + + upper_body_goal = {} + lower_body_goal = {} + + # Upper body goal keys + upper_body_keys = [ + "target_upper_body_pose", + "base_height_command", + "target_time", + "interpolation_garbage_collection_time", + "navigate_cmd", + ] + for key in upper_body_keys: + if key in goal: + upper_body_goal[key] = goal[key] + + # Always ensure navigate_cmd is present to prevent interpolation from old dangerous values + if "navigate_cmd" not in goal: + # Safety: Inject safe default navigate_cmd to ensure interpolation goes to stop + if "target_time" in goal and isinstance(goal["target_time"], list): + upper_body_goal["navigate_cmd"] = [np.array(DEFAULT_NAV_CMD)] * len( + goal["target_time"] + ) + else: + upper_body_goal["navigate_cmd"] = np.array(DEFAULT_NAV_CMD) + + # Set teleop policy command flag + has_teleop_commands = ("navigate_cmd" in goal) or ("base_height_command" in goal) + self.is_in_teleop_mode = has_teleop_commands # Track teleop state for timeout safety + self.lower_body_policy.set_use_teleop_policy_cmd(has_teleop_commands) + + # Lower body goal keys + lower_body_keys = [ + "toggle_stand_command", + "toggle_policy_action", + ] + for key in lower_body_keys: + if key in goal: + lower_body_goal[key] = goal[key] + + self.upper_body_policy.set_goal(upper_body_goal) + self.lower_body_policy.set_goal(lower_body_goal) + + def get_action(self, time: Optional[float] = None): + current_time = time if time is not None else time_module.monotonic() + + # Safety timeout: Only apply when in teleop mode (communication loss dangerous) + # When in keyboard mode, no timeout needed (user controls directly) + if self.is_in_teleop_mode: + time_since_goal = current_time - self.last_goal_time + if time_since_goal > 1.0: # 1 second timeout + print( + f"SAFETY: Teleop mode timeout after {time_since_goal:.1f}s, injecting safe goal" + ) + # Inject safe goal to trigger all safety mechanisms (gear_wbc reset + interpolation reset) + safe_goal = { + "target_time": current_time + 0.1, + "interpolation_garbage_collection_time": current_time - 1.0, + } + self.set_goal( + safe_goal + ) # This will reset is_in_teleop_mode to False and trigger all safety + + # Get indices for groups + lower_body_indices = self.robot_model.get_joint_group_indices("lower_body") + upper_body_indices = self.robot_model.get_joint_group_indices("upper_body") + + # Initialize full configuration with zeros + q = np.zeros(self.robot_model.num_dofs) + + upper_body_action = self.upper_body_policy.get_action(time) + q[upper_body_indices] = upper_body_action["target_upper_body_pose"] + q_arms = q[self.robot_model.get_joint_group_indices("arms")] + base_height_command = upper_body_action.get("base_height_command", None) + interpolated_navigate_cmd = upper_body_action.get("navigate_cmd", None) + + # Compute torso orientation relative to waist, to pass to lower body policy + self.robot_model.cache_forward_kinematics(q, auto_clip=False) + torso_orientation = self.robot_model.frame_placement("torso_link").rotation + waist_orientation = self.robot_model.frame_placement("pelvis").rotation + # Extract yaw from rotation matrix and create a rotation with only yaw + # The rotation property is a 3x3 numpy array + waist_yaw = np.arctan2(waist_orientation[1, 0], waist_orientation[0, 0]) + # Create a rotation matrix with only yaw using Pinocchio's rpy functions + waist_yaw_only_rotation = rpy.rpyToMatrix(0, 0, waist_yaw) + yaw_only_waist_from_torso = waist_yaw_only_rotation.T @ torso_orientation + torso_orientation_rpy = rpy.matrixToRpy(yaw_only_waist_from_torso) + + lower_body_action = self.lower_body_policy.get_action( + time, q_arms, base_height_command, torso_orientation_rpy, interpolated_navigate_cmd + ) + + # If pelvis is both in upper and lower body, lower body policy takes preference + q[lower_body_indices] = lower_body_action["body_action"][0][ + : len(lower_body_indices) + ] # lower body (legs + waist) + + self.last_action = {"q": q} + + return {"q": q} + + def handle_keyboard_button(self, key): + try: + self.lower_body_policy.locomotion_policy.handle_keyboard_button(key) + except AttributeError: + # Only catch AttributeError, let other exceptions propagate + self.lower_body_policy.handle_keyboard_button(key) + + def activate_policy(self): + self.handle_keyboard_button("]") diff --git a/gr00t_wbc/control/policy/g1_gear_wbc_policy.py b/gr00t_wbc/control/policy/g1_gear_wbc_policy.py new file mode 100644 index 0000000..6b4cd74 --- /dev/null +++ b/gr00t_wbc/control/policy/g1_gear_wbc_policy.py @@ -0,0 +1,295 @@ +import collections +from pathlib import Path +from typing import Any, Dict, Optional + +import numpy as np +import onnxruntime as ort +import torch + +from gr00t_wbc.control.base.policy import Policy +from gr00t_wbc.control.utils.gear_wbc_utils import get_gravity_orientation, load_config + + +class G1GearWbcPolicy(Policy): + """Simple G1 robot policy using OpenGearWbc trained neural network.""" + + def __init__(self, robot_model, config: str, model_path: str): + """Initialize G1GearWbcPolicy. + + Args: + config_path: Path to gear_wbc YAML configuration file + """ + self.config, self.LEGGED_GYM_ROOT_DIR = load_config(config) + self.robot_model = robot_model + self.use_teleop_policy_cmd = False + + package_root = Path(__file__).resolve().parents[2] + self.sim2mujoco_root_dir = str(package_root / "sim2mujoco") + model_path_1, model_path_2 = model_path.split(",") + + self.policy_1 = self.load_onnx_policy( + self.sim2mujoco_root_dir + "/resources/robots/g1/" + model_path_1 + ) + self.policy_2 = self.load_onnx_policy( + self.sim2mujoco_root_dir + "/resources/robots/g1/" + model_path_2 + ) + + # Initialize observation history buffer + self.observation = None + self.obs_history = collections.deque(maxlen=self.config["obs_history_len"]) + self.obs_buffer = np.zeros(self.config["num_obs"], dtype=np.float32) + self.counter = 0 + + # Initialize state variables + self.use_policy_action = False + self.action = np.zeros(self.config["num_actions"], dtype=np.float32) + self.target_dof_pos = self.config["default_angles"].copy() + self.cmd = self.config["cmd_init"].copy() + self.height_cmd = self.config["height_cmd"] + self.freq_cmd = self.config["freq_cmd"] + self.roll_cmd = self.config["rpy_cmd"][0] + self.pitch_cmd = self.config["rpy_cmd"][1] + self.yaw_cmd = self.config["rpy_cmd"][2] + self.gait_indices = torch.zeros((1), dtype=torch.float32) + + def load_onnx_policy(self, model_path: str): + print(f"Loading ONNX policy from {model_path}") + model = ort.InferenceSession(model_path) + + def run_inference(input_tensor): + ort_inputs = {model.get_inputs()[0].name: input_tensor.cpu().numpy()} + ort_outs = model.run(None, ort_inputs) + return torch.tensor(ort_outs[0], device="cpu") + + print(f"Successfully loaded ONNX policy from {model_path}") + + return run_inference + + def compute_observation(self, observation: Dict[str, Any]) -> tuple[np.ndarray, int]: + """Compute the observation vector from current state""" + # Get body joint indices (excluding waist roll and pitch) + self.gait_indices = torch.remainder(self.gait_indices + 0.02 * self.freq_cmd, 1.0) + durations = torch.full_like(self.gait_indices, 0.5) + phases = 0.5 + foot_indices = [ + self.gait_indices + phases, # FL + self.gait_indices, # FR + ] + self.foot_indices = torch.remainder( + torch.cat([foot_indices[i].unsqueeze(1) for i in range(2)], dim=1), 1.0 + ) + for fi in foot_indices: + stance = fi < durations + swing = fi >= durations + fi[stance] = fi[stance] * (0.5 / durations[stance]) + fi[swing] = 0.5 + (fi[swing] - durations[swing]) * (0.5 / (1 - durations[swing])) + + self.clock_inputs = torch.stack([torch.sin(2 * np.pi * fi) for fi in foot_indices], dim=1) + + body_indices = self.robot_model.get_joint_group_indices("body") + body_indices = [idx for idx in body_indices] + + n_joints = len(body_indices) + + # Extract joint data + qj = observation["q"][body_indices].copy() + dqj = observation["dq"][body_indices].copy() + + # Extract floating base data + quat = observation["floating_base_pose"][3:7].copy() # quaternion + omega = observation["floating_base_vel"][3:6].copy() # angular velocity + + # Handle default angles padding + if len(self.config["default_angles"]) < n_joints: + padded_defaults = np.zeros(n_joints, dtype=np.float32) + padded_defaults[: len(self.config["default_angles"])] = self.config["default_angles"] + else: + padded_defaults = self.config["default_angles"][:n_joints] + + # Scale the values + qj_scaled = (qj - padded_defaults) * self.config["dof_pos_scale"] + dqj_scaled = dqj * self.config["dof_vel_scale"] + gravity_orientation = get_gravity_orientation(quat) + omega_scaled = omega * self.config["ang_vel_scale"] + + # Calculate single observation dimension + single_obs_dim = 86 # 3 + 1 + 3 + 3 + 3 + n_joints + n_joints + 15, n_joints = 29 + + # Create single observation + single_obs = np.zeros(single_obs_dim, dtype=np.float32) + single_obs[0:3] = self.cmd[:3] * self.config["cmd_scale"] + single_obs[3:4] = np.array([self.height_cmd]) + single_obs[4:7] = np.array([self.roll_cmd, self.pitch_cmd, self.yaw_cmd]) + single_obs[7:10] = omega_scaled + single_obs[10:13] = gravity_orientation + # single_obs[14:17] = omega_scaled_torso + # single_obs[17:20] = gravity_torso + single_obs[13 : 13 + n_joints] = qj_scaled + single_obs[13 + n_joints : 13 + 2 * n_joints] = dqj_scaled + single_obs[13 + 2 * n_joints : 13 + 2 * n_joints + 15] = self.action + # single_obs[13 + 2 * n_joints + 15 : 13 + 2 * n_joints + 15 + 2] = ( + # processed_clock_inputs.detach().cpu().numpy() + # ) + return single_obs, single_obs_dim + + def set_observation(self, observation: Dict[str, Any]): + """Update the policy's current observation of the environment. + + Args: + observation: Dictionary containing single observation from current state + Should include 'obs' key with current single observation + """ + + # Extract the single observation + self.observation = observation + single_obs, single_obs_dim = self.compute_observation(observation) + + # Update observation history every control_decimation steps + # if self.counter % self.config['control_decimation'] == 0: + # Add current observation to history + self.obs_history.append(single_obs) + + # Fill history with zeros if not enough observations yet + while len(self.obs_history) < self.config["obs_history_len"]: + self.obs_history.appendleft(np.zeros_like(single_obs)) + + # Construct full observation with history + single_obs_dim = len(single_obs) + for i, hist_obs in enumerate(self.obs_history): + start_idx = i * single_obs_dim + end_idx = start_idx + single_obs_dim + self.obs_buffer[start_idx:end_idx] = hist_obs + + # Convert to tensor for policy + self.obs_tensor = torch.from_numpy(self.obs_buffer).unsqueeze(0) + # self.counter += 1 + + assert self.obs_tensor.shape[1] == self.config["num_obs"] + + def set_use_teleop_policy_cmd(self, use_teleop_policy_cmd: bool): + self.use_teleop_policy_cmd = use_teleop_policy_cmd + # Safety: When teleop is disabled, reset navigation to stop + if not use_teleop_policy_cmd: + self.nav_cmd = self.config["cmd_init"].copy() # Reset to safe default + + def set_goal(self, goal: Dict[str, Any]): + """Set the goal for the policy. + + Args: + goal: Dictionary containing the goal for the policy + """ + + if "toggle_policy_action" in goal: + if goal["toggle_policy_action"]: + self.use_policy_action = not self.use_policy_action + + def get_action( + self, + time: Optional[float] = None, + arms_target_pose: Optional[np.ndarray] = None, + base_height_command: Optional[np.ndarray] = None, + torso_orientation_rpy: Optional[np.ndarray] = None, + interpolated_navigate_cmd: Optional[np.ndarray] = None, + ) -> Dict[str, Any]: + """Compute and return the next action based on current observation. + + Args: + time: Optional "monotonic time" for time-dependent policies (unused) + + Returns: + Dictionary containing the action to be executed + """ + if self.obs_tensor is None: + raise ValueError("No observation set. Call set_observation() first.") + + if base_height_command is not None and self.use_teleop_policy_cmd: + self.height_cmd = ( + base_height_command[0] + if isinstance(base_height_command, list) + else base_height_command + ) + + if interpolated_navigate_cmd is not None and self.use_teleop_policy_cmd: + self.cmd = interpolated_navigate_cmd + + if torso_orientation_rpy is not None and self.use_teleop_policy_cmd: + self.roll_cmd = torso_orientation_rpy[0] + self.pitch_cmd = torso_orientation_rpy[1] + self.yaw_cmd = torso_orientation_rpy[2] + + # Run policy inference + with torch.no_grad(): + # Select appropriate policy based on command magnitude + if np.linalg.norm(self.cmd) < 0.05: + # Use standing policy for small commands + policy = self.policy_1 + else: + # Use walking policy for movement commands + policy = self.policy_2 + + self.action = policy(self.obs_tensor).detach().numpy().squeeze() + + # Transform action to target_dof_pos + if self.use_policy_action: + cmd_q = self.action * self.config["action_scale"] + self.config["default_angles"] + else: + cmd_q = self.observation["q"][self.robot_model.get_joint_group_indices("lower_body")] + + cmd_dq = np.zeros(self.config["num_actions"]) + cmd_tau = np.zeros(self.config["num_actions"]) + + return {"body_action": (cmd_q, cmd_dq, cmd_tau)} + + def handle_keyboard_button(self, key): + if key == "]": + self.use_policy_action = True + elif key == "o": + self.use_policy_action = False + elif key == "w": + self.cmd[0] += 0.2 + elif key == "s": + self.cmd[0] -= 0.2 + elif key == "a": + self.cmd[1] += 0.2 + elif key == "d": + self.cmd[1] -= 0.2 + elif key == "q": + self.cmd[2] += 0.2 + elif key == "e": + self.cmd[2] -= 0.2 + elif key == "z": + self.cmd[0] = 0.0 + self.cmd[1] = 0.0 + self.cmd[2] = 0.0 + elif key == "1": + self.height_cmd += 0.1 + elif key == "2": + self.height_cmd -= 0.1 + elif key == "n": + self.freq_cmd -= 0.1 + self.freq_cmd = max(1.0, self.freq_cmd) + elif key == "m": + self.freq_cmd += 0.1 + self.freq_cmd = min(2.0, self.freq_cmd) + elif key == "3": + self.roll_cmd -= np.deg2rad(10) + elif key == "4": + self.roll_cmd += np.deg2rad(10) + elif key == "5": + self.pitch_cmd -= np.deg2rad(10) + elif key == "6": + self.pitch_cmd += np.deg2rad(10) + elif key == "7": + self.yaw_cmd -= np.deg2rad(10) + elif key == "8": + self.yaw_cmd += np.deg2rad(10) + + if key: + print("--------------------------------") + print(f"Linear velocity command: {self.cmd}") + print(f"Base height command: {self.height_cmd}") + print(f"Use policy action: {self.use_policy_action}") + print(f"roll deg angle: {np.rad2deg(self.roll_cmd)}") + print(f"pitch deg angle: {np.rad2deg(self.pitch_cmd)}") + print(f"yaw deg angle: {np.rad2deg(self.yaw_cmd)}") + print(f"Gait frequency: {self.freq_cmd}") diff --git a/gr00t_wbc/control/policy/identity_policy.py b/gr00t_wbc/control/policy/identity_policy.py new file mode 100644 index 0000000..d4e5087 --- /dev/null +++ b/gr00t_wbc/control/policy/identity_policy.py @@ -0,0 +1,25 @@ +from copy import deepcopy +from typing import Optional + +import gymnasium as gym + +from gr00t_wbc.control.base.policy import Policy + + +class IdentityPolicy(Policy): + def __init__(self): + self.reset() + + def get_action(self, time: Optional[float] = None) -> dict[str, any]: + return self.goal + + def set_goal(self, goal: dict[str, any]) -> None: + self.goal = deepcopy(goal) + self.goal.pop("interpolation_garbage_collection_time", None) + self.goal.pop("target_time", None) + + def observation_space(self) -> gym.spaces.Dict: + return gym.spaces.Dict() + + def action_space(self) -> gym.spaces.Dict: + return gym.spaces.Dict() diff --git a/gr00t_wbc/control/policy/interpolation_policy.py b/gr00t_wbc/control/policy/interpolation_policy.py new file mode 100644 index 0000000..f190c4a --- /dev/null +++ b/gr00t_wbc/control/policy/interpolation_policy.py @@ -0,0 +1,297 @@ +import numbers +import time as time_module +from typing import Any, Dict, Optional, Union + +import gymnasium as gym +import numpy as np +import scipy.interpolate as si + +from gr00t_wbc.control.base.policy import Policy + + +class InterpolationPolicy(Policy): + def __init__( + self, + init_time: float, + init_values: dict[str, np.ndarray], + max_change_rate: float, + ): + """ + Args: + init_time: The time of recording the initial values. + init_values: The initial values of the features. + The keys are the names of the features, and the values + are the initial values of the features (1D array). + max_change_rate: The maximum change rate. + """ + super().__init__() + self.last_action = init_values # Vecs are 1D arrays + self.concat_order = sorted(init_values.keys()) + self.concat_dims = [] + for key in self.concat_order: + vec = np.array(init_values[key]) + if vec.ndim == 2 and vec.shape[0] == 1: + vec = vec[0] + init_values[key] = vec + assert vec.ndim == 1, f"The shape of {key} should be (D,). Got {vec.shape}." + self.concat_dims.append(vec.shape[0]) + + self.init_values_concat = self._concat_vecs(init_values, 1) + self.max_change_rate = max_change_rate + self.reset(init_time) + + def reset(self, init_time: float = time_module.monotonic()): + self.interp = PoseTrajectoryInterpolator(np.array([init_time]), self.init_values_concat) + self.last_waypoint_time = init_time + self.max_change_rate = self.max_change_rate + + def _concat_vecs(self, values: dict[str, np.ndarray], length: int) -> np.ndarray: + """ + Concatenate the vectors into a 2D array to be used for interpolation. + Args: + values: The values to concatenate. + length: The length of the concatenated vectors (time dimension). + Returns: + The concatenated vectors (T, D) arrays. + """ + concat_vecs = [] + for key in self.concat_order: + if key in values: + vec = np.array(values[key]) + if vec.ndim == 1: + # If the vector is 1D, tile it to the length of the time dimension + vec = np.tile(vec, (length, 1)) + assert vec.ndim == 2, f"The shape of {key} should be (T, D). Got {vec.shape}." + concat_vecs.append(vec) + else: + # If the vector is not in the values, use the last action + # Since the last action is 1D, we need to tile it to the length of the time dimension + concat_vecs.append(np.tile(self.last_action[key], (length, 1))) + return np.concatenate(concat_vecs, axis=1) # Vecs are 2D (T, D) arrays + + def _unconcat_vecs(self, concat_vec: np.ndarray) -> dict[str, np.ndarray]: + curr_idx = 0 + action = {} + assert ( + concat_vec.ndim == 1 + ), f"The shape of the concatenated vectors should be (T, D). Got {concat_vec.shape}." + for key, dim in zip(self.concat_order, self.concat_dims): + action[key] = concat_vec[curr_idx : curr_idx + dim] + curr_idx += dim + return action # Vecs are 1D arrays + + def __call__( + self, observation: Dict[str, Any], goal: Dict[str, Any], time: float + ) -> Dict[str, np.ndarray]: + raise NotImplementedError( + "`InterpolationPolicy` accepts goal and provide action in two separate methods." + ) + + def set_goal(self, goal: Dict[str, Any]) -> None: + if "target_time" not in goal: + return + assert ( + "interpolation_garbage_collection_time" in goal + ), "`interpolation_garbage_collection_time` is required." + target_time = goal.pop("target_time") + interpolation_garbage_collection_time = goal.pop("interpolation_garbage_collection_time") + + if isinstance(target_time, list): + for key, vec in goal.items(): + assert isinstance(vec, list) + assert len(vec) == len(target_time), ( + f"The length of {key} and `target_time` should be the same. " + f"Got {len(vec)} and {len(target_time)}." + ) + else: + target_time = [target_time] + for key in goal: + goal[key] = [goal[key]] + + # Concatenate all vectors in goal + concat_vecs = self._concat_vecs(goal, len(target_time)) + assert concat_vecs.shape[0] == len(target_time), ( + f"The length of the concatenated goal and `target_time` should be the same. " + f"Got {concat_vecs.shape[0]} and {len(target_time)}." + ) + + for tt, vec in zip(target_time, concat_vecs): + if tt < interpolation_garbage_collection_time: + continue + self.interp = self.interp.schedule_waypoint( + pose=vec, + time=tt, + max_change_rate=self.max_change_rate, + interpolation_garbage_collection_time=interpolation_garbage_collection_time, + last_waypoint_time=self.last_waypoint_time, + ) + self.last_waypoint_time = tt + + def get_action(self, time: Optional[float] = None) -> dict[str, Any]: + """Get the next action based on the (current) monotonic time.""" + if time is None: + time = time_module.monotonic() + concat_vec = self.interp(time) + self.last_action.update(self._unconcat_vecs(concat_vec)) + return self.last_action + + def observation_space(self) -> gym.spaces.Dict: + """Return the observation space.""" + pass + + def action_space(self) -> gym.spaces.Dict: + """Return the action space.""" + pass + + def close(self) -> None: + """Clean up resources.""" + pass + + +class PoseTrajectoryInterpolator: + def __init__(self, times: np.ndarray, poses: np.ndarray): + assert len(times) >= 1 + assert len(poses) == len(times) + + times = np.asarray(times) + poses = np.asarray(poses) + + self.num_joint = len(poses[0]) + + if len(times) == 1: + # special treatment for single step interpolation + self.single_step = True + self._times = times + self._poses = poses + else: + self.single_step = False + assert np.all(times[1:] >= times[:-1]) + self.pose_interp = si.interp1d(times, poses, axis=0, assume_sorted=True) + + @property + def times(self) -> np.ndarray: + if self.single_step: + return self._times + else: + return self.pose_interp.x + + @property + def poses(self) -> np.ndarray: + if self.single_step: + return self._poses + else: + return self.pose_interp.y + + def trim(self, start_t: float, end_t: float) -> "PoseTrajectoryInterpolator": + assert start_t <= end_t + times = self.times + should_keep = (start_t < times) & (times < end_t) + keep_times = times[should_keep] + all_times = np.concatenate([[start_t], keep_times, [end_t]]) + # remove duplicates, Slerp requires strictly increasing x + all_times = np.unique(all_times) + # interpolate + all_poses = self(all_times) + return PoseTrajectoryInterpolator(times=all_times, poses=all_poses) + + def schedule_waypoint( + self, + pose, + time, + max_change_rate=np.inf, + interpolation_garbage_collection_time=None, + last_waypoint_time=None, + ) -> "PoseTrajectoryInterpolator": + if not isinstance(max_change_rate, np.ndarray): + max_change_rate = np.array([max_change_rate] * self.num_joint) + + assert len(max_change_rate) == self.num_joint + assert np.max(max_change_rate) > 0 + + if last_waypoint_time is not None: + assert interpolation_garbage_collection_time is not None + + # trim current interpolator to between interpolation_garbage_collection_time and last_waypoint_time + start_time = self.times[0] + end_time = self.times[-1] + assert start_time <= end_time + if interpolation_garbage_collection_time is not None: + if time <= interpolation_garbage_collection_time: + # if insert time is earlier than current time + # no effect should be done to the interpolator + return self + # now, interpolation_garbage_collection_time < time + start_time = max(interpolation_garbage_collection_time, start_time) + + if last_waypoint_time is not None: + # if last_waypoint_time is earlier than start_time + # use start_time + if time <= last_waypoint_time: + end_time = interpolation_garbage_collection_time + else: + end_time = max(last_waypoint_time, interpolation_garbage_collection_time) + else: + end_time = interpolation_garbage_collection_time + + end_time = min(end_time, time) + start_time = min(start_time, end_time) + # end time should be the latest of all times except time + # after this we can assume order (proven by zhenjia, due to the 2 min operations) + + # Constraints: + # start_time <= end_time <= time (proven by zhenjia) + # interpolation_garbage_collection_time <= start_time (proven by zhenjia) + # interpolation_garbage_collection_time <= time (proven by zhenjia) + + # time can't change + # last_waypoint_time can't change + # interpolation_garbage_collection_time can't change + assert start_time <= end_time + assert end_time <= time + if last_waypoint_time is not None: + if time <= last_waypoint_time: + assert end_time == interpolation_garbage_collection_time + else: + assert end_time == max(last_waypoint_time, interpolation_garbage_collection_time) + + if interpolation_garbage_collection_time is not None: + assert interpolation_garbage_collection_time <= start_time + assert interpolation_garbage_collection_time <= time + trimmed_interp = self.trim(start_time, end_time) + # after this, all waypoints in trimmed_interp is within start_time and end_time + # and is earlier than time + + # determine speed + duration = time - end_time + end_pose = trimmed_interp(end_time) + pose_min_duration = np.max(np.abs(end_pose - pose) / max_change_rate) + duration = max(duration, pose_min_duration) + assert duration >= 0 + last_waypoint_time = end_time + duration + + # insert new pose + times = np.append(trimmed_interp.times, [last_waypoint_time], axis=0) + poses = np.append(trimmed_interp.poses, [pose], axis=0) + + # create new interpolator + final_interp = PoseTrajectoryInterpolator(times, poses) + return final_interp + + def __call__(self, t: Union[numbers.Number, np.ndarray]) -> np.ndarray: + is_single = False + if isinstance(t, numbers.Number): + is_single = True + t = np.array([t]) + + pose = np.zeros((len(t), self.num_joint)) + if self.single_step: + pose[:] = self._poses[0] + else: + start_time = self.times[0] + end_time = self.times[-1] + t = np.clip(t, start_time, end_time) + pose = self.pose_interp(t) + + if is_single: + pose = pose[0] + return pose diff --git a/gr00t_wbc/control/policy/keyboard_navigation_policy.py b/gr00t_wbc/control/policy/keyboard_navigation_policy.py new file mode 100644 index 0000000..347a7a0 --- /dev/null +++ b/gr00t_wbc/control/policy/keyboard_navigation_policy.py @@ -0,0 +1,87 @@ +from typing import Any, Dict, Optional + +import numpy as np + +from gr00t_wbc.control.base.policy import Policy + + +class KeyboardNavigationPolicy(Policy): + def __init__( + self, + max_linear_velocity: float = 0.5, + max_angular_velocity: float = 0.5, + verbose: bool = True, + **kwargs, + ): + """ + Initialize the navigation policy. + + Args: + max_linear_velocity: Maximum linear velocity in m/s (for x and y components) + max_angular_velocity: Maximum angular velocity in rad/s (for yaw component) + **kwargs: Additional arguments passed to the base Policy class + """ + super().__init__(**kwargs) + self.max_linear_velocity = max_linear_velocity + self.max_angular_velocity = max_angular_velocity + self.verbose = verbose + + # Initialize velocity commands + self.lin_vel_command = np.zeros(2, dtype=np.float32) # [vx, vy] + self.ang_vel_command = np.zeros(1, dtype=np.float32) # [wz] + + def get_action(self, time: Optional[float] = None) -> Dict[str, Any]: + """ + Get the action to execute based on current state. + + Args: + time: Current time (optional) + + Returns: + Dict containing the action to execute with: + - navigate_cmd: np.array([vx, vy, wz]) where: + - vx: linear velocity in x direction (m/s) + - vy: linear velocity in y direction (m/s) + - wz: angular velocity around z axis (rad/s) + """ + # Combine linear and angular velocities into a single command + # Ensure velocities are within limits + vx = np.clip(self.lin_vel_command[0], -self.max_linear_velocity, self.max_linear_velocity) + vy = np.clip(self.lin_vel_command[1], -self.max_linear_velocity, self.max_linear_velocity) + wz = np.clip(self.ang_vel_command[0], -self.max_angular_velocity, self.max_angular_velocity) + + navigate_cmd = np.array([vx, vy, wz], dtype=np.float32) + + action = {"navigate_cmd": navigate_cmd} + return action + + def handle_keyboard_button(self, keycode: str): + """ + Handle keyboard inputs for navigation control. + + Args: + keycode: The key that was pressed + """ + if keycode == "w": + self.lin_vel_command[0] += 0.1 # Increase forward velocity + elif keycode == "s": + self.lin_vel_command[0] -= 0.1 # Increase backward velocity + elif keycode == "a": + self.lin_vel_command[1] += 0.1 # Increase left velocity + elif keycode == "d": + self.lin_vel_command[1] -= 0.1 # Increase right velocity + elif keycode == "q": + self.ang_vel_command[0] += 0.1 # Increase counter-clockwise rotation + elif keycode == "e": + self.ang_vel_command[0] -= 0.1 # Increase clockwise rotation + elif keycode == "z": + # Reset all velocities + self.lin_vel_command[:] = 0.0 + self.ang_vel_command[:] = 0.0 + if self.verbose: + print("Navigation policy: Reset all velocity commands to zero") + + # Print current velocities after any keyboard input + if self.verbose: + print(f"Nav lin vel: ({self.lin_vel_command[0]:.2f}, {self.lin_vel_command[1]:.2f})") + print(f"Nav ang vel: {self.ang_vel_command[0]:.2f}") diff --git a/gr00t_wbc/control/policy/lerobot_replay_policy.py b/gr00t_wbc/control/policy/lerobot_replay_policy.py new file mode 100644 index 0000000..2783ca8 --- /dev/null +++ b/gr00t_wbc/control/policy/lerobot_replay_policy.py @@ -0,0 +1,107 @@ +import time + +import pandas as pd + +from gr00t_wbc.control.base.policy import Policy +from gr00t_wbc.control.main.constants import DEFAULT_BASE_HEIGHT, DEFAULT_NAV_CMD, DEFAULT_WRIST_POSE +from gr00t_wbc.control.robot_model.robot_model import RobotModel +from gr00t_wbc.data.viz.rerun_viz import RerunViz + + +class LerobotReplayPolicy(Policy): + """Replay policy for Lerobot dataset, so we can replay the dataset + and just use the action from the dataset. + + Args: + parquet_path: Path to the parquet file containing the dataset. + """ + + is_active = True # by default, the replay policy is active + + def __init__(self, robot_model: RobotModel, parquet_path: str, use_viz: bool = False): + # self.dataset = LerobotDataset(dataset_path) + self.parquet_path = parquet_path + self._ctr = 0 + # read the parquet file + self.df = pd.read_parquet(self.parquet_path) + self._max_ctr = len(self.df) + # get the action from the dataframe + self.action = self.df.iloc[self._ctr]["action"] + self.use_viz = use_viz + if self.use_viz: + self.viz = RerunViz( + image_keys=["egoview_image"], + tensor_keys=[ + "left_arm_qpos", + "left_hand_qpos", + "right_arm_qpos", + "right_hand_qpos", + ], + window_size=5.0, + ) + self.robot_model = robot_model + self.upper_body_joint_indices = self.robot_model.get_joint_group_indices("upper_body") + + def get_action(self) -> dict[str, any]: + # get the action from the dataframe + action = self.df.iloc[self._ctr]["action"] + wrist_pose = self.df.iloc[self._ctr]["action.eef"] + navigate_cmd = self.df.iloc[self._ctr].get("teleop.navigate_command", DEFAULT_NAV_CMD) + base_height_cmd = self.df.iloc[self._ctr].get( + "teleop.base_height_command", DEFAULT_BASE_HEIGHT + ) + + self._ctr += 1 + if self._ctr >= self._max_ctr: + self._ctr = 0 + # print(f"Replay {self._ctr} / {self._max_ctr}") + if self.use_viz: + self.viz.plot_tensors( + { + "left_arm_qpos": action[self.robot_model.get_joint_group_indices("left_arm")] + + 15, + "left_hand_qpos": action[self.robot_model.get_joint_group_indices("left_hand")] + + 15, + "right_arm_qpos": action[self.robot_model.get_joint_group_indices("right_arm")] + + 15, + "right_hand_qpos": action[ + self.robot_model.get_joint_group_indices("right_hand") + ] + + 15, + }, + time.monotonic(), + ) + + return { + "target_upper_body_pose": action[self.upper_body_joint_indices], + "wrist_pose": wrist_pose, + "navigate_cmd": navigate_cmd, + "base_height_cmd": base_height_cmd, + "timestamp": time.time(), + } + + def action_to_cmd(self, action: dict[str, any]) -> dict[str, any]: + action["target_upper_body_pose"] = action["q"][ + self.robot_model.get_joint_group_indices("upper_body") + ] + del action["q"] + return action + + def set_observation(self, observation: dict[str, any]): + pass + + def get_observation(self) -> dict[str, any]: + return { + "wrist_pose": self.df.iloc[self._ctr - 1].get( + "observation.eef_state", DEFAULT_WRIST_POSE + ), + "timestamp": time.time(), + } + + +if __name__ == "__main__": + policy = LerobotReplayPolicy( + parquet_path="outputs/g1-open-hands-may7/data/chunk-000/episode_000000.parquet" + ) + action = policy.get_action() + print(action) diff --git a/gr00t_wbc/control/policy/teleop_policy.py b/gr00t_wbc/control/policy/teleop_policy.py new file mode 100644 index 0000000..26d5bf6 --- /dev/null +++ b/gr00t_wbc/control/policy/teleop_policy.py @@ -0,0 +1,207 @@ +from contextlib import contextmanager +import time +from typing import Optional + +import numpy as np +from scipy.spatial.transform import Rotation as R + +from gr00t_wbc.control.base.policy import Policy +from gr00t_wbc.control.robot_model import RobotModel +from gr00t_wbc.control.teleop.teleop_retargeting_ik import TeleopRetargetingIK +from gr00t_wbc.control.teleop.teleop_streamer import TeleopStreamer + + +class TeleopPolicy(Policy): + """ + Robot-agnostic teleop policy. + Clean separation: IK processing vs command passing. + All robot-specific properties are abstracted through robot_model and hand_ik_solvers. + """ + + def __init__( + self, + body_control_device: str, + hand_control_device: str, + robot_model: RobotModel, + retargeting_ik: TeleopRetargetingIK, + body_streamer_ip: str = "192.168.?.?", + body_streamer_keyword: str = "shoulder", + enable_real_device: bool = True, + replay_data_path: Optional[str] = None, + replay_speed: float = 1.0, + wait_for_activation: int = 5, + activate_keyboard_listener: bool = True, + ): + if activate_keyboard_listener: + from gr00t_wbc.control.utils.keyboard_dispatcher import KeyboardListenerSubscriber + + self.keyboard_listener = KeyboardListenerSubscriber() + else: + self.keyboard_listener = None + + self.wait_for_activation = wait_for_activation + + self.teleop_streamer = TeleopStreamer( + robot_model=robot_model, + body_control_device=body_control_device, + hand_control_device=hand_control_device, + enable_real_device=enable_real_device, + body_streamer_ip=body_streamer_ip, + body_streamer_keyword=body_streamer_keyword, + replay_data_path=replay_data_path, + replay_speed=replay_speed, + ) + self.robot_model = robot_model + self.retargeting_ik = retargeting_ik + self.is_active = False + + self.latest_left_wrist_data = np.eye(4) + self.latest_right_wrist_data = np.eye(4) + self.latest_left_fingers_data = {"position": np.zeros((25, 4, 4))} + self.latest_right_fingers_data = {"position": np.zeros((25, 4, 4))} + + def set_goal(self, goal: dict[str, any]): + # The current teleop policy doesn't take higher level commands yet. + pass + + def get_action(self) -> dict[str, any]: + # Get structured data + streamer_output = self.teleop_streamer.get_streamer_data() + + # Handle activation using teleop_data commands + self.check_activation( + streamer_output.teleop_data, wait_for_activation=self.wait_for_activation + ) + + action = {} + + # Process streamer data if active + if self.is_active and streamer_output.ik_data: + body_data = streamer_output.ik_data["body_data"] + left_hand_data = streamer_output.ik_data["left_hand_data"] + right_hand_data = streamer_output.ik_data["right_hand_data"] + + left_wrist_name = self.robot_model.supplemental_info.hand_frame_names["left"] + right_wrist_name = self.robot_model.supplemental_info.hand_frame_names["right"] + self.latest_left_wrist_data = body_data[left_wrist_name] + self.latest_right_wrist_data = body_data[right_wrist_name] + self.latest_left_fingers_data = left_hand_data + self.latest_right_fingers_data = right_hand_data + + # TODO: This stores the same data again + ik_data = { + "body_data": body_data, + "left_hand_data": left_hand_data, + "right_hand_data": right_hand_data, + } + action["ik_data"] = ik_data + + # Wrist poses (pos and quat) + # TODO: This stores the same wrist poses in two different formats + left_wrist_matrix = self.latest_left_wrist_data + right_wrist_matrix = self.latest_right_wrist_data + left_wrist_pose = np.concatenate( + [ + left_wrist_matrix[:3, 3], + R.from_matrix(left_wrist_matrix[:3, :3]).as_quat(scalar_first=True), + ] + ) + right_wrist_pose = np.concatenate( + [ + right_wrist_matrix[:3, 3], + R.from_matrix(right_wrist_matrix[:3, :3]).as_quat(scalar_first=True), + ] + ) + + # Combine IK results with control commands (no teleop_data commands) + action.update( + { + "left_wrist": self.latest_left_wrist_data, + "right_wrist": self.latest_right_wrist_data, + "left_fingers": self.latest_left_fingers_data, + "right_fingers": self.latest_right_fingers_data, + "wrist_pose": np.concatenate([left_wrist_pose, right_wrist_pose]), + **streamer_output.control_data, # Only control & data collection commands pass through + **streamer_output.data_collection_data, + } + ) + + # Run retargeting IK + if "ik_data" in action: + self.retargeting_ik.set_goal(action["ik_data"]) + action["target_upper_body_pose"] = self.retargeting_ik.get_action() + + return action + + def close(self) -> bool: + self.teleop_streamer.stop_streaming() + return True + + def check_activation(self, teleop_data: dict, wait_for_activation: int = 5): + """Activation logic only looks at teleop data commands""" + key = self.keyboard_listener.read_msg() if self.keyboard_listener else "" + toggle_activation_by_keyboard = key == "l" + reset_teleop_policy_by_keyboard = key == "k" + toggle_activation_by_teleop = teleop_data.get("toggle_activation", False) + + if reset_teleop_policy_by_keyboard: + print("Resetting teleop policy") + self.reset() + + if toggle_activation_by_keyboard or toggle_activation_by_teleop: + self.is_active = not self.is_active + if self.is_active: + print("Starting teleop policy") + + if wait_for_activation > 0 and toggle_activation_by_keyboard: + print(f"Sleeping for {wait_for_activation} seconds before starting teleop...") + for i in range(wait_for_activation, 0, -1): + print(f"Starting in {i}...") + time.sleep(1) + + # dda: calibration logic should use current IK data + self.teleop_streamer.calibrate() + print("Teleop policy calibrated") + else: + print("Stopping teleop policy") + + @contextmanager + def activate(self): + try: + yield self + finally: + self.close() + + def handle_keyboard_button(self, keycode): + """ + Handle keyboard input with proper state toggle. + """ + if keycode == "l": + # Toggle start state + self.is_active = not self.is_active + # Reset initialization when stopping + if not self.is_active: + self._initialized = False + if keycode == "k": + print("Resetting teleop policy") + self.reset() + + def activate_policy(self, wait_for_activation: int = 5): + """activate the teleop policy""" + self.is_active = False + self.check_activation( + teleop_data={"toggle_activation": True}, wait_for_activation=wait_for_activation + ) + + def reset(self, wait_for_activation: int = 5, auto_activate: bool = False): + """Reset the teleop policy to the initial state, and re-activate it.""" + self.teleop_streamer.reset() + self.retargeting_ik.reset() + self.is_active = False + self.latest_left_wrist_data = np.eye(4) + self.latest_right_wrist_data = np.eye(4) + self.latest_left_fingers_data = {"position": np.zeros((25, 4, 4))} + self.latest_right_fingers_data = {"position": np.zeros((25, 4, 4))} + + if auto_activate: + self.activate_policy(wait_for_activation) diff --git a/gr00t_wbc/control/policy/wbc_policy_factory.py b/gr00t_wbc/control/policy/wbc_policy_factory.py new file mode 100644 index 0000000..c315e36 --- /dev/null +++ b/gr00t_wbc/control/policy/wbc_policy_factory.py @@ -0,0 +1,65 @@ +import os +from pathlib import Path +import time + +import numpy as np + +import gr00t_wbc +from gr00t_wbc.control.main.constants import DEFAULT_BASE_HEIGHT, DEFAULT_NAV_CMD +from gr00t_wbc.control.policy.g1_gear_wbc_policy import G1GearWbcPolicy +from gr00t_wbc.control.policy.identity_policy import IdentityPolicy +from gr00t_wbc.control.policy.interpolation_policy import InterpolationPolicy + +from .g1_decoupled_whole_body_policy import G1DecoupledWholeBodyPolicy + +WBC_VERSIONS = ["gear_wbc"] + + +def get_wbc_policy( + robot_type, + robot_model, + wbc_config, + init_time=time.monotonic(), +): + current_upper_body_pose = robot_model.get_initial_upper_body_pose() + + if robot_type == "g1": + upper_body_policy_type = wbc_config.get("upper_body_policy_type", "interpolation") + if upper_body_policy_type == "identity": + upper_body_policy = IdentityPolicy() + else: + upper_body_policy = InterpolationPolicy( + init_time=init_time, + init_values={ + "target_upper_body_pose": current_upper_body_pose, + "base_height_command": np.array([DEFAULT_BASE_HEIGHT]), + "navigate_cmd": np.array([DEFAULT_NAV_CMD]), + }, + max_change_rate=wbc_config["upper_body_max_joint_speed"], + ) + + lower_body_policy_type = wbc_config.get("VERSION", "gear_wbc") + if lower_body_policy_type not in ["gear_wbc"]: + raise ValueError( + f"Invalid lower body policy version: {lower_body_policy_type}. " + f"Only 'gear_wbc' is supported." + ) + + # Get the base path to gr00t_wbc and convert to Path object + package_path = Path(os.path.dirname(gr00t_wbc.__file__)) + gear_wbc_config = str(package_path / ".." / wbc_config["GEAR_WBC_CONFIG"]) + if lower_body_policy_type == "gear_wbc": + lower_body_policy = G1GearWbcPolicy( + robot_model=robot_model, + config=gear_wbc_config, + model_path=wbc_config["model_path"], + ) + + wbc_policy = G1DecoupledWholeBodyPolicy( + robot_model=robot_model, + upper_body_policy=upper_body_policy, + lower_body_policy=lower_body_policy, + ) + else: + raise ValueError(f"Invalid robot type: {robot_type}") + return wbc_policy diff --git a/gr00t_wbc/control/robot_model/__init__.py b/gr00t_wbc/control/robot_model/__init__.py new file mode 100644 index 0000000..774d303 --- /dev/null +++ b/gr00t_wbc/control/robot_model/__init__.py @@ -0,0 +1,3 @@ +from .robot_model import ReducedRobotModel, RobotModel + +__all__ = ["RobotModel", "ReducedRobotModel"] diff --git a/gr00t_wbc/control/robot_model/instantiation/__init__.py b/gr00t_wbc/control/robot_model/instantiation/__init__.py new file mode 100644 index 0000000..79b21df --- /dev/null +++ b/gr00t_wbc/control/robot_model/instantiation/__init__.py @@ -0,0 +1,15 @@ +from .g1 import instantiate_g1_robot_model + + +def get_robot_type_and_model(robot: str, enable_waist_ik: bool = False): + """Get the robot type from the robot name.""" + if robot.lower().startswith("g1"): + if "FixedLowerBody" in robot or "FloatingBody" in robot: + waist_location = "upper_body" + elif enable_waist_ik: + waist_location = "lower_and_upper_body" + else: + waist_location = "lower_body" + return "g1", instantiate_g1_robot_model(waist_location=waist_location) + else: + raise ValueError(f"Invalid robot name: {robot}") diff --git a/gr00t_wbc/control/robot_model/instantiation/g1.py b/gr00t_wbc/control/robot_model/instantiation/g1.py new file mode 100644 index 0000000..6191fbc --- /dev/null +++ b/gr00t_wbc/control/robot_model/instantiation/g1.py @@ -0,0 +1,62 @@ +import os +from pathlib import Path +from typing import Literal + +from gr00t_wbc.control.robot_model.robot_model import RobotModel +from gr00t_wbc.control.robot_model.supplemental_info.g1.g1_supplemental_info import ( + ElbowPose, + G1SupplementalInfo, + WaistLocation, +) + + +def instantiate_g1_robot_model( + waist_location: Literal["lower_body", "upper_body", "lower_and_upper_body"] = "lower_body", + high_elbow_pose: bool = False, +): + """ + Instantiate a G1 robot model with configurable waist location and pose. + + Args: + waist_location: Whether to put waist in "lower_body" (default G1 behavior), + "upper_body" (waist controlled with arms/manipulation via IK), + or "lower_and_upper_body" (waist reference from arms/manipulation + via IK then passed to lower body policy) + high_elbow_pose: Whether to use high elbow pose configuration for default joint positions + + Returns: + RobotModel: Configured G1 robot model + """ + project_root = Path(__file__).resolve().parent.parent.parent.parent.parent + robot_model_config = { + "asset_path": os.path.join(project_root, "gr00t_wbc/control/robot_model/model_data/g1"), + "urdf_path": os.path.join( + project_root, "gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_with_hand.urdf" + ), + } + assert waist_location in [ + "lower_body", + "upper_body", + "lower_and_upper_body", + ], f"Invalid waist_location: {waist_location}. Must be 'lower_body' or 'upper_body' or 'lower_and_upper_body'" + + # Map string values to enums + waist_location_enum = { + "lower_body": WaistLocation.LOWER_BODY, + "upper_body": WaistLocation.UPPER_BODY, + "lower_and_upper_body": WaistLocation.LOWER_AND_UPPER_BODY, + }[waist_location] + + elbow_pose_enum = ElbowPose.HIGH if high_elbow_pose else ElbowPose.LOW + + # Create single configurable supplemental info instance + robot_model_supplemental_info = G1SupplementalInfo( + waist_location=waist_location_enum, elbow_pose=elbow_pose_enum + ) + + robot_model = RobotModel( + robot_model_config["urdf_path"], + robot_model_config["asset_path"], + supplemental_info=robot_model_supplemental_info, + ) + return robot_model diff --git a/gr00t_wbc/control/robot_model/model_data/g1/g1_29dof.urdf b/gr00t_wbc/control/robot_model/model_data/g1/g1_29dof.urdf new file mode 100644 index 0000000..5f91f34 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/g1_29dof.urdf @@ -0,0 +1,1091 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_old.xml b/gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_old.xml new file mode 100755 index 0000000..4fd64d1 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_old.xml @@ -0,0 +1,568 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_with_hand.urdf b/gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_with_hand.urdf new file mode 100644 index 0000000..e057336 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_with_hand.urdf @@ -0,0 +1,1497 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_with_hand.xml b/gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_with_hand.xml new file mode 100644 index 0000000..a53cd99 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_with_hand.xml @@ -0,0 +1,748 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_with_hand_rev_1_0_activatedfinger.xml b/gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_with_hand_rev_1_0_activatedfinger.xml new file mode 100644 index 0000000..b508fe4 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/g1_29dof_with_hand_rev_1_0_activatedfinger.xml @@ -0,0 +1,669 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + --> + + + + + + + + + + + + + diff --git a/gr00t_wbc/control/robot_model/model_data/g1/lift_box_43dof.xml b/gr00t_wbc/control/robot_model/model_data/g1/lift_box_43dof.xml new file mode 100644 index 0000000..3ac8a3f --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/lift_box_43dof.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/head_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/head_link.STL new file mode 100644 index 0000000..401f822 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/head_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:005fb67fbd3eff94aa8bf4a6e83238174e9f91b6721f7111594322f223724411 +size 932784 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_ankle_pitch_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_ankle_pitch_link.STL new file mode 100644 index 0000000..7cd7052 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_ankle_pitch_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d49e3abc6f5b12e532062cd575b87b5ef40cd2a3fc18f54a1ca5bba4f773d51d +size 71184 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_ankle_roll_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_ankle_roll_link.STL new file mode 100644 index 0000000..cb69f65 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_ankle_roll_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c4092af943141d4d9f74232f3cfa345afc6565f46a077793b8ae0e68b39dc33f +size 653384 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_elbow_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_elbow_link.STL new file mode 100644 index 0000000..7fa0527 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_elbow_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa752198accd104d5c4c3a01382e45165b944fbbc5acce085059223324e5bed3 +size 88784 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_elbow_link_merge.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_elbow_link_merge.STL new file mode 100644 index 0000000..f287918 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_elbow_link_merge.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:73c8adc6346b900eb6a2ae7159e25dfda1be60cd98affe13536e01ab33a6c1de +size 3136784 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_index_0_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_index_0_link.STL new file mode 100644 index 0000000..a87568d --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_index_0_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b35f2f77211d5a366f0b9a4e47c4ee35e536731266f1a34e9efa12db579b892 +size 475984 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_index_1_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_index_1_link.STL new file mode 100644 index 0000000..c6c91dd --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_index_1_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e315ebc8a7a0cb98e033985b586b20d81cf8aa761181ae61ce56fcb14077a06 +size 1521784 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_middle_0_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_middle_0_link.STL new file mode 100644 index 0000000..a87568d --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_middle_0_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b35f2f77211d5a366f0b9a4e47c4ee35e536731266f1a34e9efa12db579b892 +size 475984 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_middle_1_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_middle_1_link.STL new file mode 100644 index 0000000..c6c91dd --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_middle_1_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e315ebc8a7a0cb98e033985b586b20d81cf8aa761181ae61ce56fcb14077a06 +size 1521784 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_palm_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_palm_link.STL new file mode 100644 index 0000000..c8fcc3e --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_palm_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23a486b75bd78a9bf03cec25d84d87f97f3dae038cf21a743b6d469b337e4004 +size 2140184 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_thumb_0_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_thumb_0_link.STL new file mode 100644 index 0000000..7fb38c1 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_thumb_0_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a90c721661c0622685488a3c74d1e122c7da89242d3a1daef75edb83422d05e0 +size 8884 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_thumb_1_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_thumb_1_link.STL new file mode 100644 index 0000000..54f7754 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_thumb_1_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:445c54a45bc13ce36001556f66bc0f49c83cb40321205ae4d676bb2874325684 +size 475984 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_thumb_2_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_thumb_2_link.STL new file mode 100644 index 0000000..3e4f124 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hand_thumb_2_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d8dbe5085acfc213d21aa8b0782e89cd79084e9678f3a85fc7b04a86b029db5 +size 1521784 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hip_pitch_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hip_pitch_link.STL new file mode 100644 index 0000000..4cf7475 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hip_pitch_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4725168105ee768ee31638ef22b53f6be2d7641bfd7cfefe803488d884776fa4 +size 181684 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hip_roll_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hip_roll_link.STL new file mode 100644 index 0000000..585f604 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hip_roll_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91f25922ee8a7c3152790051bebad17b4d9cd243569c38fe340285ff93a97acf +size 192184 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hip_yaw_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hip_yaw_link.STL new file mode 100644 index 0000000..b46a741 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_hip_yaw_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a16d88aa6ddac8083aa7ad55ed317bea44b1fa003d314fba88965b7ed0f3b55b +size 296284 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_knee_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_knee_link.STL new file mode 100644 index 0000000..2dcf84e --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_knee_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d92b9e3d3a636761150bb8025e32514c4602b91c7028d523ee42b3e632de477 +size 854884 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_rubber_hand.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_rubber_hand.STL new file mode 100644 index 0000000..04a2fa2 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_rubber_hand.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cff2221a690fa69303f61fce68f2d155c1517b52efb6ca9262dd56e0bc6e70fe +size 2287484 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_shoulder_pitch_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_shoulder_pitch_link.STL new file mode 100644 index 0000000..926d980 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_shoulder_pitch_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0d1cfd02fcf0d42f95e678eeca33da3afbcc366ffba5c052847773ec4f31d52 +size 176784 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_shoulder_roll_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_shoulder_roll_link.STL new file mode 100644 index 0000000..4c6840b --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_shoulder_roll_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fb9df21687773522598dc384f1a2945c7519f11cbc8bd372a49170316d6eee88 +size 400284 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_shoulder_yaw_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_shoulder_yaw_link.STL new file mode 100644 index 0000000..89b0e06 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_shoulder_yaw_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1aa97e9748e924336567992181f78c7cd0652fd52a4afcca3df6b2ef6f9e712e +size 249184 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_pitch_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_pitch_link.STL new file mode 100644 index 0000000..f9c9e4f --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_pitch_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b251d8e05047f695d0f536cd78c11973cfa4e78d08cfe82759336cc3471de3a9 +size 85984 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_roll_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_roll_link.STL new file mode 100644 index 0000000..2097ca3 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_roll_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:edc387c9a0ba8c2237e9b296d32531426fabeb6f53e58df45c76106bca74148c +size 356184 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_roll_rubber_hand.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_roll_rubber_hand.STL new file mode 100644 index 0000000..7c58819 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_roll_rubber_hand.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e81030abd023bd9e4a308ef376d814a2c12d684d8a7670c335bbd5cd7809c909 +size 3484884 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_yaw_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_yaw_link.STL new file mode 100644 index 0000000..692f4b0 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/left_wrist_yaw_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:83f8fb3a726bf9613d65dd14f0f447cb918c3c95b3938042a0c9c09749267d3b +size 318684 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/logo_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/logo_link.STL new file mode 100644 index 0000000..6c25961 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/logo_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8571a0a19bc4916fa55f91449f51d5fdefd751000054865a842449429d5f155b +size 243384 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/pelvis.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/pelvis.STL new file mode 100644 index 0000000..f98a88d --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/pelvis.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ba6bbc888e630550140d3c26763f10206da8c8bd30ed886b8ede41c61f57a31 +size 1060884 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/pelvis_contour_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/pelvis_contour_link.STL new file mode 100644 index 0000000..8025bc0 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/pelvis_contour_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5cc5c2c7a312329e3feeb2b03d3fc09fc29705bd01864f6767e51be959662420 +size 1805184 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_ankle_pitch_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_ankle_pitch_link.STL new file mode 100644 index 0000000..94a586a --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_ankle_pitch_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15be426539ec1be70246d4d82a168806db64a41301af8b35c197a33348c787a9 +size 71184 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_ankle_roll_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_ankle_roll_link.STL new file mode 100644 index 0000000..3c38507 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_ankle_roll_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b66222ea56653e627711b56d0a8949b4920da5df091da0ceb343f54e884e3a5 +size 653784 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_elbow_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_elbow_link.STL new file mode 100644 index 0000000..52aa0eb --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_elbow_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1be925d7aa268bb8fddf5362b9173066890c7d32092c05638608126e59d1e2ab +size 88784 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_elbow_link_merge.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_elbow_link_merge.STL new file mode 100644 index 0000000..b042703 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_elbow_link_merge.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6def7100e5bbb276c7498c5b7a8c8812f560ca35d6bd09c376ebab93595be7f +size 3058284 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_index_0_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_index_0_link.STL new file mode 100644 index 0000000..5e2ef47 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_index_0_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12ab4300c95e437e834f9aef772b3b431c671bf34338930b52ad11aef73bbd0d +size 475984 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_index_1_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_index_1_link.STL new file mode 100644 index 0000000..8572ae1 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_index_1_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c9c34efce4563cacdcfd29fc838a982976d40f5442c71219811dcbbf3923a33d +size 1521784 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_middle_0_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_middle_0_link.STL new file mode 100644 index 0000000..5e2ef47 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_middle_0_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12ab4300c95e437e834f9aef772b3b431c671bf34338930b52ad11aef73bbd0d +size 475984 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_middle_1_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_middle_1_link.STL new file mode 100644 index 0000000..8572ae1 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_middle_1_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c9c34efce4563cacdcfd29fc838a982976d40f5442c71219811dcbbf3923a33d +size 1521784 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_palm_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_palm_link.STL new file mode 100644 index 0000000..fd2f7f0 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_palm_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86c0b231cc44477d64a6493e5a427ba16617a00738112dd187c652675b086fb9 +size 2140184 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_thumb_0_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_thumb_0_link.STL new file mode 100644 index 0000000..a385d96 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_thumb_0_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:544298d0ea1088f5b276a10cc6a6a9e533efdd91594955fdc956c46211d07f83 +size 8884 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_thumb_1_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_thumb_1_link.STL new file mode 100644 index 0000000..c118de7 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_thumb_1_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a9a820da8dd10f298778b714f1364216e8a5976f4fd3a05689ea26327d44bf6 +size 475984 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_thumb_2_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_thumb_2_link.STL new file mode 100644 index 0000000..0979fb6 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hand_thumb_2_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f1bfb37668e8f61801c8d25f171fa1949e08666be86c67acad7e0079937cc45 +size 1521784 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hip_pitch_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hip_pitch_link.STL new file mode 100644 index 0000000..064085f --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hip_pitch_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4f3c99d7f4a7d34eadbef9461fc66e3486cb5442db1ec50c86317d459f1a9c6 +size 181284 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hip_roll_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hip_roll_link.STL new file mode 100644 index 0000000..6544025 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hip_roll_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c254ef66a356f492947f360dd931965477b631e3fcc841f91ccc46d945d54f6 +size 192684 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hip_yaw_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hip_yaw_link.STL new file mode 100644 index 0000000..0ad7bee --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_hip_yaw_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e479c2936ca2057e9eb2f7dff6c189b7419d7b8484dea0b298cbb36a2a6aa668 +size 296284 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_knee_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_knee_link.STL new file mode 100644 index 0000000..65e8a70 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_knee_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63c4008449c9bbe701a6e2b557b7a252e90cf3a5abcf54cee46862b9a69f8ec8 +size 852284 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_rubber_hand.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_rubber_hand.STL new file mode 100644 index 0000000..58148fb --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_rubber_hand.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99533b778bca6246144fa511bb9d4e555e075c641f2a0251e04372869cd99d67 +size 2192684 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_shoulder_pitch_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_shoulder_pitch_link.STL new file mode 100644 index 0000000..48a1c46 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_shoulder_pitch_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24cdb387e0128dfe602770a81c56cdce3a0181d34d039a11d1aaf8819b7b8c02 +size 176784 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_shoulder_roll_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_shoulder_roll_link.STL new file mode 100644 index 0000000..2a5d22f --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_shoulder_roll_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:962b97c48f9ce9e8399f45dd9522e0865d19aa9fd299406b2d475a8fc4a53e81 +size 401884 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_shoulder_yaw_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_shoulder_yaw_link.STL new file mode 100644 index 0000000..0882a56 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_shoulder_yaw_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0b76489271da0c72461a344c9ffb0f0c6e64f019ea5014c1624886c442a2fe5 +size 249984 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_pitch_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_pitch_link.STL new file mode 100644 index 0000000..2efd25c --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_pitch_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d22f8f3b3127f15a63e5be1ee273cd5075786c3142f1c3d9f76cbf43d2a26477 +size 79584 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_roll_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_roll_link.STL new file mode 100644 index 0000000..77d23a7 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_roll_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a7ee9212ff5b94d6cb7f52bb1bbf3f352194d5b598acff74f4c77d340c5b344f +size 356084 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_roll_rubber_hand.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_roll_rubber_hand.STL new file mode 100644 index 0000000..6f122af --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_roll_rubber_hand.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0729aff1ac4356f9314de13a46906267642e58bc47f0d8a7f17f6590a6242ccf +size 3481584 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_yaw_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_yaw_link.STL new file mode 100644 index 0000000..77edbb4 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/right_wrist_yaw_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc9dece2d12509707e0057ba2e48df8f3d56db0c79410212963a25e8a50f61a6 +size 341484 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_L_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_L_link.STL new file mode 100644 index 0000000..cc2cbbf --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_L_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82be7f93e85b3d303a1d1e1847e2c916939bd61c424ed1ebd28691ec33909dd1 +size 203584 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_L_rod_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_L_rod_link.STL new file mode 100644 index 0000000..dd439bf --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_L_rod_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c10de1effa7ea797ac268006aa2a739036c7e1f326b2012d711ee2c20c5a6e96 +size 74884 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_R_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_R_link.STL new file mode 100644 index 0000000..422ffe4 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_R_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:54ded433a3a0c76027365856fdbd55215643de88846f7d436598a4071e682725 +size 203584 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_R_rod_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_R_rod_link.STL new file mode 100644 index 0000000..6df46c8 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_constraint_R_rod_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb83fd38a9f06c99e3f301c70f12d94ae770a8f0cf9501f83580a27f908990b4 +size 74884 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_link.STL new file mode 100644 index 0000000..e4fb87c --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e96d023f0368a4e3450b86ca5d4f10227d8141156a373e7da8cb3c93266523e0 +size 2232984 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_link_rev_1_0.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_link_rev_1_0.STL new file mode 100644 index 0000000..836b992 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/torso_link_rev_1_0.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11ddb46f2098efbbd8816b1d65893632d8e78be936376c7cdcd6771899ccc723 +size 2570584 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_constraint_L.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_constraint_L.STL new file mode 100644 index 0000000..6ec689b --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_constraint_L.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ebafdcb4de6871113f0ca2c356618d6e46b1d50f6c0bf9e37f47b9d8e100d99 +size 114684 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_constraint_R.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_constraint_R.STL new file mode 100644 index 0000000..69fd76a --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_constraint_R.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:791902a291ffbd35ab97383b7b44ea5d975de7c80eef838797c970790b382ca9 +size 114684 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_roll_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_roll_link.STL new file mode 100644 index 0000000..007f56d --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_roll_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34f0aa73f41131230be4d25876c944fdf6c24d62553f199ff8b980c15e8913df +size 24184 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_roll_link_rev_1_0.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_roll_link_rev_1_0.STL new file mode 100644 index 0000000..36a5a70 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_roll_link_rev_1_0.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b67c347a05abc3e8ddae600d98a082bee273bb39f8e651647708b0a7140a8a97 +size 85884 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_support_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_support_link.STL new file mode 100644 index 0000000..4a50f94 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_support_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1fae9e1bb609848a1667d32eed8d6083ae443538a306843056a2a660f1b2926a +size 150484 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_yaw_link.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_yaw_link.STL new file mode 100644 index 0000000..c049deb --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_yaw_link.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2883f20e03f09b669b5b4ce10677ee6b5191c0934b584d7cbaef2d0662856ffb +size 336284 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_yaw_link_rev_1_0.STL b/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_yaw_link_rev_1_0.STL new file mode 100644 index 0000000..dc628fb --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/meshes/waist_yaw_link_rev_1_0.STL @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec6db442b11f25eed898b5add07940c85d804f300de24dcbd264ccd8be7d554c +size 619984 diff --git a/gr00t_wbc/control/robot_model/model_data/g1/pnp_bottle_43dof.xml b/gr00t_wbc/control/robot_model/model_data/g1/pnp_bottle_43dof.xml new file mode 100644 index 0000000..cf4d4da --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/pnp_bottle_43dof.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gr00t_wbc/control/robot_model/model_data/g1/pnp_cube_43dof.xml b/gr00t_wbc/control/robot_model/model_data/g1/pnp_cube_43dof.xml new file mode 100644 index 0000000..cb61b70 --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/pnp_cube_43dof.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gr00t_wbc/control/robot_model/model_data/g1/scene_29dof.xml b/gr00t_wbc/control/robot_model/model_data/g1/scene_29dof.xml new file mode 100644 index 0000000..0b60f2f --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/scene_29dof.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gr00t_wbc/control/robot_model/model_data/g1/scene_29dof_activated3dex.xml b/gr00t_wbc/control/robot_model/model_data/g1/scene_29dof_activated3dex.xml new file mode 100644 index 0000000..2916dfb --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/scene_29dof_activated3dex.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gr00t_wbc/control/robot_model/model_data/g1/scene_43dof.xml b/gr00t_wbc/control/robot_model/model_data/g1/scene_43dof.xml new file mode 100644 index 0000000..d66b23d --- /dev/null +++ b/gr00t_wbc/control/robot_model/model_data/g1/scene_43dof.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gr00t_wbc/control/robot_model/robot_model.py b/gr00t_wbc/control/robot_model/robot_model.py new file mode 100644 index 0000000..a3d94c8 --- /dev/null +++ b/gr00t_wbc/control/robot_model/robot_model.py @@ -0,0 +1,772 @@ +from typing import List, Optional, Set, Union + +import numpy as np +import pinocchio as pin + +from gr00t_wbc.control.robot_model.supplemental_info import RobotSupplementalInfo + + +class RobotModel: + def __init__( + self, + urdf_path, + asset_path, + set_floating_base=False, + supplemental_info: Optional[RobotSupplementalInfo] = None, + ): + self.pinocchio_wrapper = pin.RobotWrapper.BuildFromURDF( + filename=urdf_path, + package_dirs=[asset_path], + root_joint=pin.JointModelFreeFlyer() if set_floating_base else None, + ) + self.is_floating_base_model = set_floating_base + + self.joint_to_dof_index = {} + # Assume we only have single-dof joints + # First two names correspond to universe and floating base joints + names = ( + self.pinocchio_wrapper.model.names[2:] + if set_floating_base + else self.pinocchio_wrapper.model.names[1:] + ) + for name in names: + j_id = self.pinocchio_wrapper.model.getJointId(name) + jmodel = self.pinocchio_wrapper.model.joints[j_id] + self.joint_to_dof_index[name] = jmodel.idx_q + + # Store joint limits only for actual joints (excluding floating base) + # if set floating base is true and the robot can move in the world + # then we don't want to impose joint limits for the 7 dofs corresponding + # to the floating base dofs. + root_nq = 7 if set_floating_base else 0 + self.upper_joint_limits = self.pinocchio_wrapper.model.upperPositionLimit[root_nq:].copy() + self.lower_joint_limits = self.pinocchio_wrapper.model.lowerPositionLimit[root_nq:].copy() + + # Set up supplemental info if provided + self.supplemental_info = supplemental_info + if self.supplemental_info is not None: + # Cache indices for body and hand actuated joints separately + self._body_actuated_joint_indices = [ + self.dof_index(name) for name in self.supplemental_info.body_actuated_joints + ] + self._left_hand_actuated_joint_indices = [ + self.dof_index(name) for name in self.supplemental_info.left_hand_actuated_joints + ] + self._right_hand_actuated_joint_indices = [ + self.dof_index(name) for name in self.supplemental_info.right_hand_actuated_joints + ] + self._hand_actuated_joint_indices = ( + self._left_hand_actuated_joint_indices + self._right_hand_actuated_joint_indices + ) + + # Cache indices for joint groups, handling nested groups + self._joint_group_indices = {} + for group_name, group_info in self.supplemental_info.joint_groups.items(): + indices = [] + # Add indices for direct joints + indices.extend([self.dof_index(name) for name in group_info["joints"]]) + # Add indices from subgroups + for subgroup_name in group_info["groups"]: + indices.extend(self.get_joint_group_indices(subgroup_name)) + self._joint_group_indices[group_name] = sorted(set(indices)) + + # Update joint limits from supplemental info if available + if ( + hasattr(self.supplemental_info, "joint_limits") + and self.supplemental_info.joint_limits + ): + for joint_name, limits in self.supplemental_info.joint_limits.items(): + if joint_name in self.joint_to_dof_index: + idx = self.joint_to_dof_index[joint_name] - root_nq + self.lower_joint_limits[idx] = limits[0] + self.upper_joint_limits[idx] = limits[1] + + # Initialize default body pose + self.default_body_pose = self.q_zero.copy() + + # Update with supplemental info if available + if self.supplemental_info is not None: + default_joint_q = self.supplemental_info.default_joint_q + for joint, joint_values in default_joint_q.items(): + # Get the joint name mapping for this type + joint_mapping = self.supplemental_info.joint_name_mapping[joint] + + # Handle both single joint names and left/right mappings + if isinstance(joint_mapping, str): + # Single joint (e.g., waist joints) + if joint_mapping in self.joint_to_dof_index: + joint_idx = self.dof_index(joint_mapping) + self.default_body_pose[joint_idx] = ( + joint_values # joint_values is the value for single joints + ) + else: + # Left/right mapping (e.g., arm joints) + for side, value in joint_values.items(): + if side in joint_mapping and joint_mapping[side] in self.joint_to_dof_index: + joint_idx = self.dof_index(joint_mapping[side]) + self.default_body_pose[joint_idx] = value + + # Initialize initial body pose + self.initial_body_pose = self.default_body_pose.copy() + + @property + def num_dofs(self) -> int: + """Get the number of degrees of freedom of the robot (floating base pose + joints).""" + return self.pinocchio_wrapper.model.nq + + @property + def q_zero(self) -> np.ndarray: + """Get the zero pose of the robot.""" + return self.pinocchio_wrapper.q0 + + @property + def joint_names(self) -> List[str]: + """Get the names of the joints of the robot.""" + return list(self.joint_to_dof_index.keys()) + + @property + def num_joints(self) -> int: + """Get the number of joints of the robot.""" + return len(self.joint_to_dof_index) + + def dof_index(self, joint_name: str) -> int: + """ + Get the index in the degrees of freedom vector corresponding + to the single-DoF joint with name `joint_name`. + """ + if joint_name not in self.joint_to_dof_index: + raise ValueError( + f"Unknown joint name: '{joint_name}'. " + f"Available joints: {list(self.joint_to_dof_index.keys())}" + ) + return self.joint_to_dof_index[joint_name] + + def get_body_actuated_joint_indices(self) -> List[int]: + """ + Get the indices of body actuated joints in the full configuration. + Ordering is that of the actuated joints as defined in the supplemental info. + Requires supplemental_info to be provided. + """ + if self.supplemental_info is None: + raise ValueError("supplemental_info must be provided to use this method") + return self._body_actuated_joint_indices + + def get_hand_actuated_joint_indices(self, side: str = "both") -> List[int]: + """ + Get the indices of hand actuated joints in the full configuration. + Ordering is that of the actuated joints as defined in the supplemental info. + Requires supplemental_info to be provided. + + Args: + side: String specifying which hand to get indices for ('left', 'right', or 'both') + """ + if self.supplemental_info is None: + raise ValueError("supplemental_info must be provided to use this method") + + if side.lower() == "both": + return self._hand_actuated_joint_indices + elif side.lower() == "left": + return self._left_hand_actuated_joint_indices + elif side.lower() == "right": + return self._right_hand_actuated_joint_indices + else: + raise ValueError("side must be 'left', 'right', or 'both'") + + def get_joint_group_indices(self, group_names: Union[str, Set[str]]) -> List[int]: + """ + Get the indices of joints in one or more groups in the full configuration. + Requires supplemental_info to be provided. + The returned indices are sorted in ascending order, so that the joint ordering + of the full model is preserved. + + Args: + group_names: Either a single group name (str) or a set of group names (Set[str]) + + Returns: + List of joint indices in sorted order with no duplicates + """ + if self.supplemental_info is None: + raise ValueError("supplemental_info must be provided to use this method") + + # Convert single string to set for uniform handling + if isinstance(group_names, str): + group_names = {group_names} + + # Collect indices from all groups + all_indices = set() + for group_name in group_names: + if group_name not in self._joint_group_indices: + raise ValueError(f"Unknown joint group: {group_name}") + all_indices.update(self._joint_group_indices[group_name]) + + return sorted(all_indices) + + def cache_forward_kinematics(self, q: np.ndarray, auto_clip=True) -> None: + """ + Perform forward kinematics to update the pose of every joint and frame + in the Pinocchio data structures for the given configuration `q`. + + :param q: A numpy array of shape (num_dofs,) representing the robot configuration. + """ + if q.shape[0] != self.num_dofs: + raise ValueError(f"Expected q of length {self.num_dofs}, got {q.shape[0]} instead.") + + # Apply auto-clip if enabled + if auto_clip: + q = self.clip_configuration(q) + + pin.framesForwardKinematics(self.pinocchio_wrapper.model, self.pinocchio_wrapper.data, q) + + def compute_gravity_compensation_torques( + self, q: np.ndarray, joint_groups: Union[str, List[str], Set[str]] = None, auto_clip=True + ) -> np.ndarray: + """ + Compute gravity compensation torques for specified joint groups using pinocchio. + + :param q: Robot configuration (joint positions) + :param joint_groups: Joint groups to compensate (e.g., "arms", ["left_arm", "waist"], + {"left_arm", "waist"}). If None, compensates all joints + :param auto_clip: Whether to automatically clip joint values to limits + :return: Array of gravity compensation torques for all DOFs (zero for non-compensated joints) + """ + if q.shape[0] != self.num_dofs: + raise ValueError(f"Expected q of length {self.num_dofs}, got {q.shape[0]} instead.") + + # Apply auto-clip if enabled + if auto_clip: + q = self.clip_configuration(q) + + try: + # Cache forward kinematics for the current configuration + self.cache_forward_kinematics(q, auto_clip=False) # Already clipped if needed + + # Compute gravity vector using RNEA with zero velocity and acceleration + v = np.zeros(self.num_dofs) + a = np.zeros(self.num_dofs) + + gravity_torques_full = pin.rnea( + self.pinocchio_wrapper.model, self.pinocchio_wrapper.data, q, v, a + ) + + # If no joint groups specified, return full gravity torques + if joint_groups is None: + return gravity_torques_full + + # Convert list to set for get_joint_group_indices compatibility + if isinstance(joint_groups, list): + joint_groups = set(joint_groups) + + # Get joint indices for specified groups - get_joint_group_indices handles str and Set[str] + try: + compensated_joint_indices = self.get_joint_group_indices(joint_groups) + except ValueError as e: + raise ValueError(f"Error resolving joint groups {joint_groups}: {e}") + + # Create mask for joints that should receive gravity compensation + compensation_mask = np.zeros(self.num_dofs, dtype=bool) + for joint_idx in compensated_joint_indices: + if 0 <= joint_idx < len(compensation_mask): + compensation_mask[joint_idx] = True + + # Apply mask to only compensate specified joints + compensated_torques = np.zeros_like(gravity_torques_full) + compensated_torques[compensation_mask] = gravity_torques_full[compensation_mask] + + return compensated_torques + + except Exception as e: + raise RuntimeError(f"Error computing gravity compensation: {e}") + + def clip_configuration(self, q: np.ndarray, margin: float = 1e-6) -> np.ndarray: + """ + Clip the configuration to stay within joint limits with a small tolerance. + + :param q: Configuration to clip + :param margin: Tolerance to keep away from joint limits + :return: Clipped configuration + """ + q_clipped = q.copy() + + # Only clip joint positions, not floating base + root_nq = 7 if self.is_floating_base_model else 0 + q_clipped[root_nq:] = np.clip( + q[root_nq:], self.lower_joint_limits + margin, self.upper_joint_limits - margin + ) + + return q_clipped + + def frame_placement(self, frame_name: str) -> pin.SE3: + """ + Returns the SE3 transform of the specified frame in the world coordinate system. + Note: make sure cache_forward_kinematics() has been previously called. + + :param frame_name: Name of the frame, e.g. "link_elbow_frame", "hand_imu_frame", etc. + :return: A pin.SE3 object representing the pose of the frame. + """ + model = self.pinocchio_wrapper.model + data = self.pinocchio_wrapper.data + + frame_id = model.getFrameId(frame_name) + if frame_id < 0 or frame_id >= len(model.frames): + valid_frames = [f.name for f in model.frames] + raise ValueError(f"Unknown frame '{frame_name}'. Valid frames: {valid_frames}") + + # Pinocchio's data.oMf[frame_id] is a pin.SE3. + return data.oMf[frame_id].copy() + + def get_body_actuated_joints(self, q: np.ndarray) -> np.ndarray: + """ + Get the configuration of body actuated joints from a full configuration. + + :param q: Configuration in full space + :return: Configuration of body actuated joints + """ + indices = self.get_body_actuated_joint_indices() + + return q[indices] + + def get_hand_actuated_joints(self, q: np.ndarray, side: str = "both") -> np.ndarray: + """ + Get the configuration of hand actuated joints from a full configuration. + + Args: + q: Configuration in full space + side: String specifying which hand to get joints for ('left', 'right', or 'both') + """ + indices = self.get_hand_actuated_joint_indices(side) + return q[indices] + + def get_configuration_from_actuated_joints( + self, + body_actuated_joint_values: np.ndarray, + hand_actuated_joint_values: Optional[np.ndarray] = None, + left_hand_actuated_joint_values: Optional[np.ndarray] = None, + right_hand_actuated_joint_values: Optional[np.ndarray] = None, + ) -> np.ndarray: + """ + Get the full configuration from the body and hand actuated joint configurations. + Can specify either both hands together or left and right hands separately. + + Args: + body_actuated_joint_values: Configuration of body actuated joints + hand_actuated_joint_values: Configuration of both hands' actuated joints (optional) + left_hand_actuated_joint_values: Configuration of left hand actuated joints (optional) + right_hand_actuated_joint_values: Configuration of right hand actuated joints (optional) + + Returns: + Full configuration including body and hand joints + """ + q = self.pinocchio_wrapper.q0.copy() + q[self.get_body_actuated_joint_indices()] = body_actuated_joint_values + + # Handle hand configurations + if hand_actuated_joint_values is not None: + # Use combined hand configuration + q[self.get_hand_actuated_joint_indices("both")] = hand_actuated_joint_values + else: + # Use separate hand configurations + if left_hand_actuated_joint_values is not None: + q[self.get_hand_actuated_joint_indices("left")] = left_hand_actuated_joint_values + if right_hand_actuated_joint_values is not None: + q[self.get_hand_actuated_joint_indices("right")] = right_hand_actuated_joint_values + + return q + + def reset_forward_kinematics(self) -> None: + """ + Reset the forward kinematics to the initial configuration. + """ + self.cache_forward_kinematics(self.q_zero) + + def get_initial_upper_body_pose(self) -> np.ndarray: + """ + Get the initial upper body pose of the robot. + """ + return self.initial_body_pose[self.get_joint_group_indices("upper_body")] + + def get_default_body_pose(self) -> np.ndarray: + """ + Get the default body pose of the robot. + """ + return self.default_body_pose + + def set_initial_body_pose(self, q: np.ndarray, q_idx=None) -> None: + """ + Set the initial body pose of the robot. + """ + if q_idx is None: + self.initial_body_pose = q + else: + self.initial_body_pose[q_idx] = q + + +class ReducedRobotModel(RobotModel): + """ + A class that creates a reduced order robot model by fixing certain joints. + This class maintains a mapping between the reduced state space and the full state space. + """ + + def __init__( + self, + full_robot_model: RobotModel, + fixed_joints: List[str], + fixed_values: Optional[List[float]] = None, + ): + """ + Create a reduced order robot model by fixing specified joints. + + :param full_robot_model: The original robot model + :param fixed_joints: List of joint names to fix + :param fixed_values: Optional list of values to fix the joints to. If None, uses the initial + joint positions (q0) from the full robot model. + """ + self.full_robot = full_robot_model + self.supplemental_info = full_robot_model.supplemental_info + + # If fixed_values is None, use q0 from the full robot model + if fixed_values is None: + fixed_values = [] + for joint_name in fixed_joints: + full_idx = full_robot_model.dof_index(joint_name) + fixed_values.append(full_robot_model.pinocchio_wrapper.q0[full_idx]) + elif len(fixed_joints) != len(fixed_values): + raise ValueError("fixed_joints and fixed_values must have the same length") + + # Store fixed joints and their values + self.fixed_joints = fixed_joints + self.fixed_values = fixed_values + + # Create mapping between reduced and full state spaces + self.reduced_to_full = [] + self.full_to_reduced = {} + + # Initialize with floating base indices if present + if full_robot_model.is_floating_base_model: + self.reduced_to_full.extend(range(7)) # Floating base indices + for i in range(7): + self.full_to_reduced[i] = i + + # Add active joint indices + for joint_name in full_robot_model.joint_names: + if joint_name not in fixed_joints: + full_idx = full_robot_model.dof_index(joint_name) + reduced_idx = len(self.reduced_to_full) + self.reduced_to_full.append(full_idx) + self.full_to_reduced[full_idx] = reduced_idx + + # Create a reduced Pinocchio model using buildReducedModel + # First, get the list of joint IDs to lock + locked_joint_ids = [] + for joint_name in fixed_joints: + joint_id = full_robot_model.pinocchio_wrapper.model.getJointId(joint_name) + if (full_robot_model.is_floating_base_model and joint_id > 1) or ( + not full_robot_model.is_floating_base_model and joint_id > 0 + ): + locked_joint_ids.append(joint_id) + + # First build the reduced kinematic model + reduced_model = pin.buildReducedModel( + full_robot_model.pinocchio_wrapper.model, + locked_joint_ids, + full_robot_model.pinocchio_wrapper.q0, + ) + + # Then build the reduced geometry models using the reduced kinematic model + self.pinocchio_wrapper = pin.RobotWrapper( + model=reduced_model, + ) + + # Create joint to dof index mapping + self.joint_to_dof_index = {} + # Assume we only have single-dof joints + # First two names correspond to universe and floating base joints + names = ( + self.pinocchio_wrapper.model.names[2:] + if self.full_robot.is_floating_base_model + else self.pinocchio_wrapper.model.names[1:] + ) + for name in names: + j_id = self.pinocchio_wrapper.model.getJointId(name) + jmodel = self.pinocchio_wrapper.model.joints[j_id] + self.joint_to_dof_index[name] = jmodel.idx_q + + # Initialize joint limits + root_nq = 7 if self.full_robot.is_floating_base_model else 0 + self.lower_joint_limits = self.pinocchio_wrapper.model.lowerPositionLimit[root_nq:].copy() + self.upper_joint_limits = self.pinocchio_wrapper.model.upperPositionLimit[root_nq:].copy() + + # Update joint limits from supplemental info if available + if self.supplemental_info is not None: + if ( + hasattr(self.supplemental_info, "joint_limits") + and self.supplemental_info.joint_limits + ): + for joint_name, limits in self.supplemental_info.joint_limits.items(): + if joint_name in self.joint_to_dof_index: + idx = self.joint_to_dof_index[joint_name] - root_nq + self.lower_joint_limits[idx] = limits[0] + self.upper_joint_limits[idx] = limits[1] + + # Get full indices for body and hand actuated joints + full_body_indices = full_robot_model.get_body_actuated_joint_indices() + full_hand_indices = full_robot_model.get_hand_actuated_joint_indices("both") + full_left_hand_indices = full_robot_model.get_hand_actuated_joint_indices("left") + full_right_hand_indices = full_robot_model.get_hand_actuated_joint_indices("right") + + # Map to reduced indices + self._body_actuated_joint_indices = [] + for idx in full_body_indices: + if idx in self.full_to_reduced: + self._body_actuated_joint_indices.append(self.full_to_reduced[idx]) + + self._hand_actuated_joint_indices = [] + for idx in full_hand_indices: + if idx in self.full_to_reduced: + self._hand_actuated_joint_indices.append(self.full_to_reduced[idx]) + + self._left_hand_actuated_joint_indices = [] + for idx in full_left_hand_indices: + if idx in self.full_to_reduced: + self._left_hand_actuated_joint_indices.append(self.full_to_reduced[idx]) + + self._right_hand_actuated_joint_indices = [] + for idx in full_right_hand_indices: + if idx in self.full_to_reduced: + self._right_hand_actuated_joint_indices.append(self.full_to_reduced[idx]) + + # Cache indices for joint groups in reduced space + self._joint_group_indices = {} + for group_name in self.supplemental_info.joint_groups: + full_indices = full_robot_model.get_joint_group_indices(group_name) + reduced_indices = [] + for idx in full_indices: + if idx in self.full_to_reduced: + reduced_indices.append(self.full_to_reduced[idx]) + self._joint_group_indices[group_name] = sorted(set(reduced_indices)) + + # Initialize default body pose in reduced space + self.default_body_pose = self.full_to_reduced_configuration( + full_robot_model.default_body_pose + ) + + # Initialize initial body pose in reduced space + self.initial_body_pose = self.full_to_reduced_configuration( + full_robot_model.initial_body_pose + ) + + @property + def num_joints(self) -> int: + """Get the number of active joints in the reduced model.""" + return len(self.joint_names) + + @property + def joint_names(self) -> List[str]: + """Get the names of the active joints in the reduced model.""" + return [name for name in self.full_robot.joint_names if name not in self.fixed_joints] + + @classmethod + def from_fixed_groups( + cls, + full_robot_model: RobotModel, + fixed_group_names: List[str], + fixed_values: Optional[List[float]] = None, + ) -> "ReducedRobotModel": + """ + Create a reduced order robot model by fixing all joints in specified groups. + + :param full_robot_model: The original robot model + :param fixed_group_names: List of joint group names to fix + :param fixed_values: Optional list of values to fix the joints to. If None, uses the initial + joint positions (q0) from the full robot model. + :return: A ReducedRobotModel instance + """ + if full_robot_model.supplemental_info is None: + raise ValueError("supplemental_info must be provided to use this method") + + # Get all joints in the groups, including those from subgroups + fixed_joints = set() # Use a set to avoid duplicates + + for group_name in fixed_group_names: + if group_name not in full_robot_model.supplemental_info.joint_groups: + raise ValueError(f"Unknown joint group: {group_name}") + + group_info = full_robot_model.supplemental_info.joint_groups[group_name] + + # Add direct joints + fixed_joints.update(group_info["joints"]) + + # Add joints from subgroups + for subgroup_name in group_info["groups"]: + subgroup_joints = full_robot_model.get_joint_group_indices(subgroup_name) + fixed_joints.update([full_robot_model.joint_names[idx] for idx in subgroup_joints]) + + # Convert set back to list for compatibility with the original constructor + return cls(full_robot_model, list(fixed_joints), fixed_values) + + @classmethod + def from_fixed_group( + cls, + full_robot_model: RobotModel, + fixed_group_name: str, + fixed_values: Optional[List[float]] = None, + ) -> "ReducedRobotModel": + """ + Create a reduced order robot model by fixing all joints in a specified group. + This is a convenience method that calls from_fixed_groups with a single group. + + :param full_robot_model: The original robot model + :param fixed_group_name: Name of the joint group to fix + :param fixed_values: Optional list of values to fix the joints to. If None, uses the initial + joint positions (q0) from the full robot model. + :return: A ReducedRobotModel instance + """ + return cls.from_fixed_groups(full_robot_model, [fixed_group_name], fixed_values) + + @classmethod + def from_active_group( + cls, + full_robot_model: RobotModel, + active_group_name: str, + fixed_values: Optional[List[float]] = None, + ) -> "ReducedRobotModel": + """ + Create a reduced order robot model by fixing all joints EXCEPT those in the specified group. + This is a convenience method that calls from_active_groups with a single group. + + :param full_robot_model: The original robot model + :param active_group_name: Name of the joint group to keep active (all other joints will be fixed) + :param fixed_values: Optional list of values to fix the joints to. If None, uses the initial + joint positions (q0) from the full robot model. + :return: A ReducedRobotModel instance + """ + return cls.from_active_groups(full_robot_model, [active_group_name], fixed_values) + + @classmethod + def from_active_groups( + cls, + full_robot_model: RobotModel, + active_group_names: List[str], + fixed_values: Optional[List[float]] = None, + ) -> "ReducedRobotModel": + """ + Create a reduced order robot model by fixing all joints EXCEPT those in the specified groups. + This is useful when you want to keep multiple groups active and fix everything else. + + :param full_robot_model: The original robot model + :param active_group_names: List of joint group names to keep active (all other joints will be fixed) + :param fixed_values: Optional list of values to fix the joints to. If None, uses the initial + joint positions (q0) from the full robot model. + :return: A ReducedRobotModel instance + """ + if full_robot_model.supplemental_info is None: + raise ValueError("supplemental_info must be provided to use this method") + + # Get all joints in the active groups, including those from subgroups + active_joints = set() + + def add_group_joints(group_name: str): + if group_name not in full_robot_model.supplemental_info.joint_groups: + raise ValueError(f"Unknown joint group: {group_name}") + + group_info = full_robot_model.supplemental_info.joint_groups[group_name] + + # Add direct joints + if "joints" in group_info: + active_joints.update(group_info["joints"]) + + # Add joints from subgroups + if "groups" in group_info: + for subgroup_name in group_info["groups"]: + add_group_joints(subgroup_name) + + for group_name in active_group_names: + add_group_joints(group_name) + + # Get all joints from the model + all_joints = set(full_robot_model.joint_names) + + # The fixed joints are all joints minus the active joints + fixed_joints = list(all_joints - active_joints) + + return cls(full_robot_model, fixed_joints, fixed_values) + + def reduced_to_full_configuration(self, q_reduced: np.ndarray) -> np.ndarray: + """ + Convert a reduced configuration to the full configuration space. + + :param q_reduced: Configuration in reduced space + :return: Configuration in full space with fixed joints set to their fixed values + """ + if q_reduced.shape[0] != self.num_dofs: + raise ValueError( + f"Expected q_reduced of length {self.num_dofs}, got {q_reduced.shape[0]} instead" + ) + + q_full = np.zeros(self.full_robot.num_dofs) + + # Set active joints + for reduced_idx, full_idx in enumerate(self.reduced_to_full): + q_full[full_idx] = q_reduced[reduced_idx] + + # Set fixed joints + for joint_name, value in zip(self.fixed_joints, self.fixed_values): + full_idx = self.full_robot.dof_index(joint_name) + q_full[full_idx] = value + + return q_full + + def full_to_reduced_configuration(self, q_full: np.ndarray) -> np.ndarray: + """ + Convert a full configuration to the reduced configuration space. + + :param q_full: Configuration in full space + :return: Configuration in reduced space + """ + if q_full.shape[0] != self.full_robot.num_dofs: + raise ValueError( + f"Expected q_full of length {self.full_robot.num_dofs}, got {q_full.shape[0]} instead" + ) + + q_reduced = np.zeros(self.num_dofs) + + # Copy active joints + for reduced_idx, full_idx in enumerate(self.reduced_to_full): + q_reduced[reduced_idx] = q_full[full_idx] + + return q_reduced + + def cache_forward_kinematics(self, q_reduced: np.ndarray, auto_clip=True) -> None: + """ + Perform forward kinematics using the reduced configuration. + + :param q_reduced: Configuration in reduced space + """ + # First update the full robot's forward kinematics + q_full = self.reduced_to_full_configuration(q_reduced) + self.full_robot.cache_forward_kinematics(q_full, auto_clip) + + # Then update the reduced model's forward kinematics + pin.framesForwardKinematics( + self.pinocchio_wrapper.model, self.pinocchio_wrapper.data, q_reduced + ) + + def clip_configuration(self, q_reduced: np.ndarray, margin: float = 1e-6) -> np.ndarray: + """ + Clip the reduced configuration to stay within joint limits with a small tolerance. + + :param q_reduced: Configuration to clip + :param margin: Tolerance to keep away from joint limits + :return: Clipped configuration + """ + q_full = self.reduced_to_full_configuration(q_reduced) + q_full_clipped = self.full_robot.clip_configuration(q_full, margin) + return self.full_to_reduced_configuration(q_full_clipped) + + def reset_forward_kinematics(self): + """ + Reset the forward kinematics to the initial configuration. + """ + # Reset full robot's forward kinematics + self.full_robot.reset_forward_kinematics() + # Reset reduced model's forward kinematics + self.cache_forward_kinematics(self.q_zero) diff --git a/gr00t_wbc/control/robot_model/supplemental_info/__init__.py b/gr00t_wbc/control/robot_model/supplemental_info/__init__.py new file mode 100644 index 0000000..b9c987d --- /dev/null +++ b/gr00t_wbc/control/robot_model/supplemental_info/__init__.py @@ -0,0 +1,5 @@ +from gr00t_wbc.control.robot_model.supplemental_info.robot_supplemental_info import ( + RobotSupplementalInfo, +) + +__all__ = ["RobotSupplementalInfo"] diff --git a/gr00t_wbc/control/robot_model/supplemental_info/g1/__init__.py b/gr00t_wbc/control/robot_model/supplemental_info/g1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gr00t_wbc/control/robot_model/supplemental_info/g1/g1_supplemental_info.py b/gr00t_wbc/control/robot_model/supplemental_info/g1/g1_supplemental_info.py new file mode 100644 index 0000000..a68b4cd --- /dev/null +++ b/gr00t_wbc/control/robot_model/supplemental_info/g1/g1_supplemental_info.py @@ -0,0 +1,330 @@ +from dataclasses import dataclass +from enum import Enum + +import numpy as np + +from gr00t_wbc.control.robot_model.supplemental_info.robot_supplemental_info import ( + RobotSupplementalInfo, +) + + +class WaistLocation(Enum): + """Enum for waist location configuration.""" + + LOWER_BODY = "lower_body" + UPPER_BODY = "upper_body" + LOWER_AND_UPPER_BODY = "lower_and_upper_body" + + +class ElbowPose(Enum): + """Enum for elbow pose configuration.""" + + LOW = "low" + HIGH = "high" + + +@dataclass +class G1SupplementalInfo(RobotSupplementalInfo): + """ + Supplemental information for the G1 robot. + + Args: + waist_location: Where to place waist joints in the joint groups + elbow_pose: Which elbow pose configuration to use for default joint positions + """ + + def __init__( + self, + waist_location: WaistLocation = WaistLocation.LOWER_BODY, + elbow_pose: ElbowPose = ElbowPose.LOW, + ): + name = "G1_G1ThreeFinger" + + # Define all actuated joints + body_actuated_joints = [ + # Left leg + "left_hip_pitch_joint", + "left_hip_roll_joint", + "left_hip_yaw_joint", + "left_knee_joint", + "left_ankle_pitch_joint", + "left_ankle_roll_joint", + # Right leg + "right_hip_pitch_joint", + "right_hip_roll_joint", + "right_hip_yaw_joint", + "right_knee_joint", + "right_ankle_pitch_joint", + "right_ankle_roll_joint", + # Waist + "waist_yaw_joint", + "waist_roll_joint", + "waist_pitch_joint", + # Left arm + "left_shoulder_pitch_joint", + "left_shoulder_roll_joint", + "left_shoulder_yaw_joint", + "left_elbow_joint", + "left_wrist_roll_joint", + "left_wrist_pitch_joint", + "left_wrist_yaw_joint", + # Right arm + "right_shoulder_pitch_joint", + "right_shoulder_roll_joint", + "right_shoulder_yaw_joint", + "right_elbow_joint", + "right_wrist_roll_joint", + "right_wrist_pitch_joint", + "right_wrist_yaw_joint", + ] + + left_hand_actuated_joints = [ + # Left hand + "left_hand_thumb_0_joint", + "left_hand_thumb_1_joint", + "left_hand_thumb_2_joint", + "left_hand_index_0_joint", + "left_hand_index_1_joint", + "left_hand_middle_0_joint", + "left_hand_middle_1_joint", + ] + + right_hand_actuated_joints = [ + # Right hand + "right_hand_thumb_0_joint", + "right_hand_thumb_1_joint", + "right_hand_thumb_2_joint", + "right_hand_index_0_joint", + "right_hand_index_1_joint", + "right_hand_middle_0_joint", + "right_hand_middle_1_joint", + ] + + # Define joint limits from URDF + joint_limits = { + # Left leg + "left_hip_pitch_joint": [-2.5307, 2.8798], + "left_hip_roll_joint": [-0.5236, 2.9671], + "left_hip_yaw_joint": [-2.7576, 2.7576], + "left_knee_joint": [-0.087267, 2.8798], + "left_ankle_pitch_joint": [-0.87267, 0.5236], + "left_ankle_roll_joint": [-0.2618, 0.2618], + # Right leg + "right_hip_pitch_joint": [-2.5307, 2.8798], + "right_hip_roll_joint": [-2.9671, 0.5236], + "right_hip_yaw_joint": [-2.7576, 2.7576], + "right_knee_joint": [-0.087267, 2.8798], + "right_ankle_pitch_joint": [-0.87267, 0.5236], + "right_ankle_roll_joint": [-0.2618, 0.2618], + # Waist + "waist_yaw_joint": [-2.618, 2.618], + "waist_roll_joint": [-0.52, 0.52], + "waist_pitch_joint": [-0.52, 0.52], + # Left arm + "left_shoulder_pitch_joint": [-3.0892, 2.6704], + "left_shoulder_roll_joint": [0.19, 2.2515], + "left_shoulder_yaw_joint": [-2.618, 2.618], + "left_elbow_joint": [-1.0472, 2.0944], + "left_wrist_roll_joint": [-1.972222054, 1.972222054], + "left_wrist_pitch_joint": [-1.614429558, 1.614429558], + "left_wrist_yaw_joint": [-1.614429558, 1.614429558], + # Right arm + "right_shoulder_pitch_joint": [-3.0892, 2.6704], + "right_shoulder_roll_joint": [-2.2515, -0.19], + "right_shoulder_yaw_joint": [-2.618, 2.618], + "right_elbow_joint": [-1.0472, 2.0944], + "right_wrist_roll_joint": [-1.972222054, 1.972222054], + "right_wrist_pitch_joint": [-1.614429558, 1.614429558], + "right_wrist_yaw_joint": [-1.614429558, 1.614429558], + # Left hand + "left_hand_thumb_0_joint": [-1.04719755, 1.04719755], + "left_hand_thumb_1_joint": [-0.72431163, 1.04719755], + "left_hand_thumb_2_joint": [0, 1.74532925], + "left_hand_index_0_joint": [-1.57079632, 0], + "left_hand_index_1_joint": [-1.74532925, 0], + "left_hand_middle_0_joint": [-1.57079632, 0], + "left_hand_middle_1_joint": [-1.74532925, 0], + # Right hand + "right_hand_thumb_0_joint": [-1.04719755, 1.04719755], + "right_hand_thumb_1_joint": [-0.72431163, 1.04719755], + "right_hand_thumb_2_joint": [0, 1.74532925], + "right_hand_index_0_joint": [-1.57079632, 0], + "right_hand_index_1_joint": [-1.74532925, 0], + "right_hand_middle_0_joint": [-1.57079632, 0], + "right_hand_middle_1_joint": [-1.74532925, 0], + } + + # Define joint groups + joint_groups = { + # Body groups + "waist": { + "joints": ["waist_yaw_joint", "waist_roll_joint", "waist_pitch_joint"], + "groups": [], + }, + # Leg groups + "left_leg": { + "joints": [ + "left_hip_pitch_joint", + "left_hip_roll_joint", + "left_hip_yaw_joint", + "left_knee_joint", + "left_ankle_pitch_joint", + "left_ankle_roll_joint", + ], + "groups": [], + }, + "right_leg": { + "joints": [ + "right_hip_pitch_joint", + "right_hip_roll_joint", + "right_hip_yaw_joint", + "right_knee_joint", + "right_ankle_pitch_joint", + "right_ankle_roll_joint", + ], + "groups": [], + }, + "legs": {"joints": [], "groups": ["left_leg", "right_leg"]}, + # Arm groups + "left_arm": { + "joints": [ + "left_shoulder_pitch_joint", + "left_shoulder_roll_joint", + "left_shoulder_yaw_joint", + "left_elbow_joint", + "left_wrist_roll_joint", + "left_wrist_pitch_joint", + "left_wrist_yaw_joint", + ], + "groups": [], + }, + "right_arm": { + "joints": [ + "right_shoulder_pitch_joint", + "right_shoulder_roll_joint", + "right_shoulder_yaw_joint", + "right_elbow_joint", + "right_wrist_roll_joint", + "right_wrist_pitch_joint", + "right_wrist_yaw_joint", + ], + "groups": [], + }, + "arms": {"joints": [], "groups": ["left_arm", "right_arm"]}, + # Hand groups + "left_hand": { + "joints": [ + "left_hand_index_0_joint", + "left_hand_index_1_joint", + "left_hand_middle_0_joint", + "left_hand_middle_1_joint", + "left_hand_thumb_0_joint", + "left_hand_thumb_1_joint", + "left_hand_thumb_2_joint", + ], + "groups": [], + }, + "right_hand": { + "joints": [ + "right_hand_index_0_joint", + "right_hand_index_1_joint", + "right_hand_middle_0_joint", + "right_hand_middle_1_joint", + "right_hand_thumb_0_joint", + "right_hand_thumb_1_joint", + "right_hand_thumb_2_joint", + ], + "groups": [], + }, + "hands": {"joints": [], "groups": ["left_hand", "right_hand"]}, + # Full body groups + "lower_body": {"joints": [], "groups": ["waist", "legs"]}, + "upper_body_no_hands": {"joints": [], "groups": ["arms"]}, + "body": {"joints": [], "groups": ["lower_body", "upper_body_no_hands"]}, + "upper_body": {"joints": [], "groups": ["upper_body_no_hands", "hands"]}, + } + + # Define joint name mapping from generic types to robot-specific names + joint_name_mapping = { + # Waist joints + "waist_pitch": "waist_pitch_joint", + "waist_roll": "waist_roll_joint", + "waist_yaw": "waist_yaw_joint", + # Shoulder joints + "shoulder_pitch": { + "left": "left_shoulder_pitch_joint", + "right": "right_shoulder_pitch_joint", + }, + "shoulder_roll": { + "left": "left_shoulder_roll_joint", + "right": "right_shoulder_roll_joint", + }, + "shoulder_yaw": { + "left": "left_shoulder_yaw_joint", + "right": "right_shoulder_yaw_joint", + }, + # Elbow joints + "elbow_pitch": {"left": "left_elbow_joint", "right": "right_elbow_joint"}, + # Wrist joints + "wrist_pitch": {"left": "left_wrist_pitch_joint", "right": "right_wrist_pitch_joint"}, + "wrist_roll": {"left": "left_wrist_roll_joint", "right": "right_wrist_roll_joint"}, + "wrist_yaw": {"left": "left_wrist_yaw_joint", "right": "right_wrist_yaw_joint"}, + } + + root_frame_name = "pelvis" + + hand_frame_names = {"left": "left_wrist_yaw_link", "right": "right_wrist_yaw_link"} + + elbow_calibration_joint_angles = {"left": 0.0, "right": 0.0} + + hand_rotation_correction = np.array([[0, 0, 1], [0, 1, 0], [-1, 0, 0]]) + + # Configure default joint positions based on elbow pose + if elbow_pose == ElbowPose.HIGH: + default_joint_q = { + "shoulder_roll": {"left": 0.5, "right": -0.5}, + "shoulder_pitch": {"left": -0.2, "right": -0.2}, + "shoulder_yaw": {"left": -0.5, "right": 0.5}, + "wrist_roll": {"left": -0.5, "right": 0.5}, + "wrist_yaw": {"left": 0.5, "right": -0.5}, + "wrist_pitch": {"left": -0.2, "right": -0.2}, + } + else: # ElbowPose.LOW + default_joint_q = { + "shoulder_roll": {"left": 0.2, "right": -0.2}, + } + + teleop_upper_body_motion_scale = 1.0 + + # Configure joint groups based on waist location + modified_joint_groups = joint_groups.copy() + if waist_location == WaistLocation.UPPER_BODY: + # Move waist from lower_body to upper_body_no_hands + modified_joint_groups["lower_body"] = {"joints": [], "groups": ["legs"]} + modified_joint_groups["upper_body_no_hands"] = { + "joints": [], + "groups": ["arms", "waist"], + } + elif waist_location == WaistLocation.LOWER_AND_UPPER_BODY: + # Add waist to upper_body_no_hands while keeping it in lower_body + modified_joint_groups["upper_body_no_hands"] = { + "joints": [], + "groups": ["arms", "waist"], + } + # For LOWER_BODY, keep default joint_groups as is + + super().__init__( + name=name, + body_actuated_joints=body_actuated_joints, + left_hand_actuated_joints=left_hand_actuated_joints, + right_hand_actuated_joints=right_hand_actuated_joints, + joint_limits=joint_limits, + joint_groups=modified_joint_groups, + root_frame_name=root_frame_name, + hand_frame_names=hand_frame_names, + elbow_calibration_joint_angles=elbow_calibration_joint_angles, + joint_name_mapping=joint_name_mapping, + hand_rotation_correction=hand_rotation_correction, + default_joint_q=default_joint_q, + teleop_upper_body_motion_scale=teleop_upper_body_motion_scale, + ) diff --git a/gr00t_wbc/control/robot_model/supplemental_info/robot_supplemental_info.py b/gr00t_wbc/control/robot_model/supplemental_info/robot_supplemental_info.py new file mode 100644 index 0000000..d4d66cf --- /dev/null +++ b/gr00t_wbc/control/robot_model/supplemental_info/robot_supplemental_info.py @@ -0,0 +1,91 @@ +from dataclasses import dataclass +from typing import Dict, List, Union + +import numpy as np + + +@dataclass +class RobotSupplementalInfo: + """ + Base class for robot-specific information that is not easily extractable from URDF. + This includes information about actuated joints, joint hierarchies, etc. + """ + + name: str + + # List of body actuated joint names (excluding hands) + body_actuated_joints: List[str] + + # List of left hand actuated joint names + left_hand_actuated_joints: List[str] + + # List of right hand actuated joint names + right_hand_actuated_joints: List[str] + + # Dictionary of joint groups, where each group is a dictionary with: + # - "joints": list of joint names + # - "groups": list of subgroup names (optional) + # Example: { + # "right_arm": { + # "joints": ["right_shoulder_pitch_joint", "right_shoulder_roll_joint", "right_elbow_joint"], + # "groups": [] + # }, + # "left_arm": { + # "joints": ["left_shoulder_pitch_joint", "left_shoulder_roll_joint", "left_elbow_joint"], + # "groups": [] + # }, + # "upper_body": { + # "joints": ["torso_pitch_joint", "torso_yaw_joint", "torso_roll_joint"], + # "groups": ["right_arm", "left_arm"] + # } + # } + joint_groups: Dict[str, Dict[str, List[str]]] + + # Name of the root frame + root_frame_name: str + + # Dictionary of hand frame names + # Example: { + # "left": "left_hand_frame", + # "right": "right_hand_frame" + # } + hand_frame_names: Dict[str, str] + + # Dictionary of joint limits + # Example: { + # "left_shoulder_pitch_joint": [-np.pi / 2, np.pi / 2], + # "right_shoulder_pitch_joint": [-np.pi / 2, np.pi / 2] + # } + joint_limits: Dict[str, List[float]] + + # Dictionary of elbow calibration joint angles + # Example: { + # "left": -90.0, + # "right": -90.0 + # } + elbow_calibration_joint_angles: Dict[str, float] + + # Dictionary of joint name mapping from generic types to robot-specific names + # Example: { + # "waist_pitch": "waist_pitch_joint", + # "shoulder_pitch": { + # "left": "left_shoulder_pitch_joint", + # "right": "right_shoulder_pitch_joint" + # }, + # "elbow_pitch": { + # "left": "left_elbow_pitch_joint", + # "right": "right_elbow_pitch_joint" + # } + # } + joint_name_mapping: Dict[str, Union[str, Dict[str, str]]] + + # Maps from generic joint names to robot-specific joint values + # Example: { + # "waist_roll": 0.2, + # "elbow_pitch": {"left": 1.0, "right": 1.0} + # } + default_joint_q: Dict[str, Union[float, Dict[str, float]]] + + hand_rotation_correction: np.ndarray + + teleop_upper_body_motion_scale: float diff --git a/gr00t_wbc/control/sensor/__init__.py b/gr00t_wbc/control/sensor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gr00t_wbc/control/sensor/composed_camera.py b/gr00t_wbc/control/sensor/composed_camera.py new file mode 100644 index 0000000..042a9f5 --- /dev/null +++ b/gr00t_wbc/control/sensor/composed_camera.py @@ -0,0 +1,440 @@ +from collections import deque +from dataclasses import dataclass +import queue +import threading +import time +from typing import Any, Dict, Optional + +# we need to import these first in this order to avoid TSL segmentation fault +# caused by zed and oak libraries +try: + import cv2 # noqa + import depthai as dai # noqa + import pyzed.sl as sl # noqa +except ImportError: + print( + """ + Some of the camera specific dependencies are not installed. If you are + not running this on the robot, having these libraries is optional. + """ + ) + +import numpy as np # noqa + +from gr00t_wbc.control.base.sensor import Sensor +from gr00t_wbc.control.sensor.sensor_server import ( + ImageMessageSchema, + SensorClient, + SensorServer, + CameraMountPosition, +) + + +def read_qr_code(data): + current_time = time.monotonic() + detector = cv2.QRCodeDetector() + for key, img in data["images"].items(): + decoded_time, bbox, _ = detector.detectAndDecode(img) + if bbox is not None and decoded_time: + print(f"{key} latency: {(current_time - float(decoded_time)) * 1e3:.1f} ms") + else: + print(f"{key} QR code not detected.") + + +@dataclass +class ComposedCameraConfig: + """Camera configuration for composed camera""" + + ego_view_camera: Optional[str] = "oak" + """Camera type for ego view: oak, realsense, zed, or None""" + + ego_view_device_id: Optional[str] = None + """Device ID for ego view camera (optional, used for OAK cameras)""" + + head_camera: Optional[str] = None + """Camera type for head view: oak, oak_mono, realsense, zed or None""" + + head_device_id: Optional[str] = None + """Device ID for head camera (optional, used for OAK cameras)""" + + left_wrist_camera: Optional[str] = None + """Camera type for left wrist view: oak, realsense, zed or None""" + + left_wrist_device_id: Optional[str] = None + """Device ID for left wrist camera (optional, used for OAK cameras)""" + + right_wrist_camera: Optional[str] = None + """Camera type for right wrist view: oak, realsense, zed or None""" + + right_wrist_device_id: Optional[str] = None + """Device ID for right wrist camera (optional, used for OAK cameras)""" + + fps: int = 30 + """Rate at which the composed camera will publish the images. Since composed camera + can read from multiple cameras, it will publish all the images. + Note that OAK can only run at 30 FPS. 20 FPS will cause large latency. + """ + + # Server configuration + run_as_server: bool = True + """Whether to run as server or client""" + + server: bool = True + """Whether to run the camera as a server""" + + port: int = 5555 + """Port number for server/client communication""" + + test_latency: bool = False + """Whether to test latency""" + + # Queue configuration + queue_size: int = 3 + """Size of each camera's image queue""" + + def __post_init__(self): + # runyu: Note that this is a hack to make the config work with G1 camera server in orin + # we should not use this hack in the future + self.run_as_server: bool = self.server + + +class ComposedCameraSensor(Sensor, SensorServer): + + def __init__(self, config: ComposedCameraConfig): + self.config = config + self.camera_queues: Dict[str, queue.Queue] = {} + self.camera_threads: Dict[str, threading.Thread] = {} + self.shutdown_events: Dict[str, threading.Event] = {} + self.error_events: Dict[str, threading.Event] = {} + self.error_messages: Dict[str, str] = {} + self._observation_spaces: Dict[str, Any] = {} + + camera_configs = self._get_camera_configs() + + # Then create worker threads + for mount_position, camera_config in camera_configs.items(): + # Create queue and shutdown event for this camera + camera_queue = queue.Queue(maxsize=config.queue_size) + shutdown_event = threading.Event() + error_event = threading.Event() + + self.camera_queues[mount_position] = camera_queue + self.shutdown_events[mount_position] = shutdown_event + self.error_events[mount_position] = error_event + + # Start camera thread + thread = threading.Thread( + target=self._camera_worker_wrapper, + args=( + mount_position, + camera_config["camera_type"], + camera_config["device_id"], + camera_queue, + shutdown_event, + error_event, + ), + ) + thread.start() + self.camera_threads[mount_position] = thread + + if config.run_as_server: + self.start_server(config.port) + + def _get_camera_configs(self) -> Dict[str, str]: + """Get camera configurations as mount_position -> camera_type mapping""" + camera_configs = {} + + if self.config.ego_view_camera is not None: + camera_configs[CameraMountPosition.EGO_VIEW.value] = { + "camera_type": self.config.ego_view_camera, + "device_id": self.config.ego_view_device_id, + } + + if self.config.head_camera is not None: + camera_configs[CameraMountPosition.HEAD.value] = { + "camera_type": self.config.head_camera, + "device_id": self.config.head_device_id, + } + + if self.config.left_wrist_camera is not None: + camera_configs[CameraMountPosition.LEFT_WRIST.value] = { + "camera_type": self.config.left_wrist_camera, + "device_id": self.config.left_wrist_device_id, + } + + if self.config.right_wrist_camera is not None: + camera_configs[CameraMountPosition.RIGHT_WRIST.value] = { + "camera_type": self.config.right_wrist_camera, + "device_id": self.config.right_wrist_device_id, + } + + return camera_configs + + def _camera_worker_wrapper( + self, + mount_position: str, + camera_type: str, + device_id: Optional[str], + image_queue: queue.Queue, + shutdown_event: threading.Event, + error_event: threading.Event, + ): + """Worker thread that continuously captures from a single camera""" + try: + camera = self._instantiate_camera(mount_position, camera_type, device_id) + self._observation_spaces[mount_position] = camera.observation_space() + + consecutive_failures = 0 + max_consecutive_failures = 5 + + while not shutdown_event.is_set(): + frame = camera.read() + if frame: + consecutive_failures = 0 # Reset on successful read + # Non-blocking queue put with frame dropping + try: + image_queue.put_nowait(frame) + except queue.Full: + # Remove oldest frame and add new one + try: + image_queue.get_nowait() + image_queue.put_nowait(frame) + except queue.Empty: + pass + else: + consecutive_failures += 1 + if consecutive_failures >= max_consecutive_failures: + error_msg = ( + f"Camera {mount_position} ({camera_type}) dropped: " + f"failed to read {consecutive_failures} consecutive frames" + ) + print(f"[ERROR] {error_msg}") + self.error_messages[mount_position] = error_msg + error_event.set() + break + + camera.close() + + except Exception as e: + error_msg = f"Camera {mount_position} ({camera_type}) error: {str(e)}" + print(f"[ERROR] {error_msg}") + self.error_messages[mount_position] = error_msg + error_event.set() + + def _instantiate_camera( + self, mount_position: str, camera_type: str, device_id: Optional[str] = None + ) -> Sensor: + """ + Instantiate a camera sensor based on the camera type. + + Args: + camera_type: Type of camera ("oak", "oak_mono", "realsense", "zed") + device_id: Optional device ID for the camera (used for OAK cameras) + + Returns: + Sensor instance for the specified camera type + """ + if camera_type in ("oak", "oak_mono"): + from gr00t_wbc.control.sensor.oak import OAKConfig, OAKSensor + + oak_config = OAKConfig() + if camera_type == "oak_mono": + oak_config.enable_mono_cameras = True + print("Initializing OAK sensor for camera type: ", camera_type) + return OAKSensor(config=oak_config, mount_position=mount_position, device_id=device_id) + elif camera_type == "realsense": + from gr00t_wbc.control.sensor.realsense import RealSenseSensor + + print("Initializing RealSense sensor for camera type: ", camera_type) + return RealSenseSensor(mount_position=mount_position) + elif camera_type == "zed": + from gr00t_wbc.control.sensor.zed import ZEDSensor + + print("Initializing ZED sensor for camera type: ", camera_type) + return ZEDSensor(mount_position=mount_position) + elif camera_type.endswith(".mp4"): + from gr00t_wbc.control.sensor.dummy import ReplayDummySensor + + print("Initializing Replay Dummy Sensor for camera type: ", camera_type) + return ReplayDummySensor(video_path=camera_type) + else: + raise ValueError(f"Unsupported camera type: {camera_type}") + + def _check_for_errors(self): + """Check if any camera thread has encountered an error and raise exception if so.""" + for mount_position, error_event in self.error_events.items(): + if error_event.is_set(): + error_msg = self.error_messages.get( + mount_position, f"Camera {mount_position} encountered an unknown error" + ) + raise RuntimeError(error_msg) + + def read(self): + """Read frames from all cameras.""" + # Check for errors from camera threads + self._check_for_errors() + + message = {} + for mount_position, camera_queue in self.camera_queues.items(): + frame = self._get_latest_from_queue(camera_queue) + if frame is not None: + message[mount_position] = frame + return message + + def _get_latest_from_queue(self, camera_queue: queue.Queue) -> Optional[Dict[str, Any]]: + """Get most recent frame, discard older ones""" + latest = None + try: + while True: + latest = camera_queue.get_nowait() + except queue.Empty: + pass + return latest + + def close(self): + """Close all cameras.""" + # Signal all worker threads to shutdown + for shutdown_event in self.shutdown_events.values(): + shutdown_event.set() + + # Wait for all threads to finish + for thread in self.camera_threads.values(): + thread.join(timeout=5.0) + + # Clear queues + for camera_queue in self.camera_queues.values(): + try: + while True: + camera_queue.get_nowait() + except queue.Empty: + pass + + # Stop server if running + if self.config.run_as_server: + self.stop_server() + + def serialize_message(self, message: Dict[str, Any]) -> Dict[str, Any]: + """Merge all camera data into a single ImageMessageSchema.""" + all_timestamps = {} + all_images = {} + + for _, camera_data in message.items(): + all_timestamps.update(camera_data.get("timestamps", {})) + all_images.update(camera_data.get("images", {})) + + # Create a single ImageMessageSchema with all data + img_schema = ImageMessageSchema(timestamps=all_timestamps, images=all_images) + return img_schema.serialize() + + def run_server(self): + """Run the server.""" + idx = 0 + server_start_time = time.monotonic() + fps_print_time = time.monotonic() + frame_interval = 1.0 / self.config.fps + + while True: + # Calculate when this frame should ideally complete + target_time = server_start_time + (idx + 1) * frame_interval + + message = self.read() + if message: + if self.config.test_latency: + read_qr_code(message) + + serialized_message = self.serialize_message(message) + self.send_message(serialized_message) + idx += 1 + + if idx % 10 == 0: + print(f"Image sending FPS: {10 / (time.monotonic() - fps_print_time):.2f}") + fps_print_time = time.monotonic() + + # Sleep to maintain precise timing + current_time = time.monotonic() + sleep_time = target_time - current_time + if sleep_time > 0: + time.sleep(sleep_time) + else: + # If we're behind, increment idx to stay on schedule + if not message: + idx += 1 + + def observation_space(self): + """Return the observation space.""" + import gymnasium as gym + + return gym.spaces.Dict(self._observation_spaces) + + +class ComposedCameraClientSensor(Sensor, SensorClient): + """Class that serves as client for multiple different cameras.""" + + def __init__(self, server_ip: str = "localhost", port: int = 5555): + self.start_client(server_ip, port) + + # Initialize tracking variables + self._latest_message = {} + self._avg_time_per_frame = deque(maxlen=20) + self._msg_received_time = 0 + self._start_time = 0.0 # Initialize _start_time + self.idx = 0 + + print("Initialized composed camera client sensor") + + def read(self, **kwargs) -> Optional[Dict[str, Any]]: + self._start_time = time.time() + message = self.receive_message() + if not message: + return None + self.idx += 1 + + self._latest_message = ImageMessageSchema.deserialize(message).asdict() + + # if self.idx % 10 == 0: + # for image_key, image_time in self._latest_message["timestamps"].items(): + # image_latency = (time.time() - image_time) * 1000 + # print(f"Image latency for {image_key}: {image_latency:.2f} ms") + + self._msg_received_time = time.time() + self._avg_time_per_frame.append(self._msg_received_time - self._start_time) + + return self._latest_message + + def close(self): + """Close the client connection.""" + self.stop_client() + + def fps(self) -> float: + """Get the current FPS of the client.""" + if len(self._avg_time_per_frame) == 0: + return 0.0 + return float(1 / np.mean(self._avg_time_per_frame)) + + +if __name__ == "__main__": + """Test function for ComposedCamera.""" + import tyro + + config = tyro.cli(ComposedCameraConfig) + + if config.run_as_server: + composed_camera = ComposedCameraSensor(config) + print("Running composed camera server...") + composed_camera.run_server() + + else: + # Client mode + composed_client = ComposedCameraClientSensor(server_ip="localhost", port=config.port) + + try: + while True: + data = composed_client.read() + if data is not None: + print(f"FPS: {composed_client.fps():.2f}") + if "timestamp" in data: + print(f"Timestamp: {data['timestamp']}") + time.sleep(0.1) + except KeyboardInterrupt: + print("Stopping client...") + composed_client.close() diff --git a/gr00t_wbc/control/sensor/oak.py b/gr00t_wbc/control/sensor/oak.py new file mode 100644 index 0000000..f1913df --- /dev/null +++ b/gr00t_wbc/control/sensor/oak.py @@ -0,0 +1,324 @@ +import time +from typing import Any, Dict, Optional, Tuple + +import cv2 +import depthai as dai +import gymnasium as gym +import numpy as np + +from gr00t_wbc.control.base.sensor import Sensor +from gr00t_wbc.control.sensor.sensor_server import ( + CameraMountPosition, + ImageMessageSchema, + SensorServer, +) + + +class OAKConfig: + """Configuration for the OAK camera.""" + + color_image_dim: Tuple[int, int] = (640, 480) # RGB camera resolution + monochrome_image_dim: Tuple[int, int] = (640, 480) # Monochrome camera resolution + fps: int = 30 + enable_color: bool = True # Enable CAM_A (RGB) + enable_mono_cameras: bool = False # Enable CAM_B & CAM_C (Monochrome stereo pair) + mount_position: str = CameraMountPosition.EGO_VIEW.value + + +class OAKSensor(Sensor, SensorServer): + """Sensor for the OAK camera family.""" + + def __init__( + self, + run_as_server: bool = False, + port: int = 5555, + config: OAKConfig = OAKConfig(), + device_id: Optional[str] = None, + mount_position: str = CameraMountPosition.EGO_VIEW.value, + ): + """Initialize the OAK camera.""" + self.config = config + self.mount_position = mount_position + self._run_as_server = run_as_server + + device_infos = dai.Device.getAllAvailableDevices() + assert len(device_infos) > 0, f"No OAK devices found for {mount_position}" + print(f"Device infos: {device_infos}") + if device_id is not None: + device_found = False + for device_info in device_infos: + if device_info.getDeviceId() == device_id: + self.device = dai.Device(device_info) + device_found = True + break + if not device_found: + raise ValueError(f"Device with ID {device_id} not found") + else: + self.device = dai.Device() + + print(f"Connected to OAK device: {self.device.getDeviceName(), self.device.getDeviceId()}") + print(f"Device ID: {self.device.getDeviceId()}") + + sockets: list[dai.CameraBoardSocket] = self.device.getConnectedCameras() + print(f"Available cameras: {[str(s) for s in sockets]}") + + # Create pipeline (without context manager to persist across method calls) + self.pipeline = dai.Pipeline(self.device) + self.output_queues = {} + + # Configure RGB camera (CAM_A) + if config.enable_color and dai.CameraBoardSocket.CAM_A in sockets: + self.cam_rgb = self.pipeline.create(dai.node.Camera) + cam_socket = dai.CameraBoardSocket.CAM_A + self.cam_rgb = self.cam_rgb.build(cam_socket) + # Create RGB output queue + self.output_queues["color"] = self.cam_rgb.requestOutput( + config.color_image_dim, + fps=config.fps, + ).createOutputQueue() + print("Enabled CAM_A (RGB)") + + # Configure Monochrome cameras (CAM_B & CAM_C) + if config.enable_mono_cameras: + if dai.CameraBoardSocket.CAM_B in sockets: + self.cam_mono_left = self.pipeline.create(dai.node.Camera) + cam_socket = dai.CameraBoardSocket.CAM_B + self.cam_mono_left = self.cam_mono_left.build(cam_socket) + # Create mono left output queue + self.output_queues["mono_left"] = self.cam_mono_left.requestOutput( + config.monochrome_image_dim, + fps=config.fps, + ).createOutputQueue() + print("Enabled CAM_B (Monochrome Left)") + + if dai.CameraBoardSocket.CAM_C in sockets: + self.cam_mono_right = self.pipeline.create(dai.node.Camera) + cam_socket = dai.CameraBoardSocket.CAM_C + self.cam_mono_right = self.cam_mono_right.build(cam_socket) + # Create mono right output queue + self.output_queues["mono_right"] = self.cam_mono_right.requestOutput( + config.monochrome_image_dim, + fps=config.fps, + ).createOutputQueue() + print("Enabled CAM_C (Monochrome Right)") + + assert len(self.output_queues) > 0, "No output queues enabled" + # auto exposure compensation, for CoRL demo + # cam_q_in = self.cam_rgb.inputControl.createInputQueue() + # ctrl = dai.CameraControl() + # ctrl.setAutoExposureEnable() + # ctrl.setAutoExposureCompensation(-2) + # cam_q_in.send(ctrl) + + # Start pipeline on device + self.pipeline.start() + + if run_as_server: + self.start_server(port) + + def read(self) -> Optional[Dict[str, Any]]: + """Read images from the camera.""" + if not self.pipeline.isRunning(): + print(f"[ERROR] OAK pipeline stopped for {self.mount_position}") + return None + + # Check if device is still connected + if not self.device.isPipelineRunning(): + print(f"[ERROR] OAK device disconnected for {self.mount_position}") + return None + + timestamps = {} + images = {} + rgb_frame_time = None + + # Get color frame if enabled + if "color" in self.output_queues: + try: + rgb_frame = self.output_queues["color"].get() + rgb_frame_time = rgb_frame.getTimestamp() + if rgb_frame is not None: + images[self.mount_position] = rgb_frame.getCvFrame()[..., ::-1] # BGR to RGB + timestamps[self.mount_position] = ( + rgb_frame_time - dai.Clock.now() + ).total_seconds() + time.time() + except Exception as e: + print(f"[ERROR] Failed to read color frame from {self.mount_position}: {e}") + return None + + # Get mono frames if enabled + if "mono_left" in self.output_queues: + try: + mono_left_frame = self.output_queues["mono_left"].get() + mono_left_frame_time = mono_left_frame.getTimestamp() + if mono_left_frame is not None: + key = f"{self.mount_position}_left_mono" + images[key] = mono_left_frame.getCvFrame() + timestamps[key] = ( + mono_left_frame_time - dai.Clock.now() + ).total_seconds() + time.time() + except Exception as e: + print(f"[ERROR] Failed to read mono_left frame from {self.mount_position}: {e}") + return None + + if "mono_right" in self.output_queues: + try: + mono_right_frame = self.output_queues["mono_right"].get() + mono_right_frame_time = mono_right_frame.getTimestamp() + if mono_right_frame is not None: + key = f"{self.mount_position}_right_mono" + images[key] = mono_right_frame.getCvFrame() + timestamps[key] = ( + mono_right_frame_time - dai.Clock.now() + ).total_seconds() + time.time() + except Exception as e: + print(f"[ERROR] Failed to read mono_right frame from {self.mount_position}: {e}") + return None + + if ( + rgb_frame_time is not None + and (rgb_frame_time - dai.Clock.now()).total_seconds() <= -0.2 + ): + print( + f"[{self.mount_position}] OAK latency too large: " + f"{(dai.Clock.now() - rgb_frame_time).total_seconds() * 1000}ms" + ) + + return { + "timestamps": timestamps, + "images": images, + } + + def serialize(self, data: Dict[str, Any]) -> Dict[str, Any]: + """Serialize data using ImageMessageSchema.""" + serialized_msg = ImageMessageSchema(timestamps=data["timestamps"], images=data["images"]) + return serialized_msg.serialize() + + def observation_space(self) -> gym.Space: + spaces = {} + + if self.config.enable_color: + spaces["color_image"] = gym.spaces.Box( + low=0, + high=255, + shape=(self.config.color_image_dim[1], self.config.color_image_dim[0], 3), + dtype=np.uint8, + ) + + if self.config.enable_mono_cameras: + spaces["mono_left_image"] = gym.spaces.Box( + low=0, + high=255, + shape=(self.config.monochrome_image_dim[1], self.config.monochrome_image_dim[0]), + dtype=np.uint8, + ) + spaces["mono_right_image"] = gym.spaces.Box( + low=0, + high=255, + shape=(self.config.monochrome_image_dim[1], self.config.monochrome_image_dim[0]), + dtype=np.uint8, + ) + + return gym.spaces.Dict(spaces) + + def close(self): + """Close the camera connection.""" + if self._run_as_server: + self.stop_server() + if hasattr(self, "pipeline") and self.pipeline.isRunning(): + self.pipeline.stop() + self.device.close() + + def run_server(self): + """Run the server.""" + if not self._run_as_server: + raise ValueError("This function is only available when run_as_server is True") + + while True: + frame = self.read() + if frame is None: + continue + + msg = self.serialize(frame) + self.send_message({self.mount_position: msg}) + + def __del__(self): + self.close() + + +if __name__ == "__main__": + """Test function for OAK camera.""" + + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("--server", action="store_true", help="Run as server") + parser.add_argument("--client", action="store_true", help="Run as client") + parser.add_argument("--host", type=str, default="localhost", help="Server IP address") + parser.add_argument("--port", type=int, default=5555, help="Port number") + parser.add_argument("--device-id", type=str, default=None, help="Specific device ID") + parser.add_argument( + "--enable-mono", action="store_true", help="Enable monochrome cameras (CAM_B & CAM_C)" + ) + parser.add_argument("--mount-position", type=str, default="ego_view", help="Mount position") + parser.add_argument("--show-image", action="store_true", help="Display images") + args = parser.parse_args() + + oak_config = OAKConfig() + if args.enable_mono: + oak_config.enable_mono_cameras = True + + if args.server: + # Run as server + oak = OAKSensor( + run_as_server=True, + port=args.port, + config=oak_config, + device_id=args.device_id, + mount_position=args.mount_position, + ) + print(f"Starting OAK server on port {args.port}") + oak.run_server() + + else: + # Run standalone + oak = OAKSensor(run_as_server=False, config=oak_config, device_id=args.device_id) + print("Running OAK camera in standalone mode") + + while True: + frame = oak.read() + if frame is None: + print("Waiting for frame...") + time.sleep(0.5) + continue + + if "color_image" in frame: + print(f"Color image shape: {frame['color_image'].shape}") + if "mono_left_image" in frame: + print(f"Mono left image shape: {frame['mono_left_image'].shape}") + if "mono_right_image" in frame: + print(f"Mono right image shape: {frame['mono_right_image'].shape}") + if "depth_image" in frame: + print(f"Depth image shape: {frame['depth_image'].shape}") + + if args.show_image: + if "color_image" in frame: + cv2.imshow("Color Image", frame["color_image"]) + + if "mono_left_image" in frame: + cv2.imshow("Mono Left", frame["mono_left_image"]) + if "mono_right_image" in frame: + cv2.imshow("Mono Right", frame["mono_right_image"]) + + if "depth_image" in frame: + depth_colormap = cv2.applyColorMap( + cv2.convertScaleAbs(frame["depth_image"], alpha=0.03), cv2.COLORMAP_JET + ) + cv2.imshow("Depth Image", depth_colormap) + + if cv2.waitKey(1) == ord("q"): + break + + time.sleep(0.01) + + cv2.destroyAllWindows() + oak.close() diff --git a/gr00t_wbc/control/sensor/sensor_server.py b/gr00t_wbc/control/sensor/sensor_server.py new file mode 100644 index 0000000..2845337 --- /dev/null +++ b/gr00t_wbc/control/sensor/sensor_server.py @@ -0,0 +1,128 @@ +import base64 +from dataclasses import dataclass +from enum import Enum +from typing import Any, Dict + +import cv2 +import msgpack +import msgpack_numpy as m +import numpy as np +import zmq + + +@dataclass +class ImageMessageSchema: + """ + This is a standardized message schema for image data. + Any camera should use this schema to serialize (send to queue) and + deserialize (receive from queue) the image data. + + """ + + timestamps: Dict[str, float] + """Dictionary of timestamps, keyed by image identifier (e.g., {"ego_view": 123.45})""" + images: Dict[str, np.ndarray] + """Dictionary of images, keyed by image identifier (e.g., {"ego_view": array, "ego_view_left_mono": array})""" + + def serialize(self) -> Dict[str, Any]: + """Serialize the message for transmission.""" + serialized_msg = {"timestamps": self.timestamps, "images": {}} + for key, image in self.images.items(): + serialized_msg["images"][key] = ImageUtils.encode_image(image) + return serialized_msg + + @staticmethod + def deserialize(data: Dict[str, Any]) -> "ImageMessageSchema": + """Deserialize received message data.""" + timestamps = data.get("timestamps", {}) + images = {} + for key, value in data.get("images", {}).items(): + if isinstance(value, str): + images[key] = ImageUtils.decode_image(value) + else: + images[key] = value + return ImageMessageSchema(timestamps=timestamps, images=images) + + def asdict(self) -> Dict[str, Any]: + """Convert to dictionary format.""" + return {"timestamps": self.timestamps, "images": self.images} + + +class SensorServer: + def start_server(self, port: int): + self.context = zmq.Context() + self.socket = self.context.socket(zmq.PUB) + self.socket.setsockopt(zmq.SNDHWM, 20) # high water mark + self.socket.setsockopt(zmq.LINGER, 0) + self.socket.bind(f"tcp://*:{port}") + print(f"Sensor server running at tcp://*:{port}") + + self.message_sent = 0 + self.message_dropped = 0 + + def stop_server(self): + self.socket.close() + self.context.term() + + def send_message(self, data: Dict[str, Any]): + try: + packed = msgpack.packb(data, use_bin_type=True) + self.socket.send(packed, flags=zmq.NOBLOCK) + except zmq.Again: + self.message_dropped += 1 + print(f"[Warning] message dropped: {self.message_dropped}") + self.message_sent += 1 + + if self.message_sent % 100 == 0: + print( + f"[Sensor server] Message sent: {self.message_sent}, message dropped: {self.message_dropped}" + ) + + +class SensorClient: + def start_client(self, server_ip: str, port: int): + self.context = zmq.Context() + self.socket = self.context.socket(zmq.SUB) + self.socket.setsockopt_string(zmq.SUBSCRIBE, "") + self.socket.setsockopt(zmq.CONFLATE, True) # last msg only. + self.socket.setsockopt(zmq.RCVHWM, 3) # queue size 3 for receive buffer + self.socket.connect(f"tcp://{server_ip}:{port}") + + def stop_client(self): + self.socket.close() + self.context.term() + + def receive_message(self): + packed = self.socket.recv() + return msgpack.unpackb(packed, object_hook=m.decode) + + +class CameraMountPosition(Enum): + EGO_VIEW = "ego_view" + HEAD = "head" + LEFT_WRIST = "left_wrist" + RIGHT_WRIST = "right_wrist" + + +class ImageUtils: + @staticmethod + def encode_image(image: np.ndarray) -> str: + _, color_buffer = cv2.imencode(".jpg", image, [int(cv2.IMWRITE_JPEG_QUALITY), 80]) + return base64.b64encode(color_buffer).decode("utf-8") + + @staticmethod + def encode_depth_image(image: np.ndarray) -> str: + depth_compressed = cv2.imencode(".png", image)[1].tobytes() + return base64.b64encode(depth_compressed).decode("utf-8") + + @staticmethod + def decode_image(image: str) -> np.ndarray: + color_data = base64.b64decode(image) + color_array = np.frombuffer(color_data, dtype=np.uint8) + return cv2.imdecode(color_array, cv2.IMREAD_COLOR) + + @staticmethod + def decode_depth_image(image: str) -> np.ndarray: + depth_data = base64.b64decode(image) + depth_array = np.frombuffer(depth_data, dtype=np.uint8) + return cv2.imdecode(depth_array, cv2.IMREAD_UNCHANGED) diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientLogging.hpp b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientLogging.hpp new file mode 100644 index 0000000..a7469d7 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientLogging.hpp @@ -0,0 +1,158 @@ +#ifndef __CLIENT_LOGGING_HPP__ +#define __CLIENT_LOGGING_HPP__ +#include +#include +#include +#include + +namespace ManusSDK +{ + class ClientLog + { + public: + //Simple + static void debug(const char* p_String) + { + std::cout << "DEBUG: " << p_String << std::endl; + } + + static void info(const char* p_String) + { + std::cout << "INFO: " << p_String << std::endl; + } + + static void warn(const char* p_String) + { + std::cout << "WARN: " << p_String << std::endl; + } + + static void error(const char* p_String) + { + std::cout << "ERROR: " << p_String << std::endl; + } + + static void print(const char* p_String) + { + std::cout << p_String << std::endl; + } + + //Templated + template + static void debug(const char* p_Format, Args &&...p_Args) + { + std::string formatted_string = p_Format; + replacePlaceholders(formatted_string, std::forward(p_Args)...); + std::cout << "DEBUG: " << formatted_string << std::endl; + } + + template + static void info(const char* p_Format, Args &&...p_Args) + { + std::string formatted_string = p_Format; + replacePlaceholders(formatted_string, std::forward(p_Args)...); + std::cout << "INFO: " << formatted_string << std::endl; + } + + template + static void warn(const char* p_Format, Args &&...p_Args) + { + std::string formatted_string = p_Format; + replacePlaceholders(formatted_string, std::forward(p_Args)...); + std::cout << "WARN: " << formatted_string << std::endl; + } + + template + static void error(const char* p_Format, Args &&...p_Args) + { + std::string formatted_string = p_Format; + replacePlaceholders(formatted_string, std::forward(p_Args)...); + std::cout << "ERROR: " << formatted_string << std::endl; + } + + template + static void print(const char* p_Format, Args &&...p_Args) + { + std::string formatted_string = p_Format; + replacePlaceholders(formatted_string, std::forward(p_Args)...); + std::cout << formatted_string << std::endl; + } + + template + static void printWithPadding(const char* p_Format, int p_Padding, Args &&...p_Args) + { + std::string formatted_string = p_Format; + replacePlaceholdersWithPadding(formatted_string, p_Padding, std::forward(p_Args)...); + std::cout << formatted_string << std::endl; + } + + private: + // Base case for recursion: no arguments left + static void replacePlaceholders(std::string&) { + // No more arguments, so nothing to replace + } + + // Replace placeholders {} with actual values (only supports empty curly brackets, no formatting) + template + static void replacePlaceholders(std::string& p_Format, T&& p_Value, Args&&... p_Args) { + size_t pos = p_Format.find("{}"); + if (pos != std::string::npos) { + p_Format.replace(pos, 2, to_string(std::forward(p_Value))); + replacePlaceholders(p_Format, std::forward(p_Args)...); + } + } + + // Base case for recursion: no arguments left + static void replacePlaceholdersWithPadding(std::string&, int p_Padding) { + // No more arguments, so nothing to replace + } + // Replace placeholders {} with actual values (only supports empty curly brackets, no formatting) + template + static void replacePlaceholdersWithPadding(std::string& p_Format, int p_Padding, T&& p_Value, Args&&... p_Args) { + size_t pos = p_Format.find("{}"); + if (pos != std::string::npos) { + std::ostringstream oss; + oss << std::setw(p_Padding) << to_string(std::forward(p_Value)); + p_Format.replace(pos, 2, oss.str()); + replacePlaceholdersWithPadding(p_Format, p_Padding, std::forward(p_Args)...); + } + } + + // Utility to convert various types to string + template + static std::string to_string(const T& p_Value) { + std::ostringstream oss; + oss << p_Value; + return oss.str(); + } + + // Specialization for uint8_t + static std::string to_string(const uint8_t& p_Value) { + std::ostringstream oss; + oss << static_cast(p_Value); + return oss.str(); + } + + // Specialization for uint16_t + static std::string to_string(const uint16_t& p_Value) { + std::ostringstream oss; + oss << static_cast(p_Value); + return oss.str(); + } + + // Specialization for uint32_t + static std::string to_string(const uint32_t& p_Value) { + std::ostringstream oss; + oss << std::hex << p_Value; + return oss.str(); + } + + // Specialization for bool + static std::string to_string(const bool& p_Value) { + std::ostringstream oss; + oss << std::setw(5) << (p_Value ? "true" : "false"); + return oss.str(); + } + }; +} + +#endif diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientPlatformSpecific.cpp b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientPlatformSpecific.cpp new file mode 100644 index 0000000..3c70281 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientPlatformSpecific.cpp @@ -0,0 +1,559 @@ +#include "ClientPlatformSpecific.hpp" + +// signal types +#include +// std::filesystem +#include +// std::*fstream +#include +// CoreSdk_ShutDown +#include "ManusSDK.h" +// std::map +#include +// Ncurses terminal functions. +#include +// Termios terminal functions. +#include +#include +#include +// spdlog replacement +#include "ClientLogging.hpp" + +using namespace ManusSDK; + +/// @brief Reset a signal handler to its default, and then call it. +/// For signal types and explanation, see: +/// https://www.gnu.org/software/libc/manual/html_node/Standard-Signals.html +#define CALL_DEFAULT_SIGNAL_HANDLER(p_SignalType) \ + /* Reset the handler for this signal to the default. */ \ + signal(p_SignalType, SIG_DFL); \ + /* Re-raise this signal, causing the normal handler to run. */ \ + raise(p_SignalType); + +const std::string SDKClientPlatformSpecific::s_SlashForFilesystemPath = "/"; + +/// @brief Handle a signal telling the SDK client to quit. +/// A generic signal used to "politely ask a program to terminate". +/// On Linux, this can be sent by using the Gnome System Monitor and telling +/// the SDK client process to end. +static void HandleTerminationSignal(int p_Parameter) +{ + ClientLog::error( + "Termination signal sent with parameter {}.", + p_Parameter); + + CoreSdk_ShutDown(); + + CALL_DEFAULT_SIGNAL_HANDLER(SIGTERM); +} + +/// @brief Handle an interrupt signal. +/// Called when the INTR character is typed - usually ctrl + c. +static void HandleInterruptSignal(int p_Parameter) +{ + ClientLog::error( + "Interrupt signal sent with parameter {}.", + p_Parameter); + + CoreSdk_ShutDown(); + + CALL_DEFAULT_SIGNAL_HANDLER(SIGINT); +} + +/// @brief Handle a quit signal. +/// Called when the QUIT character is typed - usually ctrl + \. +static void HandleQuitSignal(int p_Parameter) +{ + ClientLog::error( + "Quit signal sent with parameter {}.", + p_Parameter); + + CoreSdk_ShutDown(); + + CALL_DEFAULT_SIGNAL_HANDLER(SIGQUIT); +} + +/// @brief Handle a hangup signal. +/// Called to report that the user's terminal has disconnected. +/// This can happen when connecting over the network, for example. +/// It also happens if the terminal window is closed while debugging. +static void HandleHangupSignal(int p_Parameter) +{ + ClientLog::error( + "Hang-up signal sent with parameter {}.", + p_Parameter); + + CoreSdk_ShutDown(); + + CALL_DEFAULT_SIGNAL_HANDLER(SIGHUP); +} + +/// @brief Initialise Ncurses so that we can check for input. +static bool InitializeNcurses(void) +{ + const WINDOW* const t_Window = initscr(); + if (!t_Window) + { + ClientLog::error("Failed to initialise the screen."); + + return false; + } + + // Don't buffer input until a newline or carriage return is typed. + if (cbreak() != OK) + { + ClientLog::error("Failed to make input unbuffered."); + + return false; + } + + // Don't echo input. + if (noecho() != OK) + { + ClientLog::error("Failed to disable input echoing."); + + return false; + } + + // Don't make newlines when the return key is pressed. + if (nonl() != OK) + { + ClientLog::error("Failed to disable newlines."); + + return false; + } + + // Do not flush the screen when interrupt/break/quit is pressed. + if (intrflush(stdscr, FALSE) != OK) + { + ClientLog::error("Failed to disable screen flushing."); + + return false; + } + + // Make getch non-blocking. + if (nodelay(stdscr, TRUE) != OK) + { + ClientLog::error("Failed to make nodelay non-blocking."); + + return false; + } + + // Enable handling the keypad ("function keys" like the arrow keys). + if (keypad(stdscr, TRUE) != OK) + { + ClientLog::error("Failed to enable keypad input."); + + return false; + } + + return true; +} + +/// @brief Initialise Termios so that terminal output is correct. +static bool InitializeTermios(void) +{ + // For some reason, printf and spdlog strings require a carriage return + // in addition to a newline after running initscr(). + // This code sets the "output modes" flag to treat newline characters + // as newline+carriage-return characters. + // https://arstechnica.com/civis/viewtopic.php?t=70699 + termios t_Settings; + if (tcgetattr(STDIN_FILENO, &t_Settings) != 0) + { + ClientLog::error("Failed to get Termios settings."); + + return false; + } + + t_Settings.c_oflag |= ONLCR; + if (tcsetattr(0, TCSANOW, &t_Settings) != 0) + { + ClientLog::error("Failed to set Termios settings."); + + return false; + } + + return true; +} + +/// @brief Register our signal handling functions. +static bool SetUpSignalHandlers(void) +{ + { + const __sighandler_t t_OldTerminationHandler = signal( + SIGTERM, + HandleTerminationSignal); + if (t_OldTerminationHandler == SIG_ERR) + { + ClientLog::error("Failed to set termination signal handler."); + return false; + } + } + + { + const __sighandler_t t_OldInterruptHandler = signal( + SIGINT, + HandleInterruptSignal); + if (t_OldInterruptHandler == SIG_ERR) + { + ClientLog::error("Failed to set interrupt signal handler."); + return false; + } + } + + { + const __sighandler_t t_OldQuitHandler = signal( + SIGQUIT, + HandleQuitSignal); + if (t_OldQuitHandler == SIG_ERR) + { + ClientLog::error("Failed to set quit signal handler."); + return false; + } + } + + { + const __sighandler_t t_OldHangupHandler = signal( + SIGHUP, + HandleHangupSignal); + if (t_OldHangupHandler == SIG_ERR) + { + ClientLog::error("Failed to set hang-up signal handler."); + return false; + } + } + + return true; +} + +/// @brief Handles keyboard input. +/// Requires things to be set up with Ncurses and Termios. +class ClientInput +{ +public: + /// @brief Update the state of the keyboard. + void Update(void) + { + // Reset the state of the last update. + for ( + InputMap_t::iterator t_Key = m_PressedLastUpdate.begin(); + t_Key != m_PressedLastUpdate.end(); + t_Key++) + { + t_Key->second = false; + } + + // Copy the current state to the last update's state, and clear the + // current state. + for ( + InputMap_t::iterator t_Key = m_CurrentlyPressed.begin(); + t_Key != m_CurrentlyPressed.end(); + t_Key++) + { + m_PressedLastUpdate[t_Key->first] = + t_Key->second; + t_Key->second = false; + } + + // Get the new state. + int t_Ch = getch(); + while (t_Ch != ERR) + { + if (t_Ch >= 'a' && t_Ch <= 'z') + { + // Unlike with Windows' GetAsyncKeyState(), upper case and + // lower case characters have different key numbers with + // getch(). + // Since all WasKeyPressed calls (as of writing this) use + // upper case, lower case keys need to be converted to work + // on Linux. + // Note that this does break the ability to check for lower + // case key presses. + t_Ch = toupper(t_Ch); + } + + m_CurrentlyPressed[t_Ch] = true; + + t_Ch = getch(); + } + } + + /// @brief Get the key's current state. + /// Note that unlike IsPressed(), this also stores the result for use in + /// the next key state check. + bool GetKey(const int p_Key) + { + bool t_IsPressed = IsPressed(p_Key); + m_PreviousKeyState[p_Key] = t_IsPressed; + + return t_IsPressed; + } + + /// @brief Was this key pressed since the last check? + /// Note that unlike WasJustPressed(), this checks if the key was pressed + /// since the last time a GetKey* function was called. + bool GetKeyDown(const int p_Key) + { + const bool t_IsPressed = IsPressed(p_Key); + + const auto t_PreviousState = m_PreviousKeyState.find(p_Key); + const bool t_PreviousValue = + t_PreviousState == m_PreviousKeyState.end() + ? false + : t_PreviousState->second; + + const bool t_Down = t_IsPressed && !t_PreviousValue; + m_PreviousKeyState[p_Key] = t_Down; + + return t_Down; + } + + /// @brief Was this key released since the last check? + /// Note that unlike WasJustReleased(), this checks if the key was released + /// since the last time a GetKey* function was called. + bool GetKeyUp(const int p_Key) + { + const bool t_IsPressed = IsPressed(p_Key); + + const auto t_PreviousState = m_PreviousKeyState.find(p_Key); + const bool t_PreviousValue = + t_PreviousState == m_PreviousKeyState.end() + ? false + : t_PreviousState->second; + + const bool t_Up = !t_IsPressed && t_PreviousValue; + m_PreviousKeyState[p_Key] = t_Up; + + return t_Up; + } + +private: + /// @brief Get the key's current state. + /// Note that unlike GetKey(), this does not store the result for use in + /// the next key state check. + bool IsPressed(const int p_Key) const + { + auto t_CurrentlyPressed = m_CurrentlyPressed.find(p_Key); + if (t_CurrentlyPressed == m_CurrentlyPressed.end()) + { + return false; + } + + return t_CurrentlyPressed->second; + } + + /// @brief Was this key pressed since the last input update? + /// Note that unlike GetKeyDown(), this function will return the same value + /// until the next keyboard state update. + bool WasJustPressed(const int p_Key) const + { + return !WasPressedLastUpdate(p_Key) && IsPressed(p_Key); + } + + /// @brief Was this key released since the last input update? + /// Note that unlike GetKeyUp(), this function will return the same value + /// until the next keyboard state update. + bool WasJustReleased(const int p_Key) const + { + return WasPressedLastUpdate(p_Key) && !IsPressed(p_Key); + } + + /// @brief Was this key pressed the previous input update? + bool WasPressedLastUpdate(const int p_Key) const + { + auto t_StateLastUpdate = m_PressedLastUpdate.find(p_Key); + if (t_StateLastUpdate == m_PressedLastUpdate.end()) + { + return false; + } + + return t_StateLastUpdate->second; + } + + typedef std::map InputMap_t; + InputMap_t m_CurrentlyPressed; + InputMap_t m_PressedLastUpdate; + // The Windows GetKey* functions only update the key state when a GetKey* + // function gets called. To make the Linux input work the same way, this + // map is used. + InputMap_t m_PreviousKeyState; +}; + +static ClientInput g_Input; + +bool SDKClientPlatformSpecific::PlatformSpecificInitialization(void) +{ + const bool t_NcursesResult = InitializeNcurses(); + const bool t_TermiosResult = InitializeTermios(); + const bool t_SignalResult = SetUpSignalHandlers(); + + return t_NcursesResult && t_TermiosResult && t_SignalResult; +} + +bool SDKClientPlatformSpecific::PlatformSpecificShutdown(void) +{ + // Ncurses. + endwin(); + + return true; +} + +void SDKClientPlatformSpecific::UpdateInput(void) +{ + g_Input.Update(); +} + +/*static*/ bool SDKClientPlatformSpecific::CopyString( + char* const p_Target, + const size_t p_MaxLengthThatWillFitInTarget, + const std::string& p_Source) +{ + if (!p_Target) + { + ClientLog::error( + "Tried to copy a string, but the target was null. The string was \"{}\".", + p_Source.c_str()); + + return false; + } + + if (p_MaxLengthThatWillFitInTarget == 0) + { + ClientLog::error( + "Tried to copy a string, but the target's size is zero. The string was \"{}\".", + p_Source.c_str()); + + return false; + } + + if (p_MaxLengthThatWillFitInTarget <= p_Source.length()) + { + ClientLog::error( + "Tried to copy a string that was longer than {} characters, which makes it too big for its target buffer. The string was \"{}\".", + p_MaxLengthThatWillFitInTarget, + p_Source.c_str()); + + return false; + } + + strcpy(p_Target, p_Source.c_str()); + + return true; +} + +bool SDKClientPlatformSpecific::ResizeWindow( + const short int p_ConsoleWidth, + const short int p_ConsoleHeight, + const short int p_ConsoleScrollback) +{ + // https://apple.stackexchange.com/questions/33736/can-a-terminal-window-be-resized-with-a-terminal-command/47841#47841 + // Use a control sequence to resize the window. + // Seems to be supported by the default terminal used in Gnome, as well + // as Mac OS. + // \\e[ -> ASCII ESC character (number 27, or 0x1B) + // -> control sequence introducer + // 8; -> resize the window + // y;xt -> The first number is the height, the second the width. + // Scrollback can't and doesn't need to be set here for Linux. + printf("\e[8;%d;%dt", p_ConsoleHeight, p_ConsoleWidth); + + ClearConsole(); + + // None of the ncurses functions for resizing the terminal actually + // seem to do anything. + /*if (resizeterm(180, 180) != OK) + { + return false; + }*/ + + return true; +} + +void SDKClientPlatformSpecific::ApplyConsolePosition( + const int p_ConsoleCurrentOffset) +{ + printf("\e[%d;1H", p_ConsoleCurrentOffset); +} + +/*static*/ void SDKClientPlatformSpecific::ClearConsole(void) +{ + // https://stackoverflow.com/questions/4062045/clearing-terminal-in-linux-with-c-code + // Use a control sequence to clear the terminal and move the cursor. + // Seems to be supported by the default terminal used in Gnome, + // as well as Mac OS. + // \\e[ -> ASCII ESC character (number 27, or 0x1B) -> control sequence introducer + // 2 -> the entire screen + // J -> clear the screen + //printf("\e[2J\n"); + + // Move the cursor to row 1 column 1. + //printf("\e[1;1H\n"); + + if (clear() != OK) + { + ClientLog::error("Failed to clear the screen."); + } + + refresh(); +} + +bool SDKClientPlatformSpecific::GetKey(const int p_Key) +{ + return g_Input.GetKey(p_Key); +} + +bool SDKClientPlatformSpecific::GetKeyDown(const int p_Key) +{ + return g_Input.GetKeyDown(p_Key); +} + +bool SDKClientPlatformSpecific::GetKeyUp(const int p_Key) +{ + return g_Input.GetKeyUp(p_Key); +} + +std::string SDKClientPlatformSpecific::GetDocumentsDirectoryPath_UTF8(void) +{ + const char* const t_Xdg = getenv("XDG_DOCUMENTS_DIR"); + + // Backup - the documents folder is usually going to be in $HOME/Documents. + const char* const t_Home = getenv("HOME"); + if (!t_Xdg && !t_Home) + { + return std::string(""); + } + + const std::string t_DocumentsDir = + (!t_Xdg || strlen(t_Xdg) == 0) + ? std::string(t_Home) + std::string("/Documents") + : std::string(t_Xdg); + + return t_DocumentsDir; +} + +std::ifstream SDKClientPlatformSpecific::GetInputFileStream( + std::string p_Path_UTF8) +{ + return std::ifstream(p_Path_UTF8, std::ifstream::binary); +} + +std::ofstream SDKClientPlatformSpecific::GetOutputFileStream( + std::string p_Path_UTF8) +{ + return std::ofstream(p_Path_UTF8, std::ofstream::binary); +} + +bool SDKClientPlatformSpecific::DoesFolderOrFileExist(std::string p_Path_UTF8) +{ + return std::filesystem::exists(p_Path_UTF8); +} + +void SDKClientPlatformSpecific::CreateFolderIfItDoesNotExist( + std::string p_Path_UTF8) +{ + if (!DoesFolderOrFileExist(p_Path_UTF8)) + { + std::filesystem::create_directory(p_Path_UTF8); + } +} diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientPlatformSpecific.hpp b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientPlatformSpecific.hpp new file mode 100644 index 0000000..4cbbe10 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientPlatformSpecific.hpp @@ -0,0 +1,90 @@ +#ifndef _CLIENT_PLATFORM_SPECIFIC_HPP_ +#define _CLIENT_PLATFORM_SPECIFIC_HPP_ + +#include "ClientPlatformSpecificTypes.hpp" + +// size_t +#include +// std::string +#include + +// Set up a Doxygen group. +/** @addtogroup SDKClient + * @{ + */ + +class SDKClientPlatformSpecific +{ +protected: + /// @brief Initialise things only needed for this platform. + bool PlatformSpecificInitialization(void); + + /// @brief Shut down things only needed for this platform. + bool PlatformSpecificShutdown(void); + + /// @brief Update the current keyboard state. + void UpdateInput(void); + + /// @brief Copy the given string into the given target. + static bool CopyString( + char* const p_Target, + const size_t p_MaxLengthThatWillFitInTarget, + const std::string& p_Source); + + /// @brief Resize the window, so the log messages will fit. + /// Make sure not to enter illegal sizes in here or SetConsoleWindowInfo + /// and SetConsoleScreenBufferSize can generate an error. + /// This is not code directly needed for the SDK to function, but its handy + /// for a quick and simple output for this client. + bool ResizeWindow( + const short int p_ConsoleWidth, + const short int p_ConsoleHeight, + const short int p_ConsoleScrollback); + + /// @brief Set the current console position. + /// This handles the platform-specific part of advancing the console + /// position. + void ApplyConsolePosition( + const int p_ConsoleCurrentOffset); + + /// @brief Clear the console window. + static void ClearConsole(void); + + /// @brief Gets key's current state. + /// True is pressed false is not pressed. + bool GetKey(const int p_Key); + + /// @brief Returns true first time it is called when key is pressed. + bool GetKeyDown(const int p_Key); + + /// @brief Returns true first time it is called when key is released. + bool GetKeyUp(const int p_Key); + + /// @brief Get the path to the user's Documents folder. + /// The string should be in UTF-8 format. + std::string GetDocumentsDirectoryPath_UTF8(void); + + /// @brief Get an input stream for the given file. + /// The file's path should be in UTF-8 format. + std::ifstream GetInputFileStream(std::string p_Path_UTF8); + + /// @brief Get an output stream for the given file. + /// The file's path should be in UTF-8 format. + std::ofstream GetOutputFileStream(std::string p_Path_UTF8); + + /// @brief Check if the given folder or file exists. + /// The folder path given should be in UTF-8 format. + bool DoesFolderOrFileExist(std::string p_Path_UTF8); + + /// @brief Create the given folder if it does not exist. + /// The folder path given should be in UTF-8 format. + void CreateFolderIfItDoesNotExist(std::string p_Path_UTF8); + + /// @brief The slash character that is used in the filesystem. + static const std::string s_SlashForFilesystemPath; +}; + +// Close the Doxygen group. +/** @} */ + +#endif diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientPlatformSpecificTypes.hpp b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientPlatformSpecificTypes.hpp new file mode 100644 index 0000000..2c994b3 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ClientPlatformSpecificTypes.hpp @@ -0,0 +1,48 @@ +#ifndef _CLIENT_PLATFORM_SPECIFIC_TYPES_HPP_ +#define _CLIENT_PLATFORM_SPECIFIC_TYPES_HPP_ + +#include + +// Set up a Doxygen group. +/** @addtogroup SDKClient + * @{ + */ + +// Virtual key code characters for WasKeyPressed(). +// Based on key codes used with GetAsyncKeyState(). +// Manually checked what values getch() returns for these keys. +// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes +enum MicrosoftVirtualKeyCodes +{ + VK_TAB = 9, // ASCII tab character value + VK_RETURN = 13, // ASCII carriage return character value + VK_ESCAPE = 27, // ASCII ESC character value + VK_DOWN = 258, + VK_UP = 259, + VK_LEFT = 260, + VK_RIGHT = 261, + VK_HOME = 262, + VK_BACK = 263, + VK_F1 = 265, + VK_F2 = 266, + VK_F3 = 267, + VK_F4 = 268, + VK_F5 = 269, + VK_F6 = 270, + VK_F7 = 271, + VK_F8 = 272, + VK_F9 = 273, + VK_F10 = 274, + VK_F11 = 275, + VK_F12 = 276, + VK_DELETE = 330, + VK_INSERT = 331, + VK_NEXT = 338, + VK_PRIOR = 339, + VK_END = 360 +}; + +// Close the Doxygen group. +/** @} */ + +#endif diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/Dockerfile b/gr00t_wbc/control/teleop/device/SDKClient_Linux/Dockerfile new file mode 100644 index 0000000..7e127a7 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/Dockerfile @@ -0,0 +1,88 @@ +# Copyright 2018 gRPC authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License.# +# +# Based on https://hub.docker.com/r/grpc/cxx. + +# To build +# docker build -f ./Dockerfile -t manus-linux . + +# To Run (Linux) +# docker run --net==host --privileged -v /dev:/dev -v /run/udev:/run/udev -i -t manus-linux /bin/bash + +# To Run (Windows) +# docker run -p 5000:5000 -i -t manus-linux /bin/bash + +# FROM ubuntu:jammy as build +FROM ubuntu:focal as build +LABEL description="Visual Studio Manus SDK Build container" + +ENV TZ=Europe/Amsterdam +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update && apt-get install -y \ + build-essential \ + # Required to install GRPC + git \ + libtool \ + libzmq3-dev \ + # Required for core integrated + libusb-1.0-0-dev \ + libudev-dev \ + # Only required for building the minimal client + libncurses5-dev \ + # Only required for visual studio debugging + gdb \ + && apt-get clean + +ENV GRPC_RELEASE_TAG="v1.28.1" + +RUN git clone -b ${GRPC_RELEASE_TAG} https://github.com/grpc/grpc /var/local/git/grpc && \ + cd /var/local/git/grpc && \ + git submodule update --init --recursive + +RUN echo "-- installing protobuf" && \ + cd /var/local/git/grpc/third_party/protobuf && \ + ./autogen.sh && ./configure --enable-shared && \ + make -j$(nproc) && make -j$(nproc) check && make install && make clean && ldconfig + +RUN echo "-- installing grpc" && \ + cd /var/local/git/grpc && \ + make -j$(nproc) && make install && make clean && ldconfig + +# configure SSH for communication with Visual Studio +RUN apt-get update && apt-get install -y openssh-server +RUN mkdir -p /var/run/sshd +RUN echo 'PasswordAuthentication yes' >> /etc/ssh/sshd_config && \ + ssh-keygen -A + +# please make sure to change the password +RUN id -u manus >/dev/null 2>&1 || useradd -m -d /home/manus -s /bin/bash -G sudo manus && \ +echo "manus:password" | chpasswd + +# copy dependencies to ~/ManusSDK/ directory +COPY ManusSDK /home/manus/ManusSDK + +# Add read/write permissions for manus devices +RUN apt-get update && apt-get install -y udev +RUN echo "# HIDAPI/libusb" > /etc/udev/rules.d/99-manus.rules && \ + echo "SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"3325\", MODE:=\"0666\"" >> /etc/udev/rules.d/99-manus.rules && \ + echo "# HIDAPI/hidraw" >> /etc/udev/rules.d/99-manus.rules && \ + echo "KERNEL==\"hidraw*\", ATTRS{idVendor}==\"3325\", MODE:=\"0666\"" >> /etc/udev/rules.d/99-manus.rules + +# change default SSH port to 5000 to not conflict with system ssh in host mode +RUN echo "Port 5000" >> /etc/ssh/sshd_config + +ENTRYPOINT service ssh start && service udev start && bin/bash + +EXPOSE 5000 diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/Dockerfile.Integrated b/gr00t_wbc/control/teleop/device/SDKClient_Linux/Dockerfile.Integrated new file mode 100644 index 0000000..0a810e8 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/Dockerfile.Integrated @@ -0,0 +1,69 @@ +# Copyright 2018 gRPC authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License.# +# +# Based on https://hub.docker.com/r/grpc/cxx. + +# To build +# docker build -f ./Dockerfile.Integrated -t manus-linux-integrated . + +# To Run (Linux) +# docker run -p 5000:5000 --privileged -v /dev:/dev -v /run/udev:/run/udev -i -t manus-linux-integrated /bin/bash + +# To Run (Windows) +# docker run -p 5000:5000 -i -t manus-linux-integrated /bin/bash + +# FROM ubuntu:jammy as build +FROM ubuntu:focal as build +LABEL description="Visual Studio Manus SDK Build container" + +ENV TZ=Europe/Amsterdam +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update && apt-get install -y \ + build-essential \ + # Required for core integrated + libusb-1.0-0-dev \ + libudev-dev \ + # Only required for building the minimal client + libncurses5-dev \ + # Only required for visual studio debugging + gdb \ + && apt-get clean + +# configure SSH for communication with Visual Studio +RUN apt-get update && apt-get install -y openssh-server +RUN mkdir -p /var/run/sshd +RUN echo 'PasswordAuthentication yes' >> /etc/ssh/sshd_config && \ + ssh-keygen -A + +# please make sure to change the password +RUN id -u manus >/dev/null 2>&1 || useradd -m -d /home/manus -s /bin/bash -G sudo manus && \ +echo "manus:password" | chpasswd + +# copy dependencies to ~/ManusSDK/ directory +COPY ManusSDK /home/manus/ManusSDK + +# Add read/write permissions for manus devices +RUN apt-get update && apt-get install -y udev +RUN echo "# HIDAPI/libusb" > /etc/udev/rules.d/99-manus.rules && \ + echo "SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"3325\", MODE:=\"0666\"" >> /etc/udev/rules.d/99-manus.rules && \ + echo "# HIDAPI/hidraw" >> /etc/udev/rules.d/99-manus.rules && \ + echo "KERNEL==\"hidraw*\", ATTRS{idVendor}==\"3325\", MODE:=\"0666\"" >> /etc/udev/rules.d/99-manus.rules + +# change default SSH port to 5000 to not conflict with system ssh in host mode +RUN echo "Port 5000" >> /etc/ssh/sshd_config + +ENTRYPOINT service ssh start && service udev start && bin/bash + +EXPOSE 5000 \ No newline at end of file diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/Main.cpp b/gr00t_wbc/control/teleop/device/SDKClient_Linux/Main.cpp new file mode 100644 index 0000000..3749ad2 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/Main.cpp @@ -0,0 +1,52 @@ +// Set up a Doxygen group. +/** @addtogroup Main + * @{ + */ + +#include "ClientLogging.hpp" +#include "SDKClient.hpp" +#include +#include +#include +#include +#include +#include + +namespace py = pybind11; + + +ClientReturnCode t_Result; +SDKClient t_SDKClient; + +int init() { + t_SDKClient.Initialize(); + t_SDKClient.Run(); + while (output_map.size() == 0){ + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return 0; +} + +py::dict get_latest_state() { + py::dict py_dict; + for (const auto& pair : output_map) { + py::list py_list; // Initialize a py::list for each vector + for (double value : pair.second) { + py_list.append(value); // Append each value from the vector to the py::list + } + py_dict[py::str(pair.first)] = py_list; // Assign the list to the dict with the string key + } + return py_dict; +} + +int shutdown() { + t_SDKClient.ShutDown(); + std::cout<<"Manus shutdown\n"; + return 0; +} + +PYBIND11_MODULE(ManusServer, m) { + m.def("init", &init); + m.def("get_latest_state", &get_latest_state); + m.def("shutdown", &shutdown); +} \ No newline at end of file diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/Makefile b/gr00t_wbc/control/teleop/device/SDKClient_Linux/Makefile new file mode 100644 index 0000000..a5acc7c --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/Makefile @@ -0,0 +1,59 @@ +CXX := g++ + +#CXXFLAGS := -pedantic-errors -Wall -Wextra -Werror -g --std=c++17 +# CXXFLAGS := -g --std=c++17 + +PYBIND11_INCLUDES := $(shell python3 -m pybind11 --includes) +PYBIND11_SUFFIX := $(shell python3-config --extension-suffix) +CXXFLAGS := -shared -g --std=c++17 -fPIC ${PYBIND11_INCLUDES} + +LDFLAGS := -L./ManusSDK/lib -lManusSDK -lncurses -lpthread -Wl,-rpath=./ManusSDK/lib +BUILD := ./ +OBJ_DIR := $(BUILD)/objects +APP_DIR := $(BUILD) + +# TARGET := SDKClient_Linux.out +TARGET := ManusServer$(PYBIND11_SUFFIX) + +INCLUDE := -I./ManusSDK/include +SRC := \ + $(wildcard *.cpp) + +OBJECTS := $(SRC:%.cpp=$(OBJ_DIR)/%.o) +DEPENDENCIES \ + := $(OBJECTS:.o=.d) + +all: build $(APP_DIR)/$(TARGET) + +$(OBJ_DIR)/%.o: %.cpp + @mkdir -p $(@D) + $(CXX) $(CXXFLAGS) $(INCLUDE) -c $< -MMD -o $@ + +$(APP_DIR)/$(TARGET): $(OBJECTS) + @mkdir -p $(@D) + $(CXX) $(CXXFLAGS) -o $(APP_DIR)/$(TARGET) $^ $(LDFLAGS) + +-include $(DEPENDENCIES) + +.PHONY: all build clean debug release info + +build: + @mkdir -p $(APP_DIR) + @mkdir -p $(OBJ_DIR) + +debug: CXXFLAGS += -DDEBUG -g +debug: all + +release: CXXFLAGS += -g +release: all + +clean: + -@rm $(TARGET) + -@rm -rvf $(OBJ_DIR)/* + +info: + @echo "[*] Application dir: ${APP_DIR} " + @echo "[*] Object dir: ${OBJ_DIR} " + @echo "[*] Sources: ${SRC} " + @echo "[*] Objects: ${OBJECTS} " + @echo "[*] Dependencies: ${DEPENDENCIES}" diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/include/ManusSDK.h b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/include/ManusSDK.h new file mode 100644 index 0000000..173a8e3 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/include/ManusSDK.h @@ -0,0 +1,1058 @@ +#ifndef __MANUS_SDK_H__ +#define __MANUS_SDK_H__ + +// Set up a Doxygen group. +/** @addtogroup ManusSDK + * @{ + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ManusSDKTypes.h" +#include "ManusSDKTypeInitializers.h" + +#include + +#ifdef MANUS_SDK_STATIC +#define CORESDK_API +#else +#ifdef _WIN32 + // MANUS_SDK_EXPORTS defines if the functions are exported to or + // imported from a DLL. +#ifdef MANUS_SDK_EXPORTS +#define CORESDK_API __declspec(dllexport) +#else +#define CORESDK_API __declspec(dllimport) +#endif +#elif defined(__linux__) +#define CORESDK_API +#else +#error Unrecognized platform. +#endif +#endif + + /****************************************************************************** + * Wrapper startup and shutdown. + *****************************************************************************/ + + /// @brief Initialize the wrapper. + /// Call this before using the wrapper. + /// @param p_TypeOfSession + /// @param p_Remote If set to true, this is essentially the same as calling the old CoreSdk_Initialize + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_FunctionCalledAtWrongTime if the function was not intended to be called at this time. + CORESDK_API SDKReturnCode CoreSdk_Initialize(SessionType p_TypeOfSession, bool p_Remote); + + /// @brief Shut down the wrapper. + /// This needs to be called last. + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available. + CORESDK_API SDKReturnCode CoreSdk_ShutDown(); + + /****************************************************************************** + * Utility functions. + *****************************************************************************/ + + /// @brief Check if the wrapper DLL was built in the debug configuration. + /// @param p_WasBuiltInDebugConfiguration + /// @return SDKReturnCode_Success if successful. + CORESDK_API SDKReturnCode CoreSdk_WasDllBuiltInDebugConfiguration(bool* p_WasBuiltInDebugConfiguration); + + /// @brief Gets the timestamp info (more readable form of timestamp). + /// @param p_Timestamp Timestamp to get info from + /// @param p_Info Info of the timestamp + /// @return SDKReturnCode_Success if successful. + CORESDK_API SDKReturnCode CoreSdk_GetTimestampInfo(ManusTimestamp p_Timestamp, ManusTimestampInfo* p_Info); + + /// @brief Sets the timestamp according to the info (more readable form of timestamp). + /// @param p_Timestamp the Timestamp to set info of + /// @param p_Info Info to get info from + /// @return SDKReturnCode_Success if successful. + CORESDK_API SDKReturnCode CoreSdk_SetTimestampInfo(ManusTimestamp* p_Timestamp, ManusTimestampInfo p_Info); + + + /****************************************************************************** + * Connection handling. + *****************************************************************************/ + + /// @brief Start a background task that looks for hosts running Manus Core. + /// Call this first when looking for hosts to connect to. + /// Underlying function will sleep for p_WaitSeconds to allow servers to reply. + /// @param p_WaitSeconds defines the time the function will sleep for to allow servers to reply, + /// @param p_LoopbackOnly if true it looks for hosts only locally, if false it looks for hosts anywhere in the network, + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_FunctionCalledAtWrongTime if the function was not intended to be called at this time. + CORESDK_API SDKReturnCode CoreSdk_LookForHosts(uint32_t p_WaitSeconds = 1, bool p_LoopbackOnly = false); + + /// @brief Get the number of hosts running Manus Core that were found. + /// This is the second function to call when looking for hosts to connect to. + /// @param p_NumberOfAvailableHostsFound + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_FunctionCalledAtWrongTime if the function was not intended to be called at this time. + CORESDK_API SDKReturnCode CoreSdk_GetNumberOfAvailableHostsFound(uint32_t* p_NumberOfAvailableHostsFound); + + /// @brief Fill the given array with information on the hosts that were found. + /// This is the third and final function to call when looking for hosts to + /// connect to. + /// @param p_AvailableHostsFound + /// @param p_NumberOfHostsThatFitInArray + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_FunctionCalledAtWrongTime if the function was not intended to be called at this time + /// SDKReturnCode_ArgumentSizeMismatch if p_NumberOfHostsThatFitInArray does not match the number of available hosts found in the network, + /// SDKReturnCode_InvalidArgument if the provided p_AvailableHostsFound is a null pointer, + /// SDKReturnCode_InternalError if the number of available hosts found is higher than the defined MAX_NUMBER_OF_HOSTS, + /// SDKReturnCode_UnsupportedStringSizeEncountered if the name of a host is higher than the defined MAX_NUM_CHARS_IN_HOST_NAME, + /// SDKReturnCode_Error if copying the string representing the host name was not successful, + /// SDKReturnCode_NullPointer if invalid host data was found. + CORESDK_API SDKReturnCode CoreSdk_GetAvailableHostsFound(ManusHost* p_AvailableHostsFound, const uint32_t p_NumberOfHostsThatFitInArray); + + + /// @brief Check if the wrapper is connected to ManusCore. + /// @param p_ConnectedToCore + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core. + CORESDK_API SDKReturnCode CoreSdk_GetIsConnectedToCore(bool* p_ConnectedToCore); + + /// @brief Connect to the preset GRPC address. + /// @return SDKReturnCode_Success + /// SDKReturnCode_NotConnected if there is no connection to core. + CORESDK_API SDKReturnCode CoreSdk_ConnectGRPC(); + + /// @brief Disconnects from the current host + /// @return SDKReturnCode_Success + /// SDKReturnCode_NotConnected if there is no connection to core. + CORESDK_API SDKReturnCode CoreSdk_Disconnect(); + + /// @brief Connect to a host using the given host information. + /// @param p_Host + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_SdkIsTerminating if the sdk is being terminated, + /// SDKReturnCode_FunctionCalledAtWrongTime if the function was not intended to be called at this time, + /// SDKReturnCode_NoCoordinateSystemSet if the user tried to connect without first setting up the coordinate system settings. + CORESDK_API SDKReturnCode CoreSdk_ConnectToHost(ManusHost p_Host); + + /// @brief Initialize a coordinate system of type CoordinateSystemVUH. (View Up Handedness) + /// This has to be called before a connection is made. + /// @param p_CoordinateSystem + /// @param p_UseWorldCoordinates if true all data will be defined with respect to a world coordinate system, + /// if false all data will be defined with respect to a local one, + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available. + CORESDK_API SDKReturnCode CoreSdk_InitializeCoordinateSystemWithVUH(CoordinateSystemVUH p_CoordinateSystem, bool p_UseWorldCoordinates); + + /// @brief Initialize a coordinate system of type CoordinateSystemDirection. + /// This has to be called before a connection is made. + /// @param p_CoordinateSystem + /// @param p_UseWorldCoordinates if true all data will be defined with respect to a world coordinate system, + /// if false all data will be defined with respect to a local one, + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available. + CORESDK_API SDKReturnCode CoreSdk_InitializeCoordinateSystemWithDirection(CoordinateSystemDirection p_CoordinateSystem, bool p_UseWorldCoordinates); + + /// @brief Get the SDK and Core version used and check if they are compatible with each other. + /// @param p_SdkVersion + /// @param p_CoreVersion + /// @param p_AreVersionsCompatible + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core. + CORESDK_API SDKReturnCode CoreSdk_GetVersionsAndCheckCompatibility(ManusVersion* p_SdkVersion, ManusVersion* p_CoreVersion, bool* p_AreVersionsCompatible); + + /// @brief Get the current session Id. + /// @param p_SessionId + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_InvalidID if the session id is zero, which means it was not correctly assigned. + CORESDK_API SDKReturnCode CoreSdk_GetSessionId(uint32_t* p_SessionId); + + /// @brief Register the callback function that is called when manus core gets connected to the sdk. + /// @param p_ConnectedCallback + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available. + CORESDK_API SDKReturnCode CoreSdk_RegisterCallbackForOnConnect(ConnectedToCoreCallback_t p_ConnectedCallback); + + /// @brief Register the callback function that is called when manus core gets disconnected from the sdk. + /// @param p_DisconnectedCallback + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available. + CORESDK_API SDKReturnCode CoreSdk_RegisterCallbackForOnDisconnect(DisconnectedFromCoreCallback_t p_DisconnectedCallback); + + /// @brief Register the callback function that is called when something is logged in the SDK. + /// @param p_LoggingCallback + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available. + CORESDK_API SDKReturnCode CoreSdk_RegisterCallbackForOnLog(LoggingCallback_t p_LoggingCallback); + + /// @brief Register the callback function that is called when skeleton data comes in. + /// @param p_SkeletonStreamCallback + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available. + CORESDK_API SDKReturnCode CoreSdk_RegisterCallbackForSkeletonStream(SkeletonStreamCallback_t p_SkeletonStreamCallback); + + /// @brief Register the callback function that is called when tracker data comes in. + /// @param p_TrackerStreamCallback + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available. + CORESDK_API SDKReturnCode CoreSdk_RegisterCallbackForTrackerStream(TrackerStreamCallback_t p_TrackerStreamCallback); + + /// @brief Register the callback function that is called when the landscape data comes in. + /// @param p_LandscapeStreamCallback + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available. + CORESDK_API SDKReturnCode CoreSdk_RegisterCallbackForLandscapeStream(LandscapeStreamCallback_t p_LandscapeStreamCallback); + + /// @brief Register the callback function that is called when core system related data comes in. + /// @param p_SystemStreamCallback + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available. + CORESDK_API SDKReturnCode CoreSdk_RegisterCallbackForSystemStream(SystemStreamCallback_t p_SystemStreamCallback); + + /// @brief Register the callback function that is called when ergonomics data comes in. + /// @param p_ErgonomicsStreamCallback + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available. + CORESDK_API SDKReturnCode CoreSdk_RegisterCallbackForErgonomicsStream(ErgonomicsStreamCallback_t p_ErgonomicsStreamCallback); + + /// @brief Register the callback function that is called when skeleton data comes in. + /// @param p_SkeletonStreamCallback + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available. + CORESDK_API SDKReturnCode CoreSdk_RegisterCallbackForRawSkeletonStream(RawSkeletonStreamCallback_t p_RawSkeletonStreamCallback); + + /// @brief Register the callback function that is called when gesture data comes in. + /// @param p_GestureStreamCallback + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available. + CORESDK_API SDKReturnCode CoreSdk_RegisterCallbackForGestureStream(GestureStreamCallback_t p_GestureStreamCallback); + + /****************************************************************************** + * Basic glove interaction. + *****************************************************************************/ + + /// @brief Try to pair a glove to a dongle + /// @param p_GloveID + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available. + CORESDK_API SDKReturnCode CoreSdk_PairGlove(uint32_t p_GloveID, bool* p_Result); + + /// @brief Try to unpair a glove to a dongle + /// @param p_GloveID + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available. + CORESDK_API SDKReturnCode CoreSdk_UnpairGlove(uint32_t p_GloveID, bool* p_Result); + + /// @brief Vibrate the motor on the given fingers of a haptic glove. + /// The order of the fingers is Thumb, Index, Middle, Ring, Pinky. + /// @param p_DongleId + /// @param p_HandType + /// @param p_Powers strength of the vibration, should be an array of 5 values + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core. + /// @deprecated Use CoreSdk_VibrateFingersForGlove or CoreSdk_VibrateFingersForSkeleton instead + [[deprecated("Use CoreSdk_VibrateFingersForGlove or CoreSdk_VibrateFingersForSkeleton instead")]] + CORESDK_API SDKReturnCode CoreSdk_VibrateFingers(uint32_t p_DongleId, Side p_HandType, const float* p_Powers); + + /// @brief Vibrate the motor on the given fingers of a haptic glove. + /// The order of the fingers is Thumb, Index, Middle, Ring, Pinky. + /// @param p_GloveId + /// @param p_Powers strength of the vibration, should be an array of 5 values + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core. + CORESDK_API SDKReturnCode CoreSdk_VibrateFingersForGlove(uint32_t p_GloveId, const float* p_Powers); + + /// @brief Vibrate the motor on the fingers of a haptic glove associated to skeleton with given id. + /// The order of the fingers is Thumb, Index, Middle, Ring, Pinky. + /// @param p_SkeletonId + /// @param p_HandType + /// @param p_Powers strength of the vibration, should be an array of 5 values + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core. + CORESDK_API SDKReturnCode CoreSdk_VibrateFingersForSkeleton(uint32_t p_SkeletonId, Side p_HandType, const float* p_Powers); + + /// @brief Get a glove ID for the given hand of the given user identified by its Id. + /// @param p_UserId + /// @param p_HandType + /// @param p_GloveId + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidArgument if the provided p_HandType was neither left nor right, + /// SDKReturnCode_InvalidID if no user with the provided p_UserId was found in the landscape. + CORESDK_API SDKReturnCode CoreSdk_GetGloveIdOfUser_UsingUserId(uint32_t p_UserId, Side p_HandType, uint32_t* p_GloveId); + + /// @brief Get the number of gloves that are available. + /// @param p_NumberOfAvailableGloves + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InternalError if the number of available gloves found is higher than the defined MAX_NUMBER_OF_GLOVES. + CORESDK_API SDKReturnCode CoreSdk_GetNumberOfAvailableGloves(uint32_t* p_NumberOfAvailableGloves); + + /// @brief Fill the given array with the IDs of all available gloves. + /// The size of the given array must match the number of available gloves. + /// Note that the number of available gloves can change at any time. + /// @param p_IdsOfAvailableGloves + /// @param p_NumberOfIdsThatFitInArray + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_ArgumentSizeMismatch if p_NumberOfIdsThatFitInArray does not match with the number of available gloves found, + /// SDKReturnCode_InvalidArgument if p_NumberOfIdsThatFitInArray is zero or is higher than the defined MAX_NUMBER_OF_GLOVES. + CORESDK_API SDKReturnCode CoreSdk_GetIdsOfAvailableGloves(uint32_t* p_IdsOfAvailableGloves, uint32_t p_NumberOfIdsThatFitInArray); + + /// @brief Get glove id's for given dongle id. + /// @param p_DongleId + /// @param p_LeftGloveId + /// @param p_RightGloveId + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core. + /// @deprecated Get this through the Landscape instead + [[deprecated("Get this through the Landscape instead")]] + CORESDK_API SDKReturnCode CoreSdk_GetGlovesForDongle(uint32_t p_DongleId, uint32_t* p_LeftGloveId, uint32_t* p_RightGloveId); + + /// @brief Get data for the glove with the given glove ID. + /// @param p_GloveId + /// @param p_GloveData + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_DataNotAvailable if no glove with the provided p_GloveId was found in the landscape. + /// @deprecated Get this through the Landscape instead + [[deprecated("Get this through the Landscape instead")]] + CORESDK_API SDKReturnCode CoreSdk_GetDataForGlove_UsingGloveId(uint32_t p_GloveId, GloveLandscapeData* p_GloveData); + + /// @brief Gets the data for a dongle with the given id. + /// @param p_DongleId + /// @param p_DongleData + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_DataNotAvailable if no dongle with the provided p_DongleId was found in the landscape. + CORESDK_API SDKReturnCode CoreSdk_GetDataForDongle(uint32_t p_DongleId, DongleLandscapeData* p_DongleData); + + /// @brief Get the number of available dongles. + /// @param p_NumberOfDongles + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InternalError if the number of dongles found is higher than the defined MAX_NUMBER_OF_DONGLES. + /// @deprecated Get this through the Landscape instead + [[deprecated("Get this through the Landscape instead")]] + CORESDK_API SDKReturnCode CoreSdk_GetNumberOfDongles(uint32_t* p_NumberOfDongles); + + /// @brief Fill the given array with the IDs of all available dongles. + /// The size of the given array must match the number of available dongles. + /// Note that the number of available dongles can change at any time. + /// @param p_DongleIds + /// @param p_NumberOfIdsThatFitInArray + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_ArgumentSizeMismatch if p_NumberOfIdsThatFitInArray does not match with the number of dongles found, + /// SDKReturnCode_InvalidArgument if p_NumberOfIdsThatFitInArray is zero or higher than the defined MAX_NUMBER_OF_HAPTICS_DONGLES. + /// @deprecated Get this through the Landscape instead + [[deprecated("Get this through the Landscape instead")]] + CORESDK_API SDKReturnCode CoreSdk_GetDongleIds(uint32_t* p_DongleIds, uint32_t p_NumberOfIdsThatFitInArray); + + /****************************************************************************** + * Haptics module. + *****************************************************************************/ + + /// @brief Get the number of available haptics dongles. + /// @param p_NumberOfHapticsDongles + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InternalError if the number of haptic dongles found is higher than the defined MAX_NUMBER_OF_HAPTICS_DONGLES. + /// @deprecated Get this through the Landscape instead + [[deprecated("Get this through the Landscape instead")]] + CORESDK_API SDKReturnCode CoreSdk_GetNumberOfHapticsDongles(uint32_t* p_NumberOfHapticsDongles); + + /// @brief Fill the given array with the IDs of all available haptics dongles. + /// The size of the given array must match the number of available haptics dongles. + /// Note that the number of available haptics dongles can change at any time. + /// @param p_HapticsDongleIds + /// @param p_NumberOfIdsThatFitInArray + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_ArgumentSizeMismatch if p_NumberOfIdsThatFitInArray does not match with the number of haptic dongles found, + /// SDKReturnCode_InvalidArgument if p_NumberOfIdsThatFitInArray is zero or higher than the defined MAX_NUMBER_OF_HAPTICS_DONGLES. + /// @deprecated Get this through the Landscape instead + [[deprecated("Get this through the Landscape instead")]] + CORESDK_API SDKReturnCode CoreSdk_GetHapticsDongleIds(uint32_t* p_HapticsDongleIds, uint32_t p_NumberOfIdsThatFitInArray); + + /****************************************************************************** + * Users. + *****************************************************************************/ + + /// @brief Get the number of available users. + /// Note that this is reliant on the landscape, and the first second after connecting the client to the core it will not yet have landscape + /// data, so the number of users wil be 0. Since there will always be a default users, just wait until the count is above 0 and then proceed. + /// @param p_NumberOfAvailableUsers + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InternalError if the number of available users found is higher than the defined MAX_NUMBER_OF_USERS. + CORESDK_API SDKReturnCode CoreSdk_GetNumberOfAvailableUsers(uint32_t* p_NumberOfAvailableUsers); + + /// @brief Fill the given array with the IDs of all available users. + /// The size of the given array must match the number of available users. + /// Note that the number of available users can change at any time depending on the landscape. + /// @param p_IdsOfAvailableUsers, The array of ids that will get filled + /// @param p_NumberOfIdsThatFitInArray + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidArgument if p_NumberOfIdsThatFitInArray is null or higher than the defined MAX_NUMBER_OF_USERS, or + /// if p_IdsOfAvailableUsers is a null array. + /// SDKReturnCode_ArgumentSizeMismatch if p_NumberOfIdsThatFitInArray does not match with number of available users found. + CORESDK_API SDKReturnCode CoreSdk_GetIdsOfAvailableUsers(uint32_t* p_IdsOfAvailableUsers, uint32_t p_NumberOfIdsThatFitInArray); + + /****************************************************************************** + * Tracking. + *****************************************************************************/ + + /// @brief Get data for a tracker at a given index in the TrackerStream callback. + /// @param p_TrackerIndex + /// @param p_TrackerData + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_ArgumentSizeMismatch if p_TrackerIndex is higher than the total number of trackers received from the tracker stream. + CORESDK_API SDKReturnCode CoreSdk_GetTrackerData(uint32_t p_TrackerIndex, TrackerData* p_TrackerData); + + /// @brief Get the number of available trackers. + /// @param p_NumberOfAvailableTrackers + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InternalError if the number of trackers found is higher than the defined MAX_NUMBER_OF_TRACKERS. + CORESDK_API SDKReturnCode CoreSdk_GetNumberOfAvailableTrackers(uint32_t* p_NumberOfAvailableTrackers); + + /// @brief Fill the given array with the IDs of available trackers. + /// The size of the given array must match the number of available trackers. + /// Note that the number of available trackers can change at any time. + /// @param p_IdsOfAvailableTrackers + /// @param p_NumberOfIdsThatFitInArray + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidArgument if p_NumberOfIdsThatFitInArray is zero or higher than the defined MAX_NUMBER_OF_TRACKERS, or + /// if p_IdsOfAvailableTrackers is a null array. + /// SDKReturnCode_ArgumentSizeMismatch if p_NumberOfIdsThatFitInArray does not match with the number of available trackers found. + CORESDK_API SDKReturnCode CoreSdk_GetIdsOfAvailableTrackers(TrackerId* p_IdsOfAvailableTrackers, uint32_t p_NumberOfIdsThatFitInArray); + + /// @brief Get the number of available trackers for a user with given Id. + /// @param p_NumberOfAvailableTrackers + /// @param p_UserId + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InternalError if the number of trackers found is higher than the defined MAX_NUMBER_OF_TRACKERS. + CORESDK_API SDKReturnCode CoreSdk_GetNumberOfAvailableTrackersForUserId(uint32_t* p_NumberOfAvailableTrackers, uint32_t p_UserId); + + /// @brief Fill the given array with the IDs of available trackers for a user with given Id. + /// The size of the given array must match the number of available trackers. + /// Note that the number of available trackers can change at any time. + /// @param p_IdsOfAvailableTrackers + /// @param p_UserId + /// @param p_NumberOfIdsThatFitInArray + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidArgument if p_NumberOfIdsThatFitInArray is zero or higher than the defined MAX_NUMBER_OF_TRACKERS, or + /// if p_IdsOfAvailableTrackers is a null array. + /// SDKReturnCode_ArgumentSizeMismatch if p_NumberOfIdsThatFitInArray does not match with the number of available trackers found. + CORESDK_API SDKReturnCode CoreSdk_GetIdsOfAvailableTrackersForUserId(TrackerId* p_IdsOfAvailableTrackers, uint32_t p_UserId, uint32_t p_NumberOfIdsThatFitInArray); + + /// @brief Get the number of available trackers for a user with given Id. + /// @param p_NumberOfAvailableTrackers + /// @param p_UserIndex + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InternalError if the number of trackers found is higher than the defined MAX_NUMBER_OF_TRACKERS. + CORESDK_API SDKReturnCode CoreSdk_GetNumberOfAvailableTrackersForUserIndex(uint32_t* p_NumberOfAvailableTrackers, uint32_t p_UserIndex); + + /// @brief Fill the given array with the IDs of available trackers for a user with given Id. + /// The size of the given array must match the number of available trackers. + /// Note that the number of available trackers can change at any time. + /// @param p_IdsOfAvailableTrackers + /// @param p_UserIndex + /// @param p_NumberOfIdsThatFitInArray + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidArgument if p_NumberOfIdsThatFitInArray is zero or higher than the defined MAX_NUMBER_OF_TRACKERS, or + /// if p_IdsOfAvailableTrackers is a null array. + /// SDKReturnCode_ArgumentSizeMismatch if p_NumberOfIdsThatFitInArray does not match with the number of available trackers found. + CORESDK_API SDKReturnCode CoreSdk_GetIdsOfAvailableTrackersForUserIndex(TrackerId* p_IdsOfAvailableTrackers, uint32_t p_UserIndex, uint32_t p_NumberOfIdsThatFitInArray); + + + + /// @brief Get data for the tracker with the given tracker Id. + /// @param p_TrackerId + /// @param p_TrackerData + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_DataNotAvailable if no tracker with the given p_TrackerId was found in the landscape. + CORESDK_API SDKReturnCode CoreSdk_GetDataForTracker_UsingTrackerId(TrackerId p_TrackerId, TrackerData* p_TrackerData); + + /// @brief Get data for the tracker with the given user id and type. + /// @param p_UserId + /// @param p_TrackerType + /// @param p_TrackerData + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_DataNotAvailable if no tracker with the given p_TrackerId and the given p_TrackerType was found in the landscape. + CORESDK_API SDKReturnCode CoreSdk_GetDataForTracker_UsingIdAndType(uint32_t p_UserId, uint32_t p_TrackerType, TrackerData* p_TrackerData); + + /// @brief Send data to Core for a tracker. + /// @param p_TrackerData + /// @param p_NumberOfTrackers amount of trackers for which we send the information + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_StubNullPointer if the stub has been reset but someone is trying to use it anyway. This usually happens after a shutdown of the SDK, + /// SDKReturnCode_NullPointer if the provided p_TrackerData is a null pointer, + /// SDKReturnCode_InvalidArgument if p_NumberOfTrackers is null or higher than the defined MAX_NUMBER_OF_TRACKERS. + CORESDK_API SDKReturnCode CoreSdk_SendDataForTrackers(const TrackerData* p_TrackerData, uint32_t p_NumberOfTrackers); + + /// @brief Sets the offset of a tracker for a specified user + /// @param p_UserId + /// @param p_TrackerOffset + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_StubNullPointer if the stub has been reset but someone is trying to use it anyway. This usually happens after a shutdown of the SDK, + /// SDKReturnCode_NotConnected if there is no connection to core. + CORESDK_API SDKReturnCode CoreSdk_SetTrackerOffset(uint32_t p_UserId, const TrackerOffset* p_TrackerOffset); + + /****************************************************************************** + * Gestures. + *****************************************************************************/ + + /// @brief Get the landscape data for all the gestures in the latest landscape. + /// @param p_LandscapeDataArray + /// @param p_ArraySize + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_ArgumentSizeMismatch if p_ArraySize is higher than the total number of gestures in the landscape stream. + CORESDK_API SDKReturnCode CoreSdk_GetGestureLandscapeData(GestureLandscapeData* p_LandscapeDataArray, uint32_t p_ArraySize); + + /// @brief The start data index allows you to get more data if there are more than MAX_GESTURE_DATA_CHUNK_SIZE gestures. + /// @param p_GestureStreamDataIndex + /// @param p_StartDataIndex + /// @param p_GestureProbabilitiesCollection + /// @return SDKReturnCode_Success if successful, or if there is no more data to get + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_ArgumentSizeMismatch if p_ArraySize is higher than the total number of gestures in the landscape stream. + CORESDK_API SDKReturnCode CoreSdk_GetGestureStreamData(uint32_t p_GestureStreamDataIndex, uint32_t p_StartDataIndex, GestureProbabilities* p_GestureProbabilitiesCollection); + + /****************************************************************************** + * Glove Calibration. + *****************************************************************************/ + + /// @brief Start the calibration for a glove with the id that is specified in the arguments. + /// @param p_CalibrationArgs + /// @param p_Result + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core + CORESDK_API SDKReturnCode CoreSdk_GloveCalibrationStart(GloveCalibrationArgs p_CalibrationArgs, bool* p_Result); + + /// @brief Stop the calibration for a glove with the id that is specified in the arguments. + /// @param p_CalibrationArgs + /// @param p_Result + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core + CORESDK_API SDKReturnCode CoreSdk_GloveCalibrationStop(GloveCalibrationArgs p_CalibrationArgs, bool* p_Result); + + /// @brief Finish the calibration for a glove with the id that is specified in the arguments. + /// @param p_CalibrationArgs + /// @param p_Result + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core + CORESDK_API SDKReturnCode CoreSdk_GloveCalibrationFinish(GloveCalibrationArgs p_CalibrationArgs, bool* p_Result); + + + /// @brief Get the number of steps in a glove calibration sequence. + /// @param p_CalibrationArgs + /// @param p_NumberOfSteps + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core + CORESDK_API SDKReturnCode CoreSdk_GloveCalibrationGetNumberOfSteps(GloveCalibrationArgs p_CalibrationArgs, uint32_t* p_NumberOfSteps); + + /// @brief Get information data about a specific calibration step for a glove. + /// @param p_CalibrationStepArgs + /// @param p_Data + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core + CORESDK_API SDKReturnCode CoreSdk_GloveCalibrationGetStepData(GloveCalibrationStepArgs p_CalibrationStepArgs, GloveCalibrationStepData* p_Data); + + /// @brief Start a specific calibration step for a glove. + /// @param p_CalibrationStepArgs + /// @param p_Result + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core + CORESDK_API SDKReturnCode CoreSdk_GloveCalibrationStartStep(GloveCalibrationStepArgs p_CalibrationStepArgs, bool* p_Result); + + + CORESDK_API SDKReturnCode CoreSdk_GetGloveCalibrationSize(uint32_t p_GloveId, uint32_t* p_Size); + + CORESDK_API SDKReturnCode CoreSdk_GetGloveCalibration(unsigned char* p_CalibrationBytes, uint32_t p_BytesLength); + + CORESDK_API SDKReturnCode CoreSdk_SetGloveCalibration(uint32_t p_GloveId, unsigned char* p_CalibrationBytes, SetGloveCalibrationReturnCode* p_Result); + + /****************************************************************************** + * Skeletal System. + *****************************************************************************/ + + /// @brief Get information about the final animated skeleton with given index. + /// @param p_SkeletonIndex + /// @param p_Info + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_ArgumentSizeMismatch if p_SkeletonIndex is higher than the total number of skeletons received from the skeleton stream. + CORESDK_API SDKReturnCode CoreSdk_GetSkeletonInfo(uint32_t p_SkeletonIndex, SkeletonInfo* p_Info); + + /// @brief Get data for the final animated skeleton with given index. + /// The size of the given array must match the nodes count. + /// Note that the nodes count can change at any time. + /// @param p_SkeletonIndex + /// @param p_Nodes + /// @param p_NodeCount + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_ArgumentSizeMismatch if p_SkeletonIndex is higher than the total number of skeletons received from the skeleton stream or + /// if the provided p_NodeCount is different from the node count of that skeleton. + CORESDK_API SDKReturnCode CoreSdk_GetSkeletonData(uint32_t p_SkeletonIndex, SkeletonNode* p_Nodes, uint32_t p_NodeCount); + + /// @brief Determine how the raw data should be moving + /// @param p_HandMotion + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + CORESDK_API SDKReturnCode CoreSdk_SetRawSkeletonHandMotion(HandMotion p_HandMotion); + + /// @brief Get the raw skeleton hand motion type + /// @param p_HandMotion + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + CORESDK_API SDKReturnCode CoreSdk_GetRawSkeletonHandMotion(HandMotion* p_HandMotion); + + /// @brief Get information about the raw skeleton data with given index. + /// @param p_SkeletonIndex + /// @param p_Info + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_ArgumentSizeMismatch if p_SkeletonIndex is higher than the total number of skeletons received from the skeleton stream. + CORESDK_API SDKReturnCode CoreSdk_GetRawSkeletonInfo(uint32_t p_SkeletonIndex, RawSkeletonInfo* p_Info); + + /// @brief Get data for the raw skeleton with given index. + /// The size of the given array must match the nodes count. + /// Note that the nodes count can change at any time. + /// @param p_SkeletonIndex + /// @param p_Nodes + /// @param p_NodeCount + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_ArgumentSizeMismatch if p_SkeletonIndex is higher than the total number of skeletons received from the skeleton stream or + /// if the provided p_NodeCount is different from the node count of that skeleton. + CORESDK_API SDKReturnCode CoreSdk_GetRawSkeletonData(uint32_t p_SkeletonIndex, SkeletonNode* p_Nodes, uint32_t p_NodeCount); + + /// @brief Identifies if the glove associated to the skeleton with given id and side is a haptic one. + /// @param p_SkeletonId + /// @param p_HandType + /// @param p_IsHaptics + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_DataNotAvailable if the requested glove is not found or if the skeleton does not have any user associated to it, + /// SDKReturnCode_InvalidArgument if the provided p_HandType is neither Left nor Right. + CORESDK_API SDKReturnCode CoreSdk_DoesSkeletonGloveSupportHaptics(uint32_t p_SkeletonId, Side p_HandType, bool* p_IsHaptics); + /****************************************************************************** + * Skeletal System Setup. + *****************************************************************************/ + + /// @brief Create a new SkeletonSetup with the given information and returns the index on which it is saved. + /// @param p_Skeleton The SkeletonSetup information used by this skeleton. + /// @param p_SkeletonSetupIndex The index which is used by this SkeletonSetup. + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_ArgumentSizeMismatch if the maximum number of temporary skeletons per session has been reached, + /// when this happens the user can either load or clear a temporary skeleton in order to create a new skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_CreateSkeletonSetup(SkeletonSetupInfo p_Skeleton, uint32_t* p_SkeletonSetupIndex); + + /// @brief Add a node to a SkeletonSetup at a given index. + /// @param p_SkeletonSetupIndex + /// @param p_Node + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_AddNodeToSkeletonSetup(uint32_t p_SkeletonSetupIndex, NodeSetup p_Node); + + /// @brief Add a chain to a SkeletonSetup at a given index. + /// @param p_SkeletonSetupIndex + /// @param p_Chain + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_AddChainToSkeletonSetup(uint32_t p_SkeletonSetupIndex, ChainSetup p_Chain); + + /// @brief Add a collider to a SkeletonSetup at a given index. + /// @param p_SkeletonSetupIndex + /// @param p_Collider + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_AddColliderToSkeletonSetup(uint32_t p_SkeletonSetupIndex, ColliderSetup p_Collider); + + /// @brief Add a mesh setup to a SkeletonSetup at a given index. + /// @param p_SkeletonSetupIndex + /// @param p_NodeID + /// @param p_MeshSetupIndex + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_AddMeshSetupToSkeletonSetup(uint32_t p_SkeletonSetupIndex, uint32_t p_NodeID, uint32_t* p_MeshSetupIndex); + + /// @brief Add a Vertex to a mesh setup at a given index. + /// @param p_SkeletonSetupIndex + /// @param p_MeshSetupIndex + /// @param p_Vertex + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_AddVertexToMeshSetup(uint32_t p_SkeletonSetupIndex, uint32_t p_MeshSetupIndex, Vertex p_Vertex); + + + /// @brief Add a Triangle to a mesh setup at a given index. + /// @param p_SkeletonSetupIndex + /// @param p_MeshSetupIndex + /// @param p_Triangle + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_AddTriangleToMeshSetup(uint32_t p_SkeletonSetupIndex, uint32_t p_MeshSetupIndex, Triangle p_Triangle); + + /// @brief Overwrite an existing SkeletonSetup at a given index. + /// This will remove all chains and nodes from the current skeleton at this index. + /// @param p_SkeletonSetupIndex + /// @param p_SkeletonSetup + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup, + /// SDKReturnCode_NullPointer if the skeleton setup was not correctly saved, thus the new skeleton setup is a null pointer. + CORESDK_API SDKReturnCode CoreSdk_OverwriteSkeletonSetup(uint32_t p_SkeletonSetupIndex, SkeletonSetupInfo p_SkeletonSetup); + + /// @brief Overwrite a chain in a SkeletonSetup, the chain to be overwritten is idenfified by p_Chain.id. + /// @param p_SkeletonSetupIndex The SkeletonSetup's index, not to be confused by the Chain's ID. + /// @param p_Chain The chain to replace the matching (via id) chain with. + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_OverwriteChainToSkeletonSetup(uint32_t p_SkeletonSetupIndex, ChainSetup p_Chain); + + /// @brief Overwrite a node in a SkeletonSetup, the node to be overwritten is identified by p_Node.id. + /// @param p_SkeletonSetupIndex The SkeletonSetup's index, not to be confused by the Chain's ID. + /// @param p_Node The node to replace the matching (via id) node with. + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_OverwriteNodeToSkeletonSetup(uint32_t p_SkeletonSetupIndex, NodeSetup p_Node); + + /// @brief Get the skeleton info (number of nodes and chains) for a SkeletonSetup at a given index. + /// @param p_SkeletonSetupIndex + /// @param p_SkeletonInfo + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_GetSkeletonSetupArraySizes(uint32_t p_SkeletonSetupIndex, SkeletonSetupArraySizes* p_SkeletonInfo); + + /// @brief Allocate chains for a SkeletonSetup at a given index. + /// @param p_SkeletonSetupIndex + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_StubNullPointer if the stub has been reset but someone is trying to use it anyway. This usually happens after a shutdown of the SDK, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_AllocateChainsForSkeletonSetup(uint32_t p_SkeletonSetupIndex); + CORESDK_API SDKReturnCode CoreSdk_PrepareSkeletonSetup(uint32_t p_SkeletonSetupIndex); + + /// @brief Get setup info for a SkeletonSetup at a given index. + /// @param p_SkeletonSetupIndex + /// @param p_SDK + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_GetSkeletonSetupInfo(uint32_t p_SkeletonSetupIndex, SkeletonSetupInfo* p_SDK); + + /// @brief Get setup chains for a SkeletonSetup at a given index. + /// The size of the given array must match the chains count. + /// Note that the chains count can change at any time. + /// @param p_SkeletonSetupIndex + /// @param p_SDK + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_NullPointer if the provided p_SDK (used to store the setup chains) is a null pointer, it should be initialized to + /// an array with size equal to the chains count, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + /// @deprecated Use GetSkeletonSetupChainsArray() instead + [[deprecated ("Use GetSkeletonSetupChainsArray() instead")]] + CORESDK_API SDKReturnCode CoreSdk_GetSkeletonSetupChains(uint32_t p_SkeletonSetupIndex, ChainSetup* p_SDK); + + /// @brief Get setup chains for a SkeletonSetup at a given index. + /// The size of the given array must match the chains count. + /// Note that the chains count can change at any time. + /// @param p_SkeletonSetupIndex + /// @param p_ChainSetupArray + /// @param p_ArraySize + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_NullPointer if the provided p_ChainSetupArray (used to store the setup chains) is a null pointer, it should be initialized to + /// an array with size equal to the chains count, + /// SDKReturnCode_ArgumentSizeMismatch if the provided p_ArraySize is not equal to the chains count, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_GetSkeletonSetupChainsArray(uint32_t p_SkeletonSetupIndex, ChainSetup* p_ChainSetupArray, uint32_t p_ArraySize); + + /// @brief Get setup nodes for a SkeletonSetup at a given index. + /// The size of the given array must match the nodes count. + /// Note that the nodes count can change at any time. + /// @param p_SkeletonSetupIndex + /// @param p_SDK + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_NullPointer if the provided p_SDK (used to store the setup nodes) is a null pointer, it should be initialized to + /// an array with size equal to the nodes count, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + /// @deprecated Use GetSkeletonSetupNodesArray() instead + [[deprecated ("Use GetSkeletonSetupNodesArray() instead")]] + CORESDK_API SDKReturnCode CoreSdk_GetSkeletonSetupNodes(uint32_t p_SkeletonSetupIndex, NodeSetup* p_SDK); + + /// @brief Get setup nodes for a SkeletonSetup at a given index. + /// The size of the given array must match the nodes count. + /// Note that the nodes count can change at any time. + /// @param p_SkeletonSetupIndex + /// @param p_NodeSetupArray + /// @param p_ArraySize + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_NullPointer if the provided p_SDK (used to store the setup nodes) is a null pointer, it should be initialized to + /// an array with size equal to the nodes count, + /// SDKReturnCode_ArgumentSizeMismatch if the provided p_ArraySize is not equal to the nodes count, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_GetSkeletonSetupNodesArray(uint32_t p_SkeletonSetupIndex, NodeSetup* p_NodeSetupArray, uint32_t p_ArraySize); + + /// @brief Get setup colliders for a SkeletonSetup at a given index. + /// The size of the given array must match the colliders count. + /// Note that the colliders count can change at any time. + /// @param p_SkeletonSetupIndex + /// @param p_SDK + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_NullPointer if the provided p_SDK (used to store the setup colliders) is a null pointer, it should be initialized to + /// an array with size equal to the colliders count, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + /// @deprecated + [[deprecated]] + CORESDK_API SDKReturnCode CoreSdk_GetSkeletonSetupColliders(uint32_t p_SkeletonSetupIndex, ColliderSetup* p_SDK); + + /// @brief Get the amount of nodes for the raw skeleton with given id + /// @param p_GloveId + /// @param p_NodeCount + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidID if the provided glove id does not match any skeleton in core + CORESDK_API SDKReturnCode CoreSdk_GetRawSkeletonNodeCount(uint32_t p_GloveId, uint32_t& p_NodeCount); + + /// @brief Get the information for nodes of the raw skeleton with given id + /// The size of the given array p_NodeInfo must match the node count retrieved from CoreSdk_GetRawSkeletonNodeCount. + /// @param p_GloveId + /// @param p_NodeInfo + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidID if the provided glove id does not match any skeleton in core + /// @deprecated Use CoreSdk_GetRawSkeletonNodeInfoArray instead + [[deprecated("Use CoreSdk_GetRawSkeletonNodeInfoArray instead")]] + CORESDK_API SDKReturnCode CoreSdk_GetRawSkeletonNodeInfo(uint32_t p_GloveId, NodeInfo* p_NodeInfo); + + + /// @brief Get the information for nodes of the raw skeleton with given id + /// The size of the given array p_NodeInfo and p_NodeCount must match the node count retrieved from CoreSdk_GetRawSkeletonNodeCount. + /// @param p_GloveId + /// @param p_NodeInfoArray + /// @param p_ArraySize + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_InvalidID if the provided glove id does not match any skeleton in core, + /// SDKReturnCode_ArgumentSizeMismatch if p_ArraySize does not match node count retrieved from CoreSdk_GetRawSkeletonNodeCount. + CORESDK_API SDKReturnCode CoreSdk_GetRawSkeletonNodeInfoArray(uint32_t p_GloveId, NodeInfo* p_NodeInfoArray, uint32_t p_ArraySize); + + /// @brief Sends a skeleton setup to core to become a skeleton upon which data is applied. + /// Returns the skeleton ID of the loaded skeleton, this ID is used to identify the skeleton in the SkeletonStreamCallback. + /// Loading a skeleton into core for retargetting removes it from the temporary skeletons array, thus causing + /// the p_SkeletonSetupIndex to no longer be valid after this call. + /// @param p_SkeletonSetupIndex The SkeletonSetup's index, will become invalid after this function. + /// @param p_SkeletonId This is the ID of the loaded skeleton. + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_StubNullPointer if the stub has been reset but someone is trying to use it anyway. This usually happens after a shutdown of the SDK, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_LoadSkeleton(uint32_t p_SkeletonSetupIndex, uint32_t* p_SkeletonId); + + /// @brief Unload a skeleton with a certain ID from Manus Core, so data will no longer be generated for it. + /// @param p_SkeletonId The Skeleton's ID which is assigned by Manus Core. + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_StubNullPointer if the stub has been reset but someone is trying to use it anyway. This usually happens after a shutdown of the SDK, + /// SDKReturnCode_NotConnected if there is no connection to core. + CORESDK_API SDKReturnCode CoreSdk_UnloadSkeleton(uint32_t p_SkeletonId); + + /// @brief Save a SkeletonSetup at a given index and sessionId into Manus Core for use in the Dev Tools. + /// p_IsSkeletonModified is intended to be used mainly by the Dev Tools, it is set to true after saving any modification to the skeleton, + /// this triggers the OnSyStemCallback which is used in the SDK to be notified about a change to its temporary skeletons. + /// @param p_SkeletonSetupIndex + /// @param p_SessionId + /// @param p_IsSkeletonModified + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_StubNullPointer if the stub has been reset but someone is trying to use it anyway. This usually happens after a shutdown of the SDK, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_SaveTemporarySkeleton(uint32_t p_SkeletonSetupIndex, uint32_t p_SessionId, bool p_IsSkeletonModified); + + /// @brief Send a temporary skeleton to Core to compress it (convert it into an array of bytes) and then get the compressed data back in the SDK, + /// the skeleton compressed data are stored internally. It returns the length of the bytes array for correct memory allocation. + /// This function must be called before CoreSdk_GetCompressedTemporarySkeletonData. + /// @param p_SkeletonSetupIndex + /// @param p_SessionId + /// @param p_TemporarySkeletonLengthInBytes + /// @return + CORESDK_API SDKReturnCode CoreSdk_CompressTemporarySkeletonAndGetSize(uint32_t p_SkeletonSetupIndex, uint32_t p_SessionId, uint32_t* p_TemporarySkeletonLengthInBytes); + + /// @brief Get the compressed temporary skeleton data which are stored internally when calling function CoreSdk_CompressTemporarySkeletonAndGetSize. + /// The size of p_TemporarySkeletonData must match with p_TemporarySkeletonLengthInBytes. + /// @param p_TemporarySkeletonData + /// @param p_TemporarySkeletonLengthInBytes + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_StubNullPointer if the stub has been reset but someone is trying to use it anyway. This usually happens after a shutdown of the SDK, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_GetCompressedTemporarySkeletonData(unsigned char* p_TemporarySkeletonData, uint32_t p_TemporarySkeletonLengthInBytes); + + /// @brief Get a SkeletonSetup at a given index and sessionId from Manus Core. + /// This function does NOT return a loaded skeleton! + /// @param p_SkeletonSetupIndex + /// @param p_SessionId + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_StubNullPointer if the stub has been reset but someone is trying to use it anyway. This usually happens after a shutdown of the SDK, + CORESDK_API SDKReturnCode CoreSdk_GetTemporarySkeleton(uint32_t p_SkeletonSetupIndex, uint32_t p_SessionId); + + /// @brief Send the compressed data (an array of bytes) retrieved from a file to Core to be decompressed and converted back into a temporary skeleton. + /// The received temporary skeleton is saved internally with the given index and to the given session id. + /// The size of p_TemporarySkeletonData must match with p_TemporarySkeletonLengthInBytes. + /// @param p_SkeletonSetupIndex + /// @param p_SessionId + /// @param p_TemporarySkeletonData + /// @param p_TemporarySkeletonLengthInBytes + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_StubNullPointer if the stub has been reset but someone is trying to use it anyway. This usually happens after a shutdown of the SDK, + CORESDK_API SDKReturnCode CoreSdk_GetTemporarySkeletonFromCompressedData(uint32_t p_SkeletonSetupIndex, uint32_t p_SessionId, unsigned char* p_TemporarySkeletonData, uint32_t p_TemporarySkeletonLengthInBytes); + + /// @brief Clear a SkeletonSetup at a given index and sessionId in Manus Core and the SDK. + /// The given setup index will no longer be valid after this call. + /// @param p_SkeletonSetupIndex + /// @param p_SessionId + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_StubNullPointer if the stub has been reset but someone is trying to use it anyway. This usually happens after a shutdown of the SDK, + /// SDKReturnCode_InvalidID if the provided p_SkeletonSetupIndex does not correspond to any skeleton setup. + CORESDK_API SDKReturnCode CoreSdk_ClearTemporarySkeleton(uint32_t p_SkeletonSetupIndex, uint32_t p_SessionId); + + /// @brief Clear all temporary skeletons associated to the current session both in the sdk and core. + /// All skeleton setups will no longer be valid after this call. + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_NotConnected if there is no connection to core, + /// SDKReturnCode_StubNullPointer if the stub has been reset but someone is trying to use it anyway. This usually happens after a shutdown of the SDK, + CORESDK_API SDKReturnCode CoreSdk_ClearAllTemporarySkeletons(); + + /// @brief Get temporary skeleton counts for all sessions connected to Core. + /// @param p_temporarySkeletonSessionsData + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_StubNullPointer if the stub has been reset but someone is trying to use it anyway. This usually happens after a shutdown of the SDK, + /// SDKReturnCode_NotConnected if there is no connection to core. + CORESDK_API SDKReturnCode CoreSdk_GetTemporarySkeletonCountForAllSessions(TemporarySkeletonCountForAllSessions* p_TemporarySkeletonCountForAllSessions); + + /// @brief Get information (name and index) for the SkeletonSetups of the specified session. + /// @param p_SessionId + /// @param p_temporarySkeletonSessionsData + /// @return SDKReturnCode_Success if successful, + /// SDKReturnCode_SdkNotAvailable if the Core SDK is not available, + /// SDKReturnCode_StubNullPointer if the stub has been reset but someone is trying to use it anyway. This usually happens after a shutdown of the SDK, + /// SDKReturnCode_NotConnected if there is no connection to core. + CORESDK_API SDKReturnCode CoreSdk_GetTemporarySkeletonsInfoForSession(uint32_t p_SessionId, TemporarySkeletonsInfoForSession* p_TemporarySkeletonsInfoForSession); + +#ifdef __cplusplus +} // extern "C" +#endif + +// Close the Doxygen group. +/** @} */ + +#endif // #ifndef H_MANUS_SDK diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/include/ManusSDKTypeInitializers.h b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/include/ManusSDKTypeInitializers.h new file mode 100644 index 0000000..939bfd5 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/include/ManusSDKTypeInitializers.h @@ -0,0 +1,397 @@ +#ifndef __MANUS_SDK_TYPE_INITIALIZERS_H__ +#define __MANUS_SDK_TYPE_INITIALIZERS_H__ + +// Set up a Doxygen group. +/** @addtogroup ManusSDKTypeInitializers + * @{ + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ManusSDKTypes.h" + +#include + +#ifdef MANUS_SDK_STATIC + #define CORESDK_API +#else + #ifdef _WIN32 + // MANUS_SDK_EXPORTS defines if the functions are exported to or + // imported from a DLL. + #ifdef MANUS_SDK_EXPORTS + #define CORESDK_API __declspec(dllexport) + #else + #define CORESDK_API __declspec(dllimport) + #endif + #elif defined(__linux__) + #define CORESDK_API + #else + #error Unrecognized platform. + #endif +#endif + + ///@brief All these initializers take a preallocated instance of their related structure and set default values. + + /// @brief Initializer for a ManusVec3 struct + /// @param p_Val + CORESDK_API void ManusVec3_Init(ManusVec3* p_Val); + + /// @brief Initializer for a ManusVec2 struct + /// @param p_Val + CORESDK_API void ManusVec2_Init(ManusVec2* p_Val); + + /// @brief Initializer for a ManusQuaternion struct + /// @param p_Val + CORESDK_API void ManusQuaternion_Init(ManusQuaternion* p_Val); + + /// @brief Initializer for a ManusTransform struct + /// @param p_Val + CORESDK_API void ManusTransform_Init(ManusTransform* p_Val); + + /// @brief Initializer for a Color struct + /// @param p_Val + CORESDK_API void Color_Init(Color* p_Val); + + /// @brief Initializer for a ManusTimestampInfo struct + /// @param p_Val + CORESDK_API void ManusTimestampInfo_Init(ManusTimestampInfo* p_Val); + + /// @brief Initializer for a ManusTimestamp struct + /// @param p_Val + CORESDK_API void ManusTimestamp_Init(ManusTimestamp* p_Val); + + /// @brief Initializer for a IMUCalibrationInfo struct + /// @param p_Val + CORESDK_API void IMUCalibrationInfo_Init(IMUCalibrationInfo* p_Val); + + /// @brief Initializer for a Version struct + /// @param p_Val + CORESDK_API void Version_Init(Version* p_Val); + + /// @brief Initializer for a FirmwareVersion struct + /// @param p_Val + CORESDK_API void FirmwareVersion_Init(FirmwareVersion* p_Val); + + /// @brief Initializer for a ManusVersion struct + /// @param p_Val + CORESDK_API void ManusVersion_Init(ManusVersion* p_Val); + + /// @brief Initializer for a TrackerId struct + /// @param p_Val + CORESDK_API void TrackerId_Init(TrackerId* p_Val); + + /// @brief Initializer for a TrackerData struct + /// @param p_Val + CORESDK_API void TrackerData_Init(TrackerData* p_Val); + + /// @brief Initializer for a ManusHost struct + /// @param p_Val + CORESDK_API void ManusHost_Init(ManusHost* p_Val); + + /// @brief Initializer for a SkeletonNode struct + /// @param p_Val + CORESDK_API void SkeletonNode_Init(SkeletonNode* p_Val); + + /// @brief Initializer for a SkeletonInfo struct + /// @param p_Val + CORESDK_API void SkeletonInfo_Init(SkeletonInfo* p_Val); + + /// @brief Initializer for a RawSkeletonInfo struct + /// @param p_Val + CORESDK_API void RawSkeletonInfo_Init(RawSkeletonInfo* p_Val); + + /// @brief Initializer for a SkeletonStreamInfo struct + /// @param p_Val + CORESDK_API void SkeletonStreamInfo_Init(SkeletonStreamInfo* p_Val); + + /// @brief Initializer for a ErgonomicsData struct + /// @param p_Val + CORESDK_API void ErgonomicsData_Init(ErgonomicsData* p_Val); + + /// @brief Initializer for a ErgonomicsStream struct + /// @param p_Val + CORESDK_API void ErgonomicsStream_Init(ErgonomicsStream* p_Val); + + /// @brief Initializer for a DongleLandscapeData struct + /// @param p_Val + CORESDK_API void DongleLandscapeData_Init(DongleLandscapeData* p_Val); + + /// @brief Initializer for a GloveLandscapeData struct + /// @param p_Val + CORESDK_API void GloveLandscapeData_Init(GloveLandscapeData* p_Val); + + /// @brief Initializer for a Measurement struct + /// @param p_Val + CORESDK_API void Measurement_Init(Measurement* p_Val); + + /// @brief Initializer for a TrackerOffset struct + /// @param p_Val + CORESDK_API void TrackerOffset_Init(TrackerOffset* p_Val); + + /// @brief Initializer for a ExtraTrackerOffset struct + /// @param p_Val + CORESDK_API void ExtraTrackerOffset_Init(ExtraTrackerOffset* p_Val); + + /// @brief Initializer for a TrackerLandscapeData struct + /// @param p_Val + CORESDK_API void TrackerLandscapeData_Init(TrackerLandscapeData* p_Val); + + /// @brief Initializer for a UserProfileLandscapeData struct + /// @param p_Val + CORESDK_API void UserProfileLandscapeData_Init(UserProfileLandscapeData* p_Val); + + /// @brief Initializer for a UserLandscapeData struct + /// @param p_Val + CORESDK_API void UserLandscapeData_Init(UserLandscapeData* p_Val); + + /// @brief Initializer for a SkeletonLandscapeData struct + /// @param p_Val + CORESDK_API void SkeletonLandscapeData_Init(SkeletonLandscapeData* p_Val); + + /// @brief Initializer for a DeviceLandscape struct + /// @param p_Val + CORESDK_API void DeviceLandscape_Init(DeviceLandscape* p_Val); + + /// @brief Initializer for a UserLandscape struct + /// @param p_Val + CORESDK_API void UserLandscape_Init(UserLandscape* p_Val); + + /// @brief Initializer for a SkeletonLandscape struct + /// @param p_Val + CORESDK_API void SkeletonLandscape_Init(SkeletonLandscape* p_Val); + + /// @brief Initializer for a TrackerLandscape struct + /// @param p_Val + CORESDK_API void TrackerLandscape_Init(TrackerLandscape* p_Val); + + /// @brief Initializer for a LicenseInfo struct + /// @param p_Val + CORESDK_API void LicenseInfo_Init(LicenseInfo* p_Val); + + /// @brief Initializer for a SettingsLandscape struct + /// @param p_Val + CORESDK_API void SettingsLandscape_Init(SettingsLandscape* p_Val); + + /// @brief Initializer for a TimecodeInterface struct + /// @param p_Val + CORESDK_API void TimecodeInterface_Init(TimecodeInterface* p_Val); + + /// @brief Initializer for a TimeLandscape struct + /// @param p_Val + CORESDK_API void TimeLandscape_Init(TimeLandscape* p_Val); + + /// @brief Initializer for a GestureLandscapeData struct + /// @param p_Val + CORESDK_API void GestureLandscapeData_Init(GestureLandscapeData* p_Val); + + /// @brief Initializer for a NetDeviceLandscapeData struct + /// @param p_Val + CORESDK_API void NetDeviceLandscapeData_Init(NetDeviceLandscapeData* p_Val); + + /// @brief Initializer for a NetDevicesLandscape struct + /// @param p_Val + CORESDK_API void NetDevicesLandscape_Init(NetDevicesLandscape* p_Val); + + /// @brief Initializer for a Landscape struct + /// @param p_Val + CORESDK_API void Landscape_Init(Landscape* p_Val); + + /// @brief Initializer for a SkeletonSetupArraySizes struct + /// @param p_Val + CORESDK_API void SkeletonSetupArraySizes_Init(SkeletonSetupArraySizes* p_Val); + + /// @brief Initializer for a NodeSettingsIK struct + /// @param p_Val + CORESDK_API void NodeSettingsIK_Init(NodeSettingsIK* p_Val); + + /// @brief Initializer for a NodeSettingsFoot struct + /// @param p_Val + CORESDK_API void NodeSettingsFoot_Init(NodeSettingsFoot* p_Val); + + /// @brief Initializer for a NodeSettingsRotationOffset struct + /// @param p_Val + CORESDK_API void NodeSettingsRotationOffset_Init(NodeSettingsRotationOffset* p_Val); + + /// @brief Initializer for a NodeSettingsLeaf struct + /// @param p_Val + CORESDK_API void NodeSettingsLeaf_Init(NodeSettingsLeaf* p_Val); + + /// @brief Initializer for a NodeSettings struct + /// @param p_Val + CORESDK_API void NodeSettings_Init(NodeSettings* p_Val); + + /// @brief Initializer for a NodeSetup struct + /// @param p_Val + CORESDK_API void NodeSetup_Init(NodeSetup* p_Val); + + /// @brief Initializer for a ChainSettingsPelvis struct + /// @param p_Val + CORESDK_API void ChainSettingsPelvis_Init(ChainSettingsPelvis* p_Val); + + /// @brief Initializer for a ChainSettingsLeg struct + /// @param p_Val + CORESDK_API void ChainSettingsLeg_Init(ChainSettingsLeg* p_Val); + + /// @brief Initializer for a ChainSettingsSpine struct + /// @param p_Val + CORESDK_API void ChainSettingsSpine_Init(ChainSettingsSpine* p_Val); + + /// @brief Initializer for a ChainSettingsNeck struct + /// @param p_Val + CORESDK_API void ChainSettingsNeck_Init(ChainSettingsNeck* p_Val); + + /// @brief Initializer for a ChainSettingsHead struct + /// @param p_Val + CORESDK_API void ChainSettingsHead_Init(ChainSettingsHead* p_Val); + + /// @brief Initializer for a ChainSettingsArm struct + /// @param p_Val + CORESDK_API void ChainSettingsArm_Init(ChainSettingsArm* p_Val); + + /// @brief Initializer for a ChainSettingsShoulder struct + /// @param p_Val + CORESDK_API void ChainSettingsShoulder_Init(ChainSettingsShoulder* p_Val); + + /// @brief Initializer for a ChainSettingsFinger struct + /// @param p_Val + CORESDK_API void ChainSettingsFinger_Init(ChainSettingsFinger* p_Val); + + /// @brief Initializer for a ChainSettingsHand struct + /// @param p_Val + CORESDK_API void ChainSettingsHand_Init(ChainSettingsHand* p_Val); + + /// @brief Initializer for a ChainSettingsFoot struct + /// @param p_Val + CORESDK_API void ChainSettingsFoot_Init(ChainSettingsFoot* p_Val); + + /// @brief Initializer for a ChainSettingsToe struct + /// @param p_Val + CORESDK_API void ChainSettingsToe_Init(ChainSettingsToe* p_Val); + + /// @brief Initializer for a ChainSettings struct + /// @param p_Val + CORESDK_API void ChainSettings_Init(ChainSettings* p_Val); + + /// @brief Initializer for a ChainSetup struct + /// @param p_Val + CORESDK_API void ChainSetup_Init(ChainSetup* p_Val); + + /// @brief Initializer for a SphereColliderSetup struct + /// @param p_Val + CORESDK_API void SphereColliderSetup_Init(SphereColliderSetup* p_Val); + + /// @brief Initializer for a CapsuleColliderSetup struct + /// @param p_Val + CORESDK_API void CapsuleColliderSetup_Init(CapsuleColliderSetup* p_Val); + + /// @brief Initializer for a BoxColliderSetup struct + /// @param p_Val + CORESDK_API void BoxColliderSetup_Init(BoxColliderSetup* p_Val); + + /// @brief Initializer for a ColliderSetup struct + /// @param p_Val + CORESDK_API void ColliderSetup_Init(ColliderSetup* p_Val); + + /// @brief Initializer for a Vertex Weight struct + /// @param p_Val + CORESDK_API void Weight_Init(Weight* p_Val); + + /// @brief Initializer for a Vertex struct + /// @param p_Val + CORESDK_API void Vertex_Init(Vertex* p_Val); + + /// @brief Initializer for a Triangle struct + /// @param p_Val + CORESDK_API void Triangle_Init(Triangle* p_Val); + + /// @brief Initializer for a SkeletonTargetUserData struct + /// @param p_Val + CORESDK_API void SkeletonTargetUserData_Init(SkeletonTargetUserData* p_Val); + + /// @brief Initializer for a SkeletonTargetAnimationData struct + /// @param p_Val + CORESDK_API void SkeletonTargetAnimationData_Init(SkeletonTargetAnimationData* p_Val); + + /// @brief Initializer for a SkeletonTargetGloveData struct + /// @param p_Val + CORESDK_API void SkeletonTargetGloveData_Init(SkeletonTargetGloveData* p_Val); + + /// @brief Initializer for a SkeletonSettings struct + /// @param p_Val + CORESDK_API void SkeletonSettings_Init(SkeletonSettings* p_Val); + + /// @brief Initializer for a SkeletonSetupInfo struct + /// @param p_Val + CORESDK_API void SkeletonSetupInfo_Init(SkeletonSetupInfo* p_Val); + + /// @brief Initializer for a TemporarySkeletonInfo struct + /// @param p_Val + CORESDK_API void TemporarySkeletonInfo_Init(TemporarySkeletonInfo* p_Val); + + /// @brief Initializer for a TemporarySkeletonsForSession struct + /// @param p_Val + CORESDK_API void TemporarySkeletonsInfoForSession_Init(TemporarySkeletonsInfoForSession* p_Val); + + /// @brief Initializer for a TemporarySkeletonCountForSession struct + /// @param p_Val + CORESDK_API void TemporarySkeletonCountForSession_Init(TemporarySkeletonCountForSession* p_Val); + + /// @brief Initializer for a TemporarySkeletonCountForAllSessions struct + /// @param p_Val + CORESDK_API void TemporarySkeletonCountForAllSessions_Init(TemporarySkeletonCountForAllSessions* p_Val); + + /// @brief Initializer for a TemporarySkeletonSessionsData struct + /// @param p_Val + CORESDK_API void TemporarySkeletonSessionsData_Init(TemporarySkeletonSessionsData* p_Val); + + /// @brief Initializer for a SystemMessage struct + /// @param p_Val + CORESDK_API void SystemMessage_Init(SystemMessage* p_Val); + + /// @brief Initializer for a CoordinateSystemVUH struct + /// @param p_Val + CORESDK_API void CoordinateSystemVUH_Init(CoordinateSystemVUH* p_Val); + + /// @brief Initializer for a CoordinateSystemDirection struct + /// @param p_Val + CORESDK_API void CoordinateSystemDirection_Init(CoordinateSystemDirection* p_Val); + + /// @brief Initializer for a NodeInfo struct + /// @param p_Val + CORESDK_API void NodeInfo_Init(NodeInfo* p_Val); + + /// @brief Initializer for a GestureStreamInfo struct + /// @param p_Val + CORESDK_API void GestureStreamInfo_Init(GestureStreamInfo* p_Val); + + /// @brief Initializer for a GestureProbabilities struct + /// @param p_Val + CORESDK_API void GestureProbabilities_Init(GestureProbabilities* p_Val); + + /// @brief Initializer for a GestureProbability struct + /// @param p_Val + CORESDK_API void GestureProbability_Init(GestureProbability* p_Val); + + /// @brief Initializer for a GloveCalibrationArgs struct + /// @param p_Val + CORESDK_API void GloveCalibrationArgs_Init(GloveCalibrationArgs* p_Val); + + /// @brief Initializer for a GloveCalibrationStepArgs struct + /// @param p_Val + CORESDK_API void GloveCalibrationStepArgs_Init(GloveCalibrationStepArgs* p_Val); + + /// @brief Initializer for a GloveCalibrationStepData struct + /// @param p_Val + CORESDK_API void GloveCalibrationStepData_Init(GloveCalibrationStepData* p_Val); + +#ifdef __cplusplus +} // extern "C" +#endif + +// Close the Doxygen group. +/** @} */ + +#endif // #ifndef MANUS_SDK_TYPE_INITIALIZERS_H diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/include/ManusSDKTypes.h b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/include/ManusSDKTypes.h new file mode 100644 index 0000000..7c45344 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/include/ManusSDKTypes.h @@ -0,0 +1,1717 @@ +#ifndef __MANUS_SDK_TYPES_H__ +#define __MANUS_SDK_TYPES_H__ + +// Set up a Doxygen group. +/** @addtogroup ManusSdkTypes + * @{ + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/****************************************************************************** + * Preprocessor defines. + *****************************************************************************/ + +/// @brief Used to descriptively refer to the number of fingers on a hand. +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define NUM_FINGERS_ON_HAND 5 + +/// @brief Used to descriptively refer to the number of flex sensor segments. +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define NUM_FLEX_SEGMENTS_PER_FINGER 2 + +/// @brief Used to descriptively refer to the maximum IMUs count on a glove. +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_NUM_IMUS_ON_GLOVE (NUM_FINGERS_ON_HAND + 1) + +/// @brief Used to descriptively refer to the maximum number of Polygon users. +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_USERS 16 // we should never reach that + +/// @brief Used to descriptively refer to the maximum user name length. +/// Used with arrays to make them more descriptive than simply using the +/// number, and to make changing the number easier and safer. +#define MAX_NUM_CHARS_IN_USERNAME 64 + +/// @brief Used to descriptively refer to the maximum body measurement name length. +/// Used with arrays to make them more descriptive than simply using the +/// number, and to make changing the number easier and safer. +#define MAX_NUM_CHARS_IN_MEASUREMENT 64 + +/// Host name length is based on a post made here: +/// https://community.cisco.com/t5/other-network-architecture/maximum-length-hostname/td-p/529327 +/// Which in turn was based on: https://www.ietf.org/rfc/rfc1035.txt +/// @brief Used to descriptively refer to the maximum host name length. +/// Used with arrays to make them more descriptive than simply using the +/// number, and to make changing the number easier and safer. +#define MAX_NUM_CHARS_IN_HOST_NAME 256 + +/// @brief Used to descriptively refer to the maximum IP address length. +/// Used with arrays to make them more descriptive than simply using the +/// number, and to make changing the number easier and safer. +/// It is based on the length of an IPv6 address. Example: +/// "2001:0db8:0000:0000:0000:8a2e:0370:7334". +#define MAX_NUM_CHARS_IN_IP_ADDRESS 40 + +/// @brief Used to descriptively refer to the maximum tracker name length. +/// Used with arrays to make them more descriptive than simply using the +/// number, and to make changing the number easier and safer. +#define MAX_NUM_CHARS_IN_TRACKER_ID 32 + +/// @brief Used to descriptively refer to the maximum tracker manufacturer length. +/// Used with arrays to make them more descriptive than simply using the +/// number, and to make changing the number easier and safer. +#define MAX_NUM_CHARS_IN_TRACKER_MANUFACTURER 32 + +/// @brief Used to descriptively refer to the maximum tracker manufacturer length. +/// Used with arrays to make them more descriptive than simply using the +/// number, and to make changing the number easier and safer. +#define MAX_NUM_CHARS_IN_TRACKER_PRODUCTNAME 32 + +/// @brief Used to descriptively refer to the maximum target name length. +/// Used with arrays to make them more descriptive than simply using the +/// number, and to make changing the number easier and safer. +#define MAX_NUM_CHARS_IN_TARGET_ID 32 + +/// @brief Used to descriptively refer to the maximum version string length. +/// Used with arrays to make them more descriptive than simply using the +/// number, and to make changing the number easier and safer. +#define MAX_NUM_CHARS_IN_VERSION 16 + +/// @brief Used to descriptively refer to the value given to glove and dongle IDs when they are uninitialized. +#define UNINITIALIZED_ID 0 + +/// @brief Used to descriptively refer to the maximum number of hosts that we support when finding manus core. +#define MAX_NUMBER_OF_HOSTS 100 + +/// @brief Used to descriptively refer to the max number of supported dongles. +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_NUMBER_OF_DONGLES 16 // we only have 35 radio channels available. if we got 16 dongles, expect wireless issues! and no 16 usb ports is also not likely. + +/// @brief Used to descriptively refer to the maximum license type string length. +#define MAX_NUM_CHARS_IN_LICENSE_TYPE 64 + +/// @brief Used to descriptively refer to the maximum calibration step title length. +/// Used with arrays to make them more descriptive than simply using the +/// number, and to make changing the number easier and safer. +#define MAX_NUM_CHARS_IN_CALIBRATION_TITLE 64 + +/// @brief Used to descriptively refer to the maximum calibration step description length. +/// Used with arrays to make them more descriptive than simply using the +/// number, and to make changing the number easier and safer. +#define MAX_NUM_CHARS_IN_CALIBRATION_DESCRIPTION 256 + +#define MAX_NUM_CHARS_IN_CALIBRATION_FILE 65536 + +/// @brief Constants for the maximum number of devices considered plausible. +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_NUMBER_OF_GLOVES (MAX_NUMBER_OF_DONGLES* 2) +#define MAX_NUMBER_OF_HAPTICS_DONGLES MAX_NUMBER_OF_DONGLES + +/// @brief Constants for the maximum number of skeletons. +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_NUMBER_OF_SKELETONS 32 + +/// @brief Constants for the maximum number of users. +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_NUMBER_OF_USERS MAX_USERS + +/// @brief Constants for the maximum number of trackers for each polygon skeleton. +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define NUMBER_OF_TRACKERS_PER_POLYGON_SKELETON 8 + +/// @brief Constants for the maximum number of trackers. +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_NUMBER_OF_TRACKERS (MAX_NUMBER_OF_USERS * NUMBER_OF_TRACKERS_PER_POLYGON_SKELETON) + +/// @brief Used to descriptively refer to the maximum node name length. +/// Used with arrays to make them more descriptive than simply using the +/// number, and to make changing the number easier and safer. +#define MAX_NUM_CHARS_IN_NODE_NAME 256 // this is for a UTF8 string , NOT an ASCII CHAR array (same base type though) + + +/// @brief Used to descriptively refer to the max number of chain nodes. +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_CHAIN_LENGTH 32 + +/// @brief Used to descriptively refer to the max number of fingers we support per hand in chains (not the same as a glove, as we can retarget alien hands with more or less fingers). +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_NUM_FINGER_IDS 10 + +/// @brief Used to descriptively refer to the max number of toes we support per foot in chains ( we can retarget alien feet with more or less toes). +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_NUM_TOE_IDS 10 + +/// @brief Used to descriptively refer to the max length of a system error message string. +/// Used with arrays to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_NUM_CHARS_IN_SYSTEM_ERROR_MESSAGE 256 + +/// @brief Used to descriptively refer to the max length of a debugging id string. +/// Used with arrays to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_NUM_CHAR_DEBUGGING_ID 64 + +/// @brief Used to descriptively refer to the max number of ergonomics data. +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_NUMBER_OF_ERGONOMICS_DATA MAX_NUMBER_OF_GLOVES + +/// @brief Used to descriptively refer to the max number of sessions connected to Core. +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_NUMBER_OF_SESSIONS 8 // this is not the real limit for Core but just for the SDKClient to handle + +/// @brief Used to descriptively refer to the max number of skeletons for each session. +/// Used with arrays and loops to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_NUMBER_OF_SKELETONS_PER_SESSION 32 + +/// @brief Used to descriptively refer to the max length of a skeleton name string. +/// Used with arrays to make them more descriptive than simply using +/// the number, and to make changing the number easier and safer. +#define MAX_NUM_CHARS_IN_SKELETON_NAME 256 // we already encountered 34 char names in unreal, but its utf8 so enbiggen even more! + +/// @brief Used to descriptively refer to the max length of a timecode interface string. +#define MAX_NUM_CHARS_IN_TIMECODE_INTERFACE_STRINGS 64 + +/// @brief Used to descriptively refer to the max amount of timecode interfaces. +#define MAX_NUMBER_OF_AUDIO_INTERFACES 32 + +/// @brief Used to descriptively refer to the maximum number of bone weights per vertex. +#define MAX_BONE_WEIGHTS_PER_VERTEX 4 + +/// @brief Used to descriptively refer to the maximum number of nodes per each estimation skeleton +#define MAX_NUMBER_OF_NODES_PER_ESTIMATION_SKELETON 40 + +/// @brief Used to define the max amount of gestures in one chunk +#define MAX_GESTURE_DATA_CHUNK_SIZE 64 + +#define MAX_NUM_CHARS_IN_OPENXR_FEATURE_STRING 64 + +/// @brief Used to define the max amount of net devices +#define MAX_NUMBER_OF_NETDEVICES MAX_NUMBER_OF_DONGLES + +/// @brief Matchmaker requirements. +#define BROADCAST_ADDRESS "255.255.255.255" +#define BROADCAST_PORT "62687" +#define SECONDS_TO_FIND_HOSTS 2 +#define SECONDS_TO_FIND_LOCAL_HOSTS 2 +#define DEFAULT_BUFFER_SIZE 512 + +/****************************************************************************** + * Enums. + *****************************************************************************/ + +/// @brief The return values that can be given by SDK wrapper functions. +typedef enum SDKReturnCode +{ + /// No issues occurred. + SDKReturnCode_Success, + + /// Something went wrong, but no specific reason can be given. + SDKReturnCode_Error, + + /// One of the arguments given had an invalid value. + SDKReturnCode_InvalidArgument, + + /// The size of an argument given (e.g. an array) does not match the size + /// of the data that it is intended to hold. + SDKReturnCode_ArgumentSizeMismatch, + + /// A string of an unsupported size was encountered. + SDKReturnCode_UnsupportedStringSizeEncountered, + + /// The Core SDK is not available. + SDKReturnCode_SdkNotAvailable, + + /// The network host finder is not available. + SDKReturnCode_HostFinderNotAvailable, + + /// The data requested is not available. + SDKReturnCode_DataNotAvailable, + + /// Failed to allocate memory for something. + SDKReturnCode_MemoryError, + + /// Something went wrong in the SDK internally. + SDKReturnCode_InternalError, + + /// The function was not intended to be called at this time. + SDKReturnCode_FunctionCalledAtWrongTime, + + /// No connection to Core was made. + SDKReturnCode_NotConnected, + + /// The connection with Core timed out. + SDKReturnCode_ConnectionTimeout, + + /// using an uninitialized ID is bad. + SDKReturnCode_InvalidID, + + /// memory unallocated or just a null pointer passed where it wasn't supposed to be! + SDKReturnCode_NullPointer, + + /// null sequence type for polygon calibration + SDKReturnCode_InvalidSequence, + + /// don't forget to set the coordinate system type or there will be trouble + SDKReturnCode_NoCoordinateSystemSet, + + /// if everything is being terminated. don't restart + SDKReturnCode_SdkIsTerminating, + + /// the stub has been reset but someone is tryign to use it anyway. usually after a shutdown of the SDK. + SDKReturnCode_StubNullPointer, + + /// Skeleton could not be loaded. usually when using more then the max skeletons per session (32). + SDKReturnCode_SkeletonNotLoaded, + + /// Function not available for this version of the SDK + SDKReturnCode_FunctionNotAvailable, + + SDKReturnCode_MAX_SIZE +} SDKReturnCode; + +/// @brief Used to tell what severity the log is +typedef enum LogSeverity +{ + LogSeverity_Debug, + LogSeverity_Info, + LogSeverity_Warn, + LogSeverity_Error +} LogSeverity; + + +/// @brief Used to tell what client is using the wrapper. +/// This makes the session easier to identify in the landscape. +typedef enum SessionType +{ + SessionType_Unknown, + SessionType_UnityPlugin, + SessionType_UnrealPlugin, + SessionType_CoreSDK, + SessionType_Xsens, + SessionType_Optitrack, + SessionType_MotionBuilder, + SessionType_VRED, + SessionType_OpenXR, + SessionType_Qualisys, + SessionType_Vicon, + SessionType_Nokov, + SessionType_IcIdo, + SessionType_Siemens +} SessionType; + +/// @brief Describes the different types of trackers that can be used. +typedef enum TrackerType +{ + TrackerType_Unknown, + TrackerType_Head, + TrackerType_Waist, + TrackerType_LeftHand, + TrackerType_RightHand, + TrackerType_LeftFoot, + TrackerType_RightFoot, + TrackerType_LeftUpperArm, + TrackerType_RightUpperArm, + TrackerType_LeftUpperLeg, + TrackerType_RightUpperLeg, + TrackerType_Controller, + TrackerType_Camera, + + TrackerType_MAX_SIZE +} TrackerType; + +/// @brief Describes the tracking quality. +typedef enum TrackingQuality +{ + TrackingQuality_Untrackable, + TrackingQuality_BadTracking, + TrackingQuality_Trackable, +} TrackerQuality; + +/// @brief Describes the different types of tracker systems. +typedef enum TrackerSystemType +{ + TrackerSystemType_Unknown, + TrackerSystemType_Antilatency, + TrackerSystemType_ART, + TrackerSystemType_OpenVR, + TrackerSystemType_Optitrack, + TrackerSystemType_Vicon, + TrackerSystemType_OpenXR, +} TrackerSystemType; + +/// @brief Describes the paired state of the device. +typedef enum DevicePairedState +{ + DevicePairedState_Unknown, + DevicePairedState_Paired, + DevicePairedState_Unpaired, + DevicePairedState_Pairing, +} DevicePairedState; + +/// @brief Describes the different types of device classes. +typedef enum DeviceClassType +{ + DeviceClassType_Unknown, + DeviceClassType_Dongle, + DeviceClassType_Glove, + DeviceClassType_Glongle +} DeviceClassType; + +/// @brief Describes the different types of Manus devices. +typedef enum DeviceFamilyType +{ + DeviceFamilyType_Unknown, + DeviceFamilyType_Prime1, + DeviceFamilyType_Prime2, + DeviceFamilyType_PrimeX, // TODO obsolete? + DeviceFamilyType_Metaglove, + DeviceFamilyType_Prime3, + DeviceFamilyType_Virtual, + DeviceFamilyType_MetaglovePro, +} DeviceFamilyType; + +/// @brief Describes the different types of profile used during the calibration. +typedef enum ProfileType +{ + ProfileType_Hands, + ProfileType_FullBody, + + ProfileType_MAX_SIZE +} ProfileType; + +/// @brief The different types of body measurements used for the polygon calibration. +typedef enum MeasurementType +{ + MeasurementType_Unknown, + MeasurementType_PlayerHeight, + MeasurementType_SpineLength, + MeasurementType_NeckLength, + MeasurementType_UpperArmLength, + MeasurementType_LowerArmLength, + MeasurementType_ArmLength, + MeasurementType_ArmSpan, + MeasurementType_UpperLegLength, + MeasurementType_LowerLegLength, + MeasurementType_LegLength, + MeasurementType_HandLength, + MeasurementType_FootLength, + MeasurementType_HipWidth, + MeasurementType_ShoulderWidth, + MeasurementType_ShoulderHeight, + MeasurementType_HeadLength, + MeasurementType_Thickness, + MeasurementType_ArmRatio, + MeasurementType_LegRatio, + + MeasurementType_MAX_SIZE // Warning, this value is used to define the UserProfile.Measurement[SIZE] +} MeasurementType; + +/// @brief Describes the different types of tracker offsets. +typedef enum TrackerOffsetType +{ + TrackerOffsetType_Unknown, + TrackerOffsetType_HeadTrackerToHead, + TrackerOffsetType_HeadTrackerToTopOfHead, + + TrackerOffsetType_LeftHandTrackerToWrist, + TrackerOffsetType_RightHandTrackerToWrist, + TrackerOffsetType_LeftFootTrackerToAnkle, + TrackerOffsetType_RightFootTrackerToAnkle, + + TrackerOffsetType_HipTrackerToHip, + TrackerOffsetType_HipTrackerToLeftLeg, + TrackerOffsetType_HipTrackerToRightLeg, + + TrackerOffsetType_LeftUpperArmTrackerToElbow, + TrackerOffsetType_RightUpperArmTrackerToElbow, + TrackerOffsetType_LeftUpperArmTrackerToShoulder, + TrackerOffsetType_RightUpperArmTrackerToShoulder, + + TrackerOffsetType_MAX_SIZE // Warning, this value is used to define the UserProfile.TrackerOffset[SIZE] +} TrackerOffsetType; + +/// @brief Describes the different types of extra tracker offsets. +typedef enum ExtraTrackerOffsetType +{ + ExtraTrackerOffsetType_Unknown, + ExtraTrackerOffsetType_HeadForward, + ExtraTrackerOffsetType_HipForward, + ExtraTrackerOffsetType_HipHeight, + ExtraTrackerOffsetType_MAX_SIZE // Warning, this value is used to define the UserProfile.TrackerOffset[SIZE] +} ExtraTrackerOffsetType; + +/// @brief Describes the different types of body measurement units. +typedef enum MeasurementUnit +{ + MeasurementUnit_Meters, + MeasurementUnit_Percentage, +} MeasurementUnit; + +/// @brief Describes the different types of body measurement categories used for the polygon calibration. +typedef enum MeasurementCategory +{ + MeasurementCategory_Misc, + MeasurementCategory_Generic, + MeasurementCategory_Arms, + MeasurementCategory_Legs, + MeasurementCategory_Body, +} MeasurementCategory; + +/// @brief Describes the different possibilities for the update status. +typedef enum UpdateStatusEnum +{ + UpdateStatusEnum_Unknown, + UpdateStatusEnum_NoUpdateAvailable, + UpdateStatusEnum_UpdateAvailable, + UpdateStatusEnum_MandatoryUpdateAvailable, + UpdateStatusEnum_Updating, +} UpdateStatusEnum; + +/// @brief Describes the different skeleton types. +typedef enum SkeletonType +{ + SkeletonType_Invalid, + SkeletonType_Hand, + SkeletonType_Body, + SkeletonType_Both +} SkeletonType; + +/// @brief Describes the possible data that can be used for the skeleton animation. +typedef enum SkeletonTargetType +{ + SkeletonTargetType_Invalid, + SkeletonTargetType_UserData, + SkeletonTargetType_UserIndexData, + SkeletonTargetType_AnimationData, + SkeletonTargetType_GloveData +} SkeletonTargetType; + +/// @brief Describes the possible nodes types used when setting up the skeleton. +typedef enum NodeType +{ + NodeType_Invalid, + NodeType_Joint, + NodeType_Mesh, + NodeType_Leaf, + NodeType_Collider +} NodeType; + +/// @brief Describes the settings that can be applied to a node, it is defined as a flag so that more than one setting can be set. +typedef enum NodeSettingsFlag +{ + NodeSettingsFlag_None = 0, + NodeSettingsFlag_IK = 1 << 0, + NodeSettingsFlag_Foot = 1 << 1, + NodeSettingsFlag_RotationOffset = 1 << 2, + NodeSettingsFlag_Leaf = 1 << 3, +} NodeSettingsFlag; + +/// @brief Describes the possible chain types used when setting up the skeleton. +typedef enum ChainType +{ + ChainType_Invalid, + ChainType_Arm, + ChainType_Leg, + ChainType_Neck, + ChainType_Spine, + ChainType_FingerThumb, + ChainType_FingerIndex, + ChainType_FingerMiddle, + ChainType_FingerRing, + ChainType_FingerPinky, + ChainType_Pelvis, + ChainType_Head, + ChainType_Shoulder, + ChainType_Hand, + ChainType_Foot, + ChainType_Toe +} ChainType; + +/// @brief Describes the possible collider types. +typedef enum CollisionType +{ + CollisionType_None, + CollisionType_Discrete, + CollisionType_Continuous, +} CollisionType; + +/// @brief Describes the possible collider types. +typedef enum ColliderType +{ + ColliderType_Invalid, + ColliderType_Sphere, + ColliderType_Capsule, + ColliderType_Box, +} ColliderType; + +/// @brief Describes the possible chain side. +typedef enum Side +{ + Side_Invalid, + Side_Left, + Side_Right, + Side_Center +} Side; + +/// @brief Describes which sensor data the hand motion is based on. +typedef enum HandMotion +{ + HandMotion_None, + HandMotion_IMU, + HandMotion_Tracker, + HandMotion_Tracker_RotationOnly, + HandMotion_Auto +} HandMotion; + +/// @brief Describes the direction of the coordinate system axis in 3d space. +typedef enum AxisDirection +{ + AxisDirection_Invalid, + AxisDirection_Backward, + AxisDirection_Left, + AxisDirection_Down, + AxisDirection_Up, + AxisDirection_Right, + AxisDirection_Forward +} AxisDirection; + +/// @brief Describes the view of the coordinate system axis. +/// Consider yourself sitting in front of your computer screen. +/// From Viewer means it goes into the screen, so away from you. +/// To Viewer means the axis goes from the screen towards you. +typedef enum AxisView +{ + AxisView_Invalid, + + AxisView_ZFromViewer, + AxisView_YFromViewer, + AxisView_XFromViewer, + + AxisView_XToViewer, + AxisView_YToViewer, + AxisView_ZToViewer +} AxisView; + +/// @brief Describes the polarity of the coordinate system axis. +typedef enum AxisPolarity +{ + AxisPolarity_Invalid, + + AxisPolarity_NegativeZ, + AxisPolarity_NegativeY, + AxisPolarity_NegativeX, + + AxisPolarity_PositiveX, + AxisPolarity_PositiveY, + AxisPolarity_PositiveZ +} AxisPolarity; + +/// @brief Describes the possible types for system messages received from core. +typedef enum SystemMessageType +{ + SystemMessageType_Unknown, + SystemMessageType_LibDebugReplugDongle, + SystemMessageType_LibDebugRxStall, + SystemMessageType_LibDebugTxStall, + + SystemMessageType_TrackerError, + SystemMessageType_TrackerOk, + SystemMessageType_TrackerSystemOutOfDate, + + SystemMessageType_GloveSanityErrorPSOCInit, + SystemMessageType_GloveSanityErrorQCBatV, + SystemMessageType_GloveSanityErrorQCLRACalib, + SystemMessageType_GloveSanityErrorQCFlexInit, + SystemMessageType_GloveSanityErrorQCIMUInit, + SystemMessageType_GloveSanityErrorQCIMUCalib, + SystemMessageType_GloveSanityErrorQCID, + SystemMessageType_GloveSanityErrorQCInterCPU, + + SystemMessageType_SessionConnectionVersionMismatch, + + SystemMessageType_TemporarySkeletonModified, + + SystemMessageType_SessionRefusedDueToLicenseIssue, + + SystemMessageType_LaunchDevTools +} SystemMessageType; + +/// @brief Describes the possible types for the ergonomics data. +typedef enum ErgonomicsDataType +{ +// ErgonomicsDataType_Invalid, + + ErgonomicsDataType_LeftFingerThumbMCPSpread, + ErgonomicsDataType_LeftFingerThumbMCPStretch, + ErgonomicsDataType_LeftFingerThumbPIPStretch, + ErgonomicsDataType_LeftFingerThumbDIPStretch, + + ErgonomicsDataType_LeftFingerIndexMCPSpread, + ErgonomicsDataType_LeftFingerIndexMCPStretch, + ErgonomicsDataType_LeftFingerIndexPIPStretch, + ErgonomicsDataType_LeftFingerIndexDIPStretch, + + ErgonomicsDataType_LeftFingerMiddleMCPSpread, + ErgonomicsDataType_LeftFingerMiddleMCPStretch, + ErgonomicsDataType_LeftFingerMiddlePIPStretch, + ErgonomicsDataType_LeftFingerMiddleDIPStretch, + + ErgonomicsDataType_LeftFingerRingMCPSpread, + ErgonomicsDataType_LeftFingerRingMCPStretch, + ErgonomicsDataType_LeftFingerRingPIPStretch, + ErgonomicsDataType_LeftFingerRingDIPStretch, + + ErgonomicsDataType_LeftFingerPinkyMCPSpread, + ErgonomicsDataType_LeftFingerPinkyMCPStretch, + ErgonomicsDataType_LeftFingerPinkyPIPStretch, + ErgonomicsDataType_LeftFingerPinkyDIPStretch, + + + ErgonomicsDataType_RightFingerThumbMCPSpread, + ErgonomicsDataType_RightFingerThumbMCPStretch, + ErgonomicsDataType_RightFingerThumbPIPStretch, + ErgonomicsDataType_RightFingerThumbDIPStretch, + + ErgonomicsDataType_RightFingerIndexMCPSpread, + ErgonomicsDataType_RightFingerIndexMCPStretch, + ErgonomicsDataType_RightFingerIndexPIPStretch, + ErgonomicsDataType_RightFingerIndexDIPStretch, + + ErgonomicsDataType_RightFingerMiddleMCPSpread, + ErgonomicsDataType_RightFingerMiddleMCPStretch, + ErgonomicsDataType_RightFingerMiddlePIPStretch, + ErgonomicsDataType_RightFingerMiddleDIPStretch, + + ErgonomicsDataType_RightFingerRingMCPSpread, + ErgonomicsDataType_RightFingerRingMCPStretch, + ErgonomicsDataType_RightFingerRingPIPStretch, + ErgonomicsDataType_RightFingerRingDIPStretch, + + ErgonomicsDataType_RightFingerPinkyMCPSpread, + ErgonomicsDataType_RightFingerPinkyMCPStretch, + ErgonomicsDataType_RightFingerPinkyPIPStretch, + ErgonomicsDataType_RightFingerPinkyDIPStretch, + + ErgonomicsDataType_MAX_SIZE +} ErgonomicsDataType; + +/// @brief Describes the possible Manus license types. +typedef enum LicenseType +{ + LicenseType_Undefined, + LicenseType_Polygon, + LicenseType_CoreXO, + LicenseType_CorePro, + LicenseType_CoreXOPro, + + LicenseType_CoreX, + LicenseType_CoreO, + LicenseType_CoreQ, + LicenseType_CoreXPro, + LicenseType_CoreOPro, + LicenseType_CoreQPro, + LicenseType_CoreXOQPro, + LicenseType_CoreXR, + LicenseType_CorePrimeThree, + LicenseType_Feature, + +} LicenseType; + +/// @brief The possible FPS rates +typedef enum TimecodeFPS +{ + TimecodeFPS_Undefined, + TimecodeFPS_23_976, + TimecodeFPS_24, + TimecodeFPS_25, + TimecodeFPS_29_97, + TimecodeFPS_30, + TimecodeFPS_48, + TimecodeFPS_50, + TimecodeFPS_59_94, + TimecodeFPS_60, + TimecodeFPS_29_97DF, + TimecodeFPS_59_94DF, +} TimecodeFPS; + +typedef enum FingerJointType +{ + FingerJointType_Invalid, + FingerJointType_Metacarpal, + FingerJointType_Proximal, + FingerJointType_Intermediate, + FingerJointType_Distal, //thumb doesn't have it + FingerJointType_Tip +}FingerJointType; + +typedef enum SetGloveCalibrationReturnCode +{ + SetGloveCalibrationReturnCode_Error, + SetGloveCalibrationReturnCode_Success, + SetGloveCalibrationReturnCode_VersionError, + SetGloveCalibrationReturnCode_WrongSideError, + SetGloveCalibrationReturnCode_GloveNotFoundError, + SetGloveCalibrationReturnCode_UserServiceError, + SetGloveCalibrationReturnCode_DeserializationError, + + SetGloveCalibrationReturnCode_MAX_SIZE +} SetGloveCalibrationReturnCode; + +/****************************************************************************** + * Structs. + *****************************************************************************/ + +/// @brief A 3D vector, used for translations. +typedef struct ManusVec3 +{ + float x; //default = 0.0f; + float y; //default = 0.0f; + float z; //default = 0.0f; +} ManusVec3; + +/// @brief A 2D vector, used for translations. +typedef struct ManusVec2 +{ + float x; //default = 0.0f; + float y; //default = 0.0f; +} ManusVec2; + +/// @brief A quaternion, used for rotations. +typedef struct ManusQuaternion +{ + float w; //default = 1.0f; + float x; //default = 0.0f; + float y; //default = 0.0f; + float z; //default = 0.0f; +} ManusQuaternion; + +/// @brief Transform containing position, rotation and scaling. +typedef struct ManusTransform +{ + ManusVec3 position; + ManusQuaternion rotation; + ManusVec3 scale; +} ManusTransform; + +/// @brief Color containing red, green, blue and alpha. +typedef struct Color +{ + float r; //default = 0.0f; + float g; //default = 0.0f; + float b; //default = 0.0f; + float a; //default = 0.0f; +}Color; + +/// @brief A Timestamp +typedef struct ManusTimestampInfo +{ + uint16_t fraction; //is either frame in timecode or miliseconds in non timecode + uint8_t second; + uint8_t minute; + uint8_t hour; + uint8_t day; //is 0 in timecode + uint8_t month; //is 0 in timecode + uint32_t year; //is 0 in timecode + bool timecode; +} ManusTimestampInfo; + +/// @brief A compressed timestamp +typedef struct ManusTimestamp +{ + uint64_t time; +} ManusTimestamp; + +/// @brief Information regarding IMU sensors used for calibration +typedef struct IMUCalibrationInfo +{ + uint32_t mag; // Magnometer calibration level 0-3 + uint32_t acc; // Accelerometer caibraton level 0-3 + uint32_t gyr; // Gyroscope calibration level 0-3 + uint32_t sys; // System accuracy +} IMUCalibrationInfo; + +/// @brief Used to describe hardware, firmware or ManusCore version. +typedef struct Version +{ + uint32_t major; + uint32_t minor; + uint32_t patch; + char label[MAX_NUM_CHARS_IN_VERSION]; + char sha[MAX_NUM_CHARS_IN_VERSION]; + char tag[MAX_NUM_CHARS_IN_VERSION]; +} Version; + +typedef struct FirmwareVersion +{ + int32_t version; + ManusTimestamp timestamp; +} FirmwareVersion; + +/// @brief Stores a single version string. +typedef struct ManusVersion +{ + char versionInfo[MAX_NUM_CHARS_IN_VERSION]; +} ManusVersion; + +/// @brief Contains information for connecting to a host running Manus Core. +/// Note that if one of these values is blank, the other will be used when +/// connecting. +typedef struct ManusHost +{ + char hostName[MAX_NUM_CHARS_IN_HOST_NAME]; // TODO utf8 compliant? + char ipAddress[MAX_NUM_CHARS_IN_IP_ADDRESS]; + Version manusCoreVersion; +} ManusHost; + +/****************************************************************************** + * Tracking + *****************************************************************************/ + + + +/// @brief Stores the name of a tracker. +typedef struct TrackerId +{ + char id[MAX_NUM_CHARS_IN_TRACKER_ID]; // todo. make this UTF8 compliant ? +} TrackerId; + +/// @brief Stores all the tracker data that can be sent or received. +typedef struct TrackerData +{ + ManusTimestamp lastUpdateTime; //default = 0; + + TrackerId trackerId; + + uint32_t userId; //default = 0; + + bool isHmd; //default = false; + TrackerType trackerType; //default = TrackerType::TrackerType_Unknown; + + ManusQuaternion rotation; + ManusVec3 position; + + TrackingQuality quality; //default = TrackingQuality::TrackingQuality_Untrackable; +} TrackerData; + + +/// @brief Stores the information sent by the tracker stream. +typedef struct TrackerStreamInfo +{ + ManusTimestamp publishTime; //default = 0; // DateTime.UtcNow. + uint32_t trackerCount; //default = 0; +} TrackerStreamInfo; + +// ------------------------------------------------------------------------------------------------------------------------------ +// end of Tracking +// ------------------------------------------------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------------------------------------------------ +// Gesture Stream +// ------------------------------------------------------------------------------------------------------------------------------ + +typedef struct GestureProbability +{ + uint32_t id; + float percent; +} GestureProbability; + +typedef struct GestureProbabilities +{ + uint32_t id; //default = 0; + bool isUserID; //default = false; + uint32_t totalGestureCount; //default = 0; + GestureProbability gestureData[MAX_GESTURE_DATA_CHUNK_SIZE]; //default = { 0 }; + uint32_t gestureCount; //default = 0; +} GestureProbabilities; + +typedef struct GestureStreamInfo +{ + ManusTimestamp publishTime; //default = 0; // DateTime.UtcNow. + uint32_t gestureProbabilitiesCount; //default = 0; +} GestureStreamInfo; + + +// ------------------------------------------------------------------------------------------------------------------------------ +// end of Gesture Stream +// ------------------------------------------------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------------------------------------------------ +// Skeleton Stream +// ------------------------------------------------------------------------------------------------------------------------------ + +/// @brief Stores the information regarding each skeleton node. +/// The transform is defined as a local or global transform depending on the coordinate system set when initializing the SDK. +/// See functions CoreSdk_InitializeCoordinateSystemWithVUH and CoreSdk_InitializeCoordinateSystemWithDirection. +typedef struct SkeletonNode +{ + uint32_t id; + ManusTransform transform; +} SkeletonNode; + +/// @brief Stores the information regarding the skeletons that have been added to Manus Core. +typedef struct SkeletonInfo +{ + uint32_t id; //default = 0; + uint32_t nodesCount; //default = 0; + ManusTimestamp publishTime; //default = 0; // DateTime.UtcNow. +} SkeletonInfo; + +/// @brief Stores the information regarding the skeletons coming from the estimation system in core +typedef struct RawSkeletonInfo +{ + uint32_t gloveId; //default = 0; + uint32_t nodesCount; //default = 0; + ManusTimestamp publishTime; //default = 0; // DateTime.UtcNow. +} RawSkeletonInfo; + +/// @brief Stores the information sent by the skeleton stream. +typedef struct SkeletonStreamInfo +{ + ManusTimestamp publishTime; //default = 0; // DateTime.UtcNow. + uint32_t skeletonsCount; //default = 0; +} SkeletonStreamInfo; + + +// ------------------------------------------------------------------------------------------------------------------------------ +// end of Skeleton Stream +// ------------------------------------------------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------------------------------------------------ +// Ergonomics +// ------------------------------------------------------------------------------------------------------------------------------ + +/// @brief Stores the received ergonomics data. +typedef struct ErgonomicsData +{ + uint32_t id; //default = 0; + bool isUserID; //default = false; + float data[ErgonomicsDataType_MAX_SIZE]; //default = { 0 }; +} ErgonomicsData; + +/// @brief Stores the information sent by the ergonomics stream. +typedef struct ErgonomicsStream +{ + ManusTimestamp publishTime; //default = 0; + ErgonomicsData data[MAX_NUMBER_OF_ERGONOMICS_DATA]; + uint32_t dataCount; //default = 0; +} ErgonomicsStream; + +// ------------------------------------------------------------------------------------------------------------------------------ +// end of Ergonomics +// ------------------------------------------------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------------------------------------------------ +// Landscape +// ------------------------------------------------------------------------------------------------------------------------------ + +/// @brief Stores all the received dongle data. +typedef struct DongleLandscapeData +{ + uint32_t id; + DeviceClassType classType; + DeviceFamilyType familyType; + bool isHaptics; + + Version hardwareVersion; + Version firmwareVersion; + ManusTimestamp firmwareTimestamp; + + uint32_t chargingState; + + int32_t channel; + + UpdateStatusEnum updateStatus; + + char licenseType[MAX_NUM_CHARS_IN_LICENSE_TYPE]; + + ManusTimestamp lastSeen; // this may be used in playback/recording TBD + + uint32_t leftGloveID; + uint32_t rightGloveID; + + LicenseType licenseLevel; //default = LicenseType::LicenseType_Undefined; + ManusTimestamp licenseExpiration; + + uint32_t netDeviceID; +} DongleLandscapeData; + +/// @brief Stores all the received glove data. +typedef struct GloveLandscapeData +{ + uint32_t id; + DeviceClassType classType; + DeviceFamilyType familyType; + Side side; + bool isHaptics; + + DevicePairedState pairedState; + uint32_t dongleID; + + Version hardwareVersion; + Version firmwareVersion; + ManusTimestamp firmwareTimestamp; + + UpdateStatusEnum updateStatus; + + uint32_t batteryPercentage; + int32_t transmissionStrength; + + IMUCalibrationInfo iMUCalibrationInfo[MAX_NUM_IMUS_ON_GLOVE]; + + ManusTimestamp lastSeen; // this may be used in playback/recording TBD + bool excluded; + + uint32_t netDeviceID; +} GloveLandscapeData; + +/// @brief Stores informations regarding the lengths of different parts of the body. +typedef struct Measurement +{ + MeasurementType entryType; //default = MeasurementType::MeasurementType_Unknown; + float value; //default = 0.0f; + + MeasurementUnit unit; //default = MeasurementUnit_Meters; + MeasurementCategory category; //default = MeasurementCategory_Misc; + char displayName[MAX_NUM_CHARS_IN_MEASUREMENT]; +} Measurement; + +/// @brief Stores the local offsets to the trackers. +typedef struct TrackerOffset +{ + TrackerOffsetType entryType; + ManusVec3 translation; + ManusQuaternion rotation; +} TrackerOffset; + +/// @brief Stores possible extra offsets to the trackers. +typedef struct ExtraTrackerOffset +{ + ExtraTrackerOffsetType entryType; //default = ExtraTrackerOffsetType::ExtraTrackerOffsetType_Unknown; + float value; //default = 0.0f; +} ExtraTrackerOffset; + +/// @brief Stores all the received tracker data. +typedef struct TrackerLandscapeData +{ + char id[MAX_NUM_CHARS_IN_TRACKER_ID]; + TrackerType type; //default = TrackerType::TrackerType_Unknown; + TrackerSystemType systemType; //default = TrackerSystemType::TrackerSystemType_Unknown; + uint32_t user; //default = 0; + bool isHMD; //default = false; + char manufacturer[MAX_NUM_CHARS_IN_TRACKER_MANUFACTURER]; // default = "Unknown" + char productName[MAX_NUM_CHARS_IN_TRACKER_PRODUCTNAME]; // default = "Unknown" +} TrackerLandscapeData; + +/// @brief Stores all the received user profile data. +typedef struct UserProfileLandscapeData +{ + ProfileType profileType; //default = ProfileType::ProfileType_Hands; + Measurement measurements[MeasurementType_MAX_SIZE]; + TrackerOffset trackerOffsets[TrackerOffsetType_MAX_SIZE]; + ExtraTrackerOffset extraTrackerOffsets[ExtraTrackerOffsetType_MAX_SIZE]; +} UserProfileLandscapeData; + +/// @brief Stores all the received user data. +typedef struct UserLandscapeData +{ + uint32_t id; //default = 0; + char name[MAX_NUM_CHARS_IN_USERNAME]; + Color color; + uint32_t dongleID; //default = 0; + uint32_t leftGloveID; //default = 0; + uint32_t rightGloveID; //default = 0; + UserProfileLandscapeData profile; + uint32_t userIndex; //default = 0; +} UserLandscapeData; + +/// @brief Stores all the received skeleton data. +typedef struct SkeletonLandscapeData +{ + uint32_t id; //default = 0; + char session[MAX_NUM_CHARS_IN_HOST_NAME]; + uint32_t userId; //default = 0; + SkeletonType type; //default = SkeletonType::SkeletonType_Invalid; + char rootBoneName[MAX_NUM_CHARS_IN_NODE_NAME]; + bool scaled; //default = false; +} SkeletonLandscapeData; + +/// @brief Stores all the information related to the devices present in the landscape. +typedef struct DeviceLandscape +{ + DongleLandscapeData dongles[MAX_NUMBER_OF_DONGLES]; + uint32_t dongleCount; //default = 0; + GloveLandscapeData gloves[MAX_NUMBER_OF_GLOVES]; + uint32_t gloveCount; //default = 0; +} DeviceLandscape; + +/// @brief Stores all the information related to the users present in the landscape. +typedef struct UserLandscape +{ + UserLandscapeData users[MAX_USERS]; + uint32_t userCount; //default = 0; +} UserLandscape; + +/// @brief Stores all the information related to the skeletons present in the landscape. +typedef struct SkeletonLandscape +{ + SkeletonLandscapeData skeletons[MAX_NUMBER_OF_SKELETONS]; + uint32_t skeletonCount; //default = 0; +} SkeletonLandscape; + +/// @brief Stores all the information related to the trackers present in the landscape. +typedef struct TrackerLandscape +{ + TrackerLandscapeData trackers[MAX_NUMBER_OF_TRACKERS]; + uint32_t trackerCount; //default = 0; +} TrackerLandscape; + +/// @brief Stores the license information. +typedef struct LicenseInfo +{ + bool recording; //default = false; + bool exporting; //default = false; + bool advancedExporting; //default = false; + bool unitySession; //default = false; + bool unrealSession; //default = false; + bool openXRSession; //default = false; + bool sdk; //default = false; + bool raw; //default = false; + bool mobuSession; //default = false; + bool xsensSession; //default = false; + bool optitrackSession; //default = false; + bool qualisysSession; //default = false; + bool viconSession; //default = false; + bool nokovSession; //default = false; + bool icidoSession; //default = false; + bool siemensSession; //default = false; + bool vredSession; //default = false; + +} LicenseInfo; + +/// @brief Stores the landscape settings. +typedef struct SettingsLandscape +{ + Version manusCoreVersion; + LicenseInfo license; + bool playbackMode; //default = false; + bool ignoreSessionTimeOuts; //default = false; + FirmwareVersion firmwareOne; + FirmwareVersion firmwareTwo; + bool recordingMode; //default = false; + bool isNetDevice; // default = false; + bool isConnectedAsNetDevice; // default = false; +}SettingsLandscape; + +typedef struct TimecodeInterface +{ + char name[MAX_NUM_CHARS_IN_TIMECODE_INTERFACE_STRINGS]; + char api[MAX_NUM_CHARS_IN_TIMECODE_INTERFACE_STRINGS]; + int index; //default = -1; +} TimecodeInterface; + +typedef struct TimeLandscape +{ + TimecodeInterface interfaces[MAX_NUMBER_OF_AUDIO_INTERFACES]; + uint32_t interfaceCount; //default = 0; + TimecodeInterface currentInterface; + TimecodeFPS fps; //default = Undefined; + bool fakeTimecode; //default = false; + bool useSyncPulse; //default = false; + bool deviceKeepAlive; //default = false; + bool syncStatus; //default = false; + bool timecodeStatus; //default = false; + int32_t ltcChannel; //default = -1; +} TimeLandscape; + +/// @brief Contains information about a gesture +typedef struct GestureLandscapeData +{ + uint32_t id; //default = 0; + char name[MAX_NUM_CHARS_IN_USERNAME]; +} GestureLandscapeData; + +/// @brief Contains information about a net devices +typedef struct NetDeviceLandscapeData +{ + uint32_t netDeviceID; + char hostname[MAX_NUM_CHARS_IN_HOST_NAME]; + char ip[MAX_NUM_CHARS_IN_IP_ADDRESS]; +} NetDeviceLandscapeData; + +/// @brief Contains information about a single net device +typedef struct NetDevicesLandscape +{ + uint32_t numberOfNetDevices; // default = 0; + NetDeviceLandscapeData netDevices[MAX_NUMBER_OF_NETDEVICES]; +} NetDeviceLandscape; + +/// @brief Stores the landscape data. +typedef struct Landscape +{ + DeviceLandscape gloveDevices; + UserLandscape users; + SkeletonLandscape skeletons; + TrackerLandscape trackers; + SettingsLandscape settings; + NetDevicesLandscape netDevices; + TimeLandscape time; + uint32_t gestureCount; //default = 0; +} Landscape; + +// ------------------------------------------------------------------------------------------------------------------------------ +// end of Landscape +// ------------------------------------------------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------------------------------------------------ +// Skeleton +// ------------------------------------------------------------------------------------------------------------------------------ +/// @brief Stores the inverse kinematics settings of a node, the ikAim is used to control the IK solve, 1 is default, -1 is inversed. +typedef struct NodeSettingsIK +{ + float ikAim; +} NodeSettingsIK; + +/// @brief Stores the settings for a node of type foot, heightFromGround is used to set the height of the 3d model ankle from ground. +typedef struct NodeSettingsFoot +{ + float heightFromGround; +} NodeSettingsFoot; + +/// @brief Stores the rotation offset of a node, this is used internally for building the skeleton. +typedef struct NodeSettingsRotationOffset +{ + ManusQuaternion value; +} NodeSettingsRotationOffset; + +/// @brief Stores the settings of a node of type leaf, the direction vector is defined with respect to the previous node in the chain. +typedef struct NodeSettingsLeaf +{ + ManusVec3 direction; + float length; +} NodeSettingsLeaf; + +/// @brief Stores all the node settings. +typedef struct NodeSettings +{ + NodeSettingsFlag usedSettings; + NodeSettingsIK ik; + NodeSettingsFoot foot; + NodeSettingsRotationOffset rotationOffset; + NodeSettingsLeaf leaf; +} NodeSettings; + +/// @brief Stores the node setup information. Each node represents a segment of the skeleton that can be animated, nodes combine together to form chains. +/// the parentID is used to identify the node with respect to which the current one will move. +typedef struct NodeSetup +{ + uint32_t id; //default = 0; + char name[MAX_NUM_CHARS_IN_NODE_NAME]; // this is a UTF8 string , NOT an ASCII CHAR array (same base type though) + NodeType type; //default = NodeType::NodeType_Invalid; + ManusTransform transform; + uint32_t parentID; //default = 0; + NodeSettings settings; +} NodeSetup; + +/// @brief Stores all the settings of a chain of type pelvis. +typedef struct ChainSettingsPelvis +{ + float hipHeight; //default = 0.0f; + float hipBendOffset; //default = 0.0f; + float thicknessMultiplier; //default = 1.0f; +} ChainSettingsPelvis; + +/// @brief Stores all the settings of a chain of type leg. +typedef struct ChainSettingsLeg +{ + bool reverseKneeDirection; //default = false; + float kneeRotationOffset; //default = 0.0f; + float footForwardOffset; //default = 0.0f; + float footSideOffset; //default = 0.0f; +} ChainSettingsLeg; + +/// @brief Stores all the settings of a chain of type spine. +typedef struct ChainSettingsSpine +{ + float spineBendOffset; //default = 0.0f; +} ChainSettingsSpine; + +/// @brief Stores all the settings of a chain of type neck. +typedef struct ChainSettingsNeck +{ + float neckBendOffset; //default = 0.0f; +} ChainSettingsNeck; + +/// @brief Stores all the settings of a chain of type head. +typedef struct ChainSettingsHead +{ + float headPitchOffset; //default = 0.0f; + float headYawOffset; //default = 0.0f; + float headTiltOffset; //default = 0.0f; + bool useLeafAtEnd; //default = false; +} ChainSettingsHead; + +/// @brief Stores all the settings of a chain of type arm. +typedef struct ChainSettingsArm +{ + float armLengthMultiplier; //default = 0.0f; + float elbowRotationOffset; //default = 0.0f; + + ManusVec3 armRotationOffset; + + ManusVec3 positionMultiplier; + ManusVec3 positionOffset; +} ChainSettingsArm; + +/// @brief Stores all the settings of a chain of type shoulder. +typedef struct ChainSettingsShoulder +{ + float forwardOffset; //default = 0.0f; + float shrugOffset; //default = 0.0f; + + float forwardMultiplier; //default = 0.0f; + float shrugMultiplier; //default = 0.0f; +} ChainSettingsShoulder; + +/// @brief Stores all the settings of a chain of type finger. +typedef struct ChainSettingsFinger +{ + bool useLeafAtEnd; //default = false; + int32_t metacarpalBoneId; //default = 0; + int32_t handChainId; //default = 0; + float fingerWidth; //default = 0; +} ChainSettingsFinger; + +/// @brief Stores all the settings of a chain of type hand. +typedef struct ChainSettingsHand +{ + int32_t fingerChainIds[MAX_NUM_FINGER_IDS]; + int32_t fingerChainIdsUsed; //default = 0; + HandMotion handMotion; //default = HandMotion::HandMotion_None; +} ChainSettingsHand; + +/// @brief Stores all the settings of a chain of type foot. +typedef struct ChainSettingsFoot +{ + int32_t toeChainIds[MAX_NUM_TOE_IDS]; + int32_t toeChainIdsUsed; //default = 0; +} ChainSettingsFoot; + +/// @brief Stores all the settings of a chain of type toe. +typedef struct ChainSettingsToe +{ + int32_t footChainId; //default = 0; + bool useLeafAtEnd; //default = false; +} ChainSettingsToe; + +/// @brief Stores all chain settings. +typedef struct ChainSettings +{ + ChainType usedSettings; + ChainSettingsPelvis pelvis; + ChainSettingsLeg leg; + ChainSettingsSpine spine; + ChainSettingsNeck neck; + ChainSettingsHead head; + ChainSettingsArm arm; + ChainSettingsShoulder shoulder; + ChainSettingsFinger finger; + ChainSettingsHand hand; + ChainSettingsFoot foot; + ChainSettingsToe toe; +} ChainSettings; + +/// @brief Stores the chain setup information. +typedef struct ChainSetup +{ + uint32_t id; //default = 0; + ChainType type; //default = ChainType::ChainType_Invalid; + ChainType dataType; //default = ChainType::ChainType_Invalid; + uint32_t dataIndex; //default = 0; + uint32_t nodeIdCount; //default = 0; + uint32_t nodeIds[MAX_CHAIN_LENGTH]; + ChainSettings settings; + Side side; //default = Side::Side_Invalid; +} ChainSetup; + +/// @brief Stores all the settings of a collider of type sphere. +typedef struct SphereColliderSetup +{ + float radius; //default = 0; +} SphereColliderSetup; + +/// @brief Stores all the settings of a collider of type capsule. +typedef struct CapsuleColliderSetup +{ + float radius; //default = 0; + float length; //default = 0; +} CapsuleColliderSetup; + +/// @brief Stores all the settings of a collider of type box. +typedef struct BoxColliderSetup +{ + ManusVec3 size; //default = 0,0,0; +} BoxColliderSetup; + +/// @brief Stores the collider setup information. +typedef struct ColliderSetup +{ + uint32_t nodeID; //default = 0; + ManusVec3 localPosition; // default 0,0,0 + ManusVec3 localRotation; // default 0,0,0 + + ColliderType type;//default = ColliderType::ColliderType_Invalid + SphereColliderSetup sphere; + CapsuleColliderSetup capsule; + BoxColliderSetup box; +} ColliderSetup; + +/// @brief Stores the vertex weight information. +typedef struct Weight +{ + uint32_t nodeID; + float weightValue; +} Weight; + +/// @brief Stores the vertex information. +typedef struct Vertex +{ + ManusVec3 position; // default 0,0,0 + uint32_t weightsCount; //default = 0; + Weight weights[MAX_BONE_WEIGHTS_PER_VERTEX]; +} Vertex; + +/// @brief Stores the triangle information. +typedef struct Triangle +{ + int32_t vertexIndex1; + int32_t vertexIndex2; + int32_t vertexIndex3; +} Triangle; + +/// @brief Stores the information regarding the user data used to animate the skeleton. +typedef struct SkeletonTargetUserData +{ + uint32_t userID; //default = 0; +} SkeletonTargetUserData; + +/// @brief Stores the information regarding the user index data used to animate the skeleton. +typedef struct SkeletonTargetUserIndexData +{ + uint32_t userIndex; //default = 0; +} SkeletonTargetUserIndexData; + +/// @brief Stores the information regarding the animation data used to animate the skeleton. +typedef struct SkeletonTargetAnimationData +{ + char id[MAX_NUM_CHARS_IN_TARGET_ID]; +} SkeletonTargetAnimationData; + +/// @brief Stores the information regarding the glove data used to animate the skeleton. +typedef struct SkeletonTargetGloveData +{ + uint32_t gloveID; //default = 0; +} SkeletonTargetGloveData; + +/// @brief Stores all the possible skeleton settings. +typedef struct SkeletonSettings +{ + bool scaleToTarget; //default = false; + bool useEndPointApproximations; //default = false; + CollisionType collisionType; //default = CollisionType::CollisionType_None + + SkeletonTargetType targetType; + SkeletonTargetUserData skeletonTargetUserData; + SkeletonTargetUserIndexData skeletonTargetUserIndexData; + SkeletonTargetAnimationData skeletonTargetAnimationData; + SkeletonTargetGloveData skeletonGloveData; +} SkeletonSettings; + +/// @brief Stores the skeleton setup information. +typedef struct SkeletonSetupInfo +{ + uint32_t id; //default = 0; + SkeletonType type; //default = SkeletonType::SkeletonType_Invalid; + SkeletonSettings settings; + char name[MAX_NUM_CHARS_IN_SKELETON_NAME]; // this is a UTF8 string , NOT an ASCII CHAR array (same base type though) +} SkeletonSetupInfo; + + +/// @brief Stores the amount of nodes and chains in the skeleton setup. +typedef struct SkeletonSetupArraySizes +{ + uint32_t nodesCount; //default = 0; + uint32_t chainsCount; //default = 0; + uint32_t collidersCount; //default = 0; + uint32_t meshCount; //default = 0; +} SkeletonSetupArraySizes; + +/// @brief Stores the temporary skeleton information. +typedef struct TemporarySkeletonInfo +{ + char name[MAX_NUM_CHARS_IN_SKELETON_NAME]; // this is a UTF8 string , NOT an ASCII CHAR array (same base type though) + uint32_t index; //default = UINT32_MAX; +} TemporarySkeletonInfo; + +/// @brief Stores the temporary skeletons available for a specific session. +typedef struct TemporarySkeletonsInfoForSession +{ + uint32_t sessionId; //default = 0; + char sessionName[MAX_NUM_CHARS_IN_HOST_NAME]; + uint32_t skeletonCount; //default = 0; + TemporarySkeletonInfo skeletonInfo[MAX_NUMBER_OF_SKELETONS_PER_SESSION]; +} TemporarySkeletonsInfoForSession; + +/// @brief Stores the temporary skeletons available for a specific session. +typedef struct TemporarySkeletonCountForSession +{ + uint32_t sessionId; //default = 0; + char sessionName[MAX_NUM_CHARS_IN_HOST_NAME]; + uint32_t skeletonCount; //default = 0; +} TemporarySkeletonCountForSession; + +/// @brief Stores the temporary skeleton available for all sessions connected to Core. +typedef struct TemporarySkeletonCountForAllSessions +{ + uint32_t sessionsCount; //default = 0; + TemporarySkeletonCountForSession temporarySkeletonCountForSessions[MAX_NUMBER_OF_SESSIONS]; +} TemporarySkeletonCountForAllSessions; + +/// @brief ONLY USED INTERNALLY +/// @brief Stores the temporary skeleton available for all sessions connected to Core. +typedef struct TemporarySkeletonSessionsData +{ + uint32_t sessionsCount; //default = 0; + TemporarySkeletonsInfoForSession temporarySkeletonsSessions[MAX_NUMBER_OF_SESSIONS]; +} TemporarySkeletonSessionsData; + +/// @brief Stores the data associated to System messages received from Core. +typedef struct SystemMessage +{ + SystemMessageType type; + char infoString[MAX_NUM_CHARS_IN_SYSTEM_ERROR_MESSAGE]; + uint32_t infoUInt; +} SystemMessage; + +typedef struct NodeInfo +{ + uint32_t nodeId; //default = 0; + uint32_t parentId; //default = 0; + ChainType chainType; + Side side; + FingerJointType fingerJointType; +}NodeInfo; + +// ------------------------------------------------------------------------------------------------------------------------------ +// end of Skeleton +// ------------------------------------------------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------------------------------------------------ +// Glove calibration +// ------------------------------------------------------------------------------------------------------------------------------ + +typedef struct GloveCalibrationArgs { + uint32_t gloveId; //default = 0; +} GloveCalibrationArgs; + +typedef struct GloveCalibrationStepArgs { + uint32_t gloveId; //default = 0; + uint32_t stepIndex; //default = 0; +} GloveCalibrationStepArgs; + +/// @brief Stores the data associated to a single calibration step for a specific glove. +/// if the time value is negative, it means that its a continuous step and the value is estimated +typedef struct GloveCalibrationStepData { + uint32_t index; //default = 0; + char title[MAX_NUM_CHARS_IN_CALIBRATION_TITLE]; + char description[MAX_NUM_CHARS_IN_CALIBRATION_DESCRIPTION]; + float time; //default = 0 +} GloveCalibrationStepData; + +// ------------------------------------------------------------------------------------------------------------------------------ +// end of Glove calibration +// ------------------------------------------------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------------------------------------------------ +// Coordinate system settings +// ------------------------------------------------------------------------------------------------------------------------------ + +/// @brief Stores the information regarding the coordinate system used by the client, defined as VUH (view, up, handedness). +typedef struct CoordinateSystemVUH +{ + AxisView view; + AxisPolarity up; + Side handedness; + float unitScale; // in meters +} CoordinateSystemVUH; + +/// @brief Stores the information regarding the coordinate system used by the client, defined by each axis direction. +typedef struct CoordinateSystemDirection +{ + AxisDirection x; + AxisDirection y; + AxisDirection z; + float unitScale; // in meters +} CoordinateSystemDirection; + +// ------------------------------------------------------------------------------------------------------------------------------ +// end of Coordinate system settings +// ------------------------------------------------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------------------------------------------------ +// Callbacks +// ------------------------------------------------------------------------------------------------------------------------------ + +/// @brief Describes the ConnectedToCoreCallback function type +typedef void(*ConnectedToCoreCallback_t)(const ManusHost* const p_Host); +/// @brief Describes the DisconnectedToCoreCallback function type +typedef void(*DisconnectedFromCoreCallback_t)(const ManusHost* const p_Host); +/// @brief Describes the LoggingCallback function type +typedef void(*LoggingCallback_t)(LogSeverity p_Severity, const char* const p_Log, uint32_t p_Length); +/// @brief Describes the SkeletonStreamCallback function type +typedef void(*SkeletonStreamCallback_t)(const SkeletonStreamInfo* const p_SkeletonInfo); +/// @brief Describes the LandscapeStreamCallback function type +typedef void(*LandscapeStreamCallback_t)(const Landscape* const p_Landscape); +/// @brief Describes the TrackerStreamCallback function type +typedef void(*TrackerStreamCallback_t)(const TrackerStreamInfo* const p_TrackerStreamInfo); +/// @brief Describes the ErgonomicsStreamCallback type +typedef void(*ErgonomicsStreamCallback_t)(const ErgonomicsStream* const p_Ergonomics); +/// @brief Describes the SystemStreamCallback function type +typedef void(*SystemStreamCallback_t)(const SystemMessage* const p_SystemMessage); +/// @brief Describes the RawSkeletonStreamCallback function type +typedef void(*RawSkeletonStreamCallback_t)(const SkeletonStreamInfo* const p_SkeletonInfo); +/// @brief Describes the GestureStreamCallback function type +typedef void(*GestureStreamCallback_t)(const GestureStreamInfo* const p_GestureStream); + +// ------------------------------------------------------------------------------------------------------------------------------ +// end of Callbacks +// ------------------------------------------------------------------------------------------------------------------------------ +#ifdef __cplusplus +} // extern "C" +#endif + +// Close the Doxygen group. +/** @} */ + +#endif // #ifndef H_CORESDKWRAPPERTYPES diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/lib/libManusSDK.so b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/lib/libManusSDK.so new file mode 100644 index 0000000..0a4797d --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/lib/libManusSDK.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b1676a140e00368def349ce55f3990fe0fc4be3fd0a3284ad95885733b7229f +size 80253728 diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/lib/libManusSDK_Integrated.so b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/lib/libManusSDK_Integrated.so new file mode 100644 index 0000000..0a4797d --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusSDK/lib/libManusSDK_Integrated.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b1676a140e00368def349ce55f3990fe0fc4be3fd0a3284ad95885733b7229f +size 80253728 diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusServer.cpython-310-x86_64-linux-gnu.so b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusServer.cpython-310-x86_64-linux-gnu.so new file mode 100755 index 0000000..1f84aec --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/ManusServer.cpython-310-x86_64-linux-gnu.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e24785ebe724751e28605df17755b8f730357ee14f09d89be3e21c3d3d95310b +size 3820896 diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/Readme.md b/gr00t_wbc/control/teleop/device/SDKClient_Linux/Readme.md new file mode 100644 index 0000000..4da2914 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/Readme.md @@ -0,0 +1,4 @@ +# MANUS SDK Client - Linux +The MANUS SDK Client is an example client that demonstrates the most functionality of the MANUS SDK. It handles setting up a connection to the gloves (be it directly or via a MANUS Core instance). And implements all major features available in the MANUS SDK. + +For a full guide on how to get started please refer to the guide on our knowledge center: https://docs.manus-meta.com/latest/Plugins/SDK/Windows/SDK%20Client/ \ No newline at end of file diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient.cpp b/gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient.cpp new file mode 100644 index 0000000..1cb5aad --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient.cpp @@ -0,0 +1,3118 @@ +#include "SDKClient.hpp" +#include "ManusSDKTypes.h" +#include "ClientLogging.hpp" +#include +#include +#include +#include +#include +#include + +#include "ClientPlatformSpecific.hpp" + +#define GO_TO_DISPLAY(p_Key,p_Function) if (GetKeyDown(p_Key)) { ClearConsole();\ + m_CurrentInteraction = std::bind(&SDKClient::p_Function, this); return ClientReturnCode::ClientReturnCode_Success;} + +#define GO_TO_MENU_IF_REQUESTED() if (GetKeyDown('Q')) { ClearConsole();\ + m_CurrentInteraction = nullptr; return ClientReturnCode::ClientReturnCode_Success;} + +using ManusSDK::ClientLog; + +std::map> output_map; + +SDKClient* SDKClient::s_Instance = nullptr; + +std::vector idleft={3822396207, 3998055887}; +std::vector idright={3762867141, 831307785}; + +SDKClient::SDKClient() +{ + s_Instance = this; + + // using initializers like these ensure that the data is set to its default values. + ErgonomicsData_Init(&m_LeftGloveErgoData); + ErgonomicsData_Init(&m_RightGloveErgoData); + + TestTimestamp(); +} + +SDKClient::~SDKClient() +{ + s_Instance = nullptr; +} + +/// @brief Initialize the sample console and the SDK. +/// This function attempts to resize the console window and then proceeds to initialize the SDK's interface. +ClientReturnCode SDKClient::Initialize() +{ + // if (!PlatformSpecificInitialization()) + // { + // return ClientReturnCode::ClientReturnCode_FailedPlatformSpecificInitialization; + // } + + const ClientReturnCode t_IntializeResult = InitializeSDK(); + if (t_IntializeResult != ClientReturnCode::ClientReturnCode_Success) + { + ClientLog::error("Failed to initialize the SDK. Are you sure the correct ManusSDKLibary is used?"); + return ClientReturnCode::ClientReturnCode_FailedToInitialize; + } + + return ClientReturnCode::ClientReturnCode_Success; +} + +/// @brief The main SDKClient loop. +/// This is a simple state machine which switches between different substates. +ClientReturnCode SDKClient::Run() +{ + ConnectingToCore(); + + return ClientReturnCode::ClientReturnCode_Success; +} + +/// @brief When you are done with the SDK, don't forget to nicely shut it down +/// this will close all connections to the host, close any threads and clean up after itself +/// after this is called it is expected to exit the client program. If not it needs to call initialize again. +ClientReturnCode SDKClient::ShutDown() +{ + const SDKReturnCode t_Result = CoreSdk_ShutDown(); + if (t_Result != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to shut down the SDK wrapper. The value returned was {}.", (int32_t)t_Result); + return ClientReturnCode::ClientReturnCode_FailedToShutDownSDK; + } + + if (!PlatformSpecificShutdown()) + { + return ClientReturnCode::ClientReturnCode_FailedPlatformSpecificShutdown; + } + + return ClientReturnCode::ClientReturnCode_Success; +} + +/// @brief Gets called when the client is connects to manus core +/// Using this callback is optional. +/// In this sample we use this callback to change the client's state and switch to another screen +void SDKClient::OnConnectedCallback(const ManusHost* const p_Host) +{ + ClientLog::print("Connected to manus core."); + + //No need to initialize these as they get filled in the CoreSdk_GetVersionsAndCheckCompatibility + ManusVersion t_SdkVersion; + ManusVersion t_CoreVersion; + bool t_IsCompatible; + + const SDKReturnCode t_Result = CoreSdk_GetVersionsAndCheckCompatibility(&t_SdkVersion, &t_CoreVersion, &t_IsCompatible); + + if (t_Result == SDKReturnCode::SDKReturnCode_Success) + { + const std::string t_Versions = "Sdk version : " + std::string(t_SdkVersion.versionInfo) + ", Core version : " + std::string(t_CoreVersion.versionInfo) + "."; + + if (t_IsCompatible) + { + ClientLog::print("Versions are compatible.{}", t_Versions); + } + else + { + ClientLog::warn("Versions are not compatible with each other.{}", t_Versions); + } + } + else + { + ClientLog::error("Failed to get the versions from the SDK. The value returned was {}.", (int32_t)t_Result); + } + + uint32_t t_SessionId; + const SDKReturnCode t_SessionIdResult = CoreSdk_GetSessionId(&t_SessionId); + if (t_SessionIdResult == SDKReturnCode::SDKReturnCode_Success && t_SessionId != 0) + { + ClientLog::print("Session Id: {}", t_SessionId); + s_Instance->m_SessionId = t_SessionId; + } + else + { + ClientLog::print("Failed to get the Session ID from Core. The value returned was{}.", (int32_t)t_SessionIdResult); + } + + ManusHost t_Host(*p_Host); + s_Instance->m_Host = std::make_unique(t_Host); + + // Set the hand motion mode of the RawSkeletonStream. This is optional and can be set to any of the HandMotion enum values. Default = None + const SDKReturnCode t_HandMotionResult = CoreSdk_SetRawSkeletonHandMotion(HandMotion_Auto); + if (t_HandMotionResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::print("Failed to set the hand motion mode. The value returned was {}.", (int32_t)t_HandMotionResult); + } + + // Only setting state to displaying data on automatic reconnect + if (s_Instance->m_State == ClientState::ClientState_Disconnected) + { + s_Instance->m_State = ClientState::ClientState_DisplayingData; + } +} + +/// @brief Gets called when the client disconnects from manus core. +/// This callback is optional and in the sample changes the client's state. +void SDKClient::OnDisconnectedCallback(const ManusHost* const p_Host) +{ + ClientLog::print("Disconnected from manus core."); + s_Instance->m_TimeSinceLastDisconnect = std::chrono::high_resolution_clock::now(); + ManusHost t_Host(*p_Host); + s_Instance->m_Host = std::make_unique(t_Host); + s_Instance->m_State = ClientState::ClientState_Disconnected; +} + +void SDKClient::OnLogCallback(LogSeverity p_Severity, const char* const p_Log, uint32_t p_Length) +{ + if (!s_Instance)return; + + auto t_Log = new SDKLog(); + t_Log->severity = p_Severity; + t_Log->string = std::string(p_Log); + s_Instance->m_LogMutex.lock(); + s_Instance->m_Logs.push_back(t_Log); + s_Instance->m_LogMutex.unlock(); +} + +/// @brief This gets called when the client is connected to manus core +/// @param p_SkeletonStreamInfo contains the meta data on how much data regarding the skeleton we need to get from the SDK. +void SDKClient::OnSkeletonStreamCallback(const SkeletonStreamInfo* const p_SkeletonStreamInfo) +{ + if (s_Instance) + { + ClientSkeletonCollection* t_NxtClientSkeleton = new ClientSkeletonCollection(); + t_NxtClientSkeleton->skeletons.resize(p_SkeletonStreamInfo->skeletonsCount); + + for (uint32_t i = 0; i < p_SkeletonStreamInfo->skeletonsCount; i++) + { + CoreSdk_GetSkeletonInfo(i, &t_NxtClientSkeleton->skeletons[i].info); + t_NxtClientSkeleton->skeletons[i].nodes.resize(t_NxtClientSkeleton->skeletons[i].info.nodesCount); + t_NxtClientSkeleton->skeletons[i].info.publishTime = p_SkeletonStreamInfo->publishTime; + CoreSdk_GetSkeletonData(i, t_NxtClientSkeleton->skeletons[i].nodes.data(), t_NxtClientSkeleton->skeletons[i].info.nodesCount); + } + s_Instance->m_SkeletonMutex.lock(); + if (s_Instance->m_NextSkeleton != nullptr) delete s_Instance->m_NextSkeleton; + s_Instance->m_NextSkeleton = t_NxtClientSkeleton; + s_Instance->m_SkeletonMutex.unlock(); + } +} + +/// @brief This gets called when the client is connected to manus core. It sends the skeleton data coming from the estimation system, before the retargeting to the client skeleton model. +/// @param p_RawSkeletonStreamInfo contains the meta data on how much data regarding the raw skeleton we need to get from the SDK. +void SDKClient::OnRawSkeletonStreamCallback(const SkeletonStreamInfo* const p_RawSkeletonStreamInfo) +{ + // std::cout<<"OnRawSkeletonStreamCallback"<skeletons.resize(p_RawSkeletonStreamInfo->skeletonsCount); + + for (uint32_t i = 0; i < p_RawSkeletonStreamInfo->skeletonsCount; i++) + { + CoreSdk_GetRawSkeletonInfo(i, &t_NxtClientRawSkeleton->skeletons[i].info); + t_NxtClientRawSkeleton->skeletons[i].nodes.resize(t_NxtClientRawSkeleton->skeletons[i].info.nodesCount); + t_NxtClientRawSkeleton->skeletons[i].info.publishTime = p_RawSkeletonStreamInfo->publishTime; + CoreSdk_GetRawSkeletonData(i, t_NxtClientRawSkeleton->skeletons[i].nodes.data(), t_NxtClientRawSkeleton->skeletons[i].info.nodesCount); + + std::string glove_id = std::to_string(t_NxtClientRawSkeleton->skeletons[i].info.gloveId); + std::vector positions; + std::vector orientations; + for (uint32_t j = 0; j < t_NxtClientRawSkeleton->skeletons[i].info.nodesCount; j++){ + positions.push_back(t_NxtClientRawSkeleton->skeletons[i].nodes[j].transform.position.x); + positions.push_back(t_NxtClientRawSkeleton->skeletons[i].nodes[j].transform.position.y); + positions.push_back(t_NxtClientRawSkeleton->skeletons[i].nodes[j].transform.position.z); + orientations.push_back(t_NxtClientRawSkeleton->skeletons[i].nodes[j].transform.rotation.w); + orientations.push_back(t_NxtClientRawSkeleton->skeletons[i].nodes[j].transform.rotation.x); + orientations.push_back(t_NxtClientRawSkeleton->skeletons[i].nodes[j].transform.rotation.y); + orientations.push_back(t_NxtClientRawSkeleton->skeletons[i].nodes[j].transform.rotation.z); + } + output_map[glove_id +"_position"] = positions; + output_map[glove_id +"_orientation"] = orientations; + } + s_Instance->m_RawSkeletonMutex.lock(); + if (s_Instance->m_NextRawSkeleton != nullptr) delete s_Instance->m_NextRawSkeleton; + s_Instance->m_NextRawSkeleton = t_NxtClientRawSkeleton; + s_Instance->m_RawSkeletonMutex.unlock(); + } +} + +/// @brief This gets called when receiving tracker information from core +/// @param p_TrackerStreamInfo contains the meta data on how much data regarding the trackers we need to get from the SDK. +void SDKClient::OnTrackerStreamCallback(const TrackerStreamInfo* const p_TrackerStreamInfo) +{ + if (s_Instance) + { + TrackerDataCollection* t_TrackerData = new TrackerDataCollection(); + + t_TrackerData->trackerData.resize(p_TrackerStreamInfo->trackerCount); + + for (uint32_t i = 0; i < p_TrackerStreamInfo->trackerCount; i++) + { + CoreSdk_GetTrackerData(i, &t_TrackerData->trackerData[i]); + } + s_Instance->m_TrackerMutex.lock(); + if (s_Instance->m_NextTrackerData != nullptr) delete s_Instance->m_NextTrackerData; + s_Instance->m_NextTrackerData = t_TrackerData; + s_Instance->m_TrackerMutex.unlock(); + } +} + +/// @brief This gets called when receiving gesture data from Manus Core +/// In our sample we only save the first glove's gesture data. +/// Gesture data gets generated and sent when glove data changes, this means that the stream +/// does not always contain ALL of the devices, because some may not have had new data since +/// the last time the gesture data was sent. +/// @param p_GestureStream contains the basic info to retrieve gesture data. +void SDKClient::OnGestureStreamCallback(const GestureStreamInfo* const p_GestureStream) +{ + if (s_Instance) + { + for (uint32_t i = 0; i < p_GestureStream->gestureProbabilitiesCount; i++) + { + GestureProbabilities t_Probs; + CoreSdk_GetGestureStreamData(i, 0, &t_Probs); + if (t_Probs.isUserID)continue; + if (t_Probs.id != s_Instance->m_FirstLeftGloveID && t_Probs.id != s_Instance->m_FirstRightGloveID)continue; + ClientGestures* t_Gest = new ClientGestures(); + t_Gest->info = t_Probs; + t_Gest->probabilities.reserve(t_Gest->info.totalGestureCount); + uint32_t t_BatchCount = (t_Gest->info.totalGestureCount / MAX_GESTURE_DATA_CHUNK_SIZE) + 1; + uint32_t t_ProbabilityIdx = 0; + for (uint32_t b = 0; b < t_BatchCount; b++) + { + for (uint32_t j = 0; j < t_Probs.gestureCount; j++) + { + t_Gest->probabilities.push_back(t_Probs.gestureData[j]); + } + t_ProbabilityIdx += t_Probs.gestureCount; + CoreSdk_GetGestureStreamData(i, t_ProbabilityIdx, &t_Probs); //this will get more data, if needed for the next iteration. + } + + s_Instance->m_GestureMutex.lock(); + if (t_Probs.id == s_Instance->m_FirstLeftGloveID) + { + if (s_Instance->m_NewFirstLeftGloveGestures != nullptr) delete s_Instance->m_NewFirstLeftGloveGestures; + s_Instance->m_NewFirstLeftGloveGestures = t_Gest; + } + else + { + if (s_Instance->m_NewFirstRightGloveGestures != nullptr) delete s_Instance->m_NewFirstRightGloveGestures; + s_Instance->m_NewFirstRightGloveGestures = t_Gest; + } + s_Instance->m_GestureMutex.unlock(); + } + } +} + +/// @brief This gets called when receiving landscape information from core +/// @param p_Landscape contains the new landscape from core. +void SDKClient::OnLandscapeCallback(const Landscape* const p_Landscape) +{ + if (s_Instance == nullptr)return; + + Landscape* t_Landscape = new Landscape(*p_Landscape); + s_Instance->m_LandscapeMutex.lock(); + if (s_Instance->m_NewLandscape != nullptr) delete s_Instance->m_NewLandscape; + s_Instance->m_NewLandscape = t_Landscape; + s_Instance->m_NewGestureLandscapeData.resize(t_Landscape->gestureCount); + CoreSdk_GetGestureLandscapeData(s_Instance->m_NewGestureLandscapeData.data(), (uint32_t)s_Instance->m_NewGestureLandscapeData.size()); + s_Instance->m_LandscapeMutex.unlock(); +} + + +/// @brief This gets called when receiving a system message from Core. +/// @param p_SystemMessage contains the system message received from core. +void SDKClient::OnSystemCallback(const SystemMessage* const p_SystemMessage) +{ + if (s_Instance) + { + s_Instance->m_SystemMessageMutex.lock(); + + switch (p_SystemMessage->type) + { + case SystemMessageType::SystemMessageType_TemporarySkeletonModified: + // if the message was triggered by a temporary skeleton being modified then save the skeleton index, + // this information will be used to get and load the skeleton into core + s_Instance->m_ModifiedSkeletonIndex = p_SystemMessage->infoUInt; + break; + default: + s_Instance->m_SystemMessageCode = p_SystemMessage->type; + s_Instance->m_SystemMessage = p_SystemMessage->infoString; + break; + } + s_Instance->m_SystemMessageMutex.unlock(); + } +} + +/// @brief This gets called when receiving ergonomics data from Manus Core +/// In our sample we only save the first left and first right glove's latests ergonomics data. +/// Ergonomics data gets generated and sent when glove data changes, this means that the stream +/// does not always contain ALL of the devices, because some may not have had new data since +/// the last time the ergonomics data was sent. +/// @param p_Ergo contains the ergonomics data for each glove connected to Core. +void SDKClient::OnErgonomicsCallback(const ErgonomicsStream* const p_Ergo) +{ + if (s_Instance) + { + long int left_glove_id = 0; + long int right_glove_id = 0; + for (uint32_t i = 0; i < p_Ergo->dataCount; i++) + { + if (p_Ergo->data[i].isUserID)continue; + + ErgonomicsData* t_Ergo = nullptr; + if (std::find(idleft.begin(), idleft.end(), p_Ergo->data[i].id) != idleft.end()) + { + t_Ergo = &s_Instance->m_LeftGloveErgoData; + left_glove_id = p_Ergo->data[i].id; + } + if (std::find(idright.begin(), idright.end(), p_Ergo->data[i].id) != idright.end()) + { + t_Ergo = &s_Instance->m_RightGloveErgoData; + right_glove_id = p_Ergo->data[i].id; + } + if (t_Ergo == nullptr)continue; + CoreSdk_GetTimestampInfo(p_Ergo->publishTime, &s_Instance->m_ErgoTimestampInfo); + t_Ergo->id = p_Ergo->data[i].id; + t_Ergo->isUserID = p_Ergo->data[i].isUserID; + // Create a vector to store the angles of the hand + std::vector orientations_left; + std::vector orientations_right; + for (int j = 0; j < ErgonomicsDataType::ErgonomicsDataType_MAX_SIZE; j++) + { + t_Ergo->data[j] = p_Ergo->data[i].data[j]; + double roundedValue = s_Instance->RoundFloatValue(t_Ergo->data[j], 2); + if (t_Ergo == &s_Instance->m_LeftGloveErgoData) + { + orientations_left.push_back(roundedValue); + } + else if (t_Ergo == &s_Instance->m_RightGloveErgoData) + { + orientations_right.push_back(roundedValue); + } + } + if(left_glove_id!=0){ + output_map[std::to_string(left_glove_id)+"_angle"] = orientations_left; + } + if(right_glove_id!=0){ + output_map[std::to_string(right_glove_id)+"_angle"] = orientations_right; + } + } + } +} + +/// @brief Round the given float value so that it has no more than the given number of decimals. +float SDKClient::RoundFloatValue(float p_Value, int p_NumDecimalsToKeep) +{ + // Since C++11, powf is supposed to be declared in . + // Unfortunately, gcc decided to be non-compliant on this for no apparent + // reason, so now we have to do this. + // https://stackoverflow.com/questions/5483930/powf-is-not-a-member-of-std + float t_Power = static_cast(std::pow( + 10.0, + static_cast(p_NumDecimalsToKeep))); + return std::round(p_Value * t_Power) / t_Power; +} + +/// @brief Set the position that the next log message will appear at. +/// Using this allows us to have somewhat of a static, yet flexible layout of logging. +void SDKClient::AdvanceConsolePosition(short int p_Y) +{ + if (p_Y < 0) + { + m_ConsoleCurrentOffset = 0; + } + else + { + m_ConsoleCurrentOffset += p_Y; + } + + ApplyConsolePosition(m_ConsoleCurrentOffset); +} + +/// @brief Initialize the sdk, register the callbacks and set the coordinate system. +/// This needs to be done before any of the other SDK functions can be used. +ClientReturnCode SDKClient::InitializeSDK() +{ + // ClientLog::print("Select what mode you would like to start in (and press enter to submit)"); + // ClientLog::print("[1] Core Integrated - This will run standalone without the need for a MANUS Core connection"); + // ClientLog::print("[2] Core Local - This will connect to a MANUS Core running locally on your machine"); + // ClientLog::print("[3] Core Remote - This will search for a MANUS Core running locally on your network"); + // std::string t_ConnectionTypeInput; + // std::cin >> t_ConnectionTypeInput; + + std::string t_ConnectionTypeInput = "1"; + + switch (t_ConnectionTypeInput[0]) + { + case '1': + m_ConnectionType = ConnectionType::ConnectionType_Integrated; + m_State = ClientState::ClientState_ConnectingToCore; + break; + case '2': + m_ConnectionType = ConnectionType::ConnectionType_Local; + m_State = ClientState::ClientState_LookingForHosts; + break; + case '3': + m_ConnectionType = ConnectionType::ConnectionType_Remote; + m_State = ClientState::ClientState_LookingForHosts; + break; + default: + m_ConnectionType = ConnectionType::ConnectionType_Invalid; + m_State = ClientState::ClientState_Starting; + ClientLog::print("Invalid input, try again"); + return InitializeSDK(); + } + + // Invalid connection type detected + if (m_ConnectionType == ConnectionType::ConnectionType_Invalid + || m_ConnectionType == ConnectionType::ClientState_MAX_CLIENT_STATE_SIZE) + return ClientReturnCode::ClientReturnCode_FailedToInitialize; + + // before we can use the SDK, some internal SDK bits need to be initialized. + // however after initializing, the SDK is not yet connected to a host or doing anything network related just yet. + bool t_Remote = m_ConnectionType != ConnectionType::ConnectionType_Integrated; + const SDKReturnCode t_InitializeResult = CoreSdk_Initialize(m_ClientType, t_Remote); + if (t_InitializeResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to initialize the Manus Core SDK. The value returned was {}.", (int32_t)t_InitializeResult); + return ClientReturnCode::ClientReturnCode_FailedToInitialize; + } + + const ClientReturnCode t_CallBackResults = RegisterAllCallbacks(); + if (t_CallBackResults != ::ClientReturnCode::ClientReturnCode_Success) + { + ClientLog::error("Failed to initialize callbacks."); + return t_CallBackResults; + } + + // after everything is registered and initialized as seen above + // we must also set the coordinate system being used for the data in this client. + // (each client can have their own settings. unreal and unity for instance use different coordinate systems) + // if this is not set, the SDK will not connect to any Manus core host. + // The coordinate system used for this example is z-up, x-positive, right-handed and in meter scale. + CoordinateSystemVUH t_VUH; + CoordinateSystemVUH_Init(&t_VUH); + t_VUH.handedness = Side::Side_Right; + t_VUH.up = AxisPolarity::AxisPolarity_PositiveZ; + t_VUH.view = AxisView::AxisView_XFromViewer; + t_VUH.unitScale = 1.0f; //1.0 is meters, 0.01 is cm, 0.001 is mm. + + // The above specified coordinate system is used to initialize and the coordinate space is specified (world/local). + const SDKReturnCode t_CoordinateResult = CoreSdk_InitializeCoordinateSystemWithVUH(t_VUH, true); + + /* this is an example if you want to use the other coordinate system instead of VUH (view, up, handedness) + CoordinateSystemDirection t_Direction; + t_Direction.x = AxisDirection::AD_Right; + t_Direction.y = AxisDirection::AD_Up; + t_Direction.z = AxisDirection::AD_Forward; + const SDKReturnCode t_InitializeResult = CoreSdk_InitializeCoordinateSystemWithDirection(t_Direction, true); + */ + + if (t_CoordinateResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to initialize the Manus Core SDK coordinate system. The value returned was {}.", (int32_t)t_InitializeResult); + return ClientReturnCode::ClientReturnCode_FailedToInitialize; + } + + return ClientReturnCode::ClientReturnCode_Success; +} + +/// @brief Used to restart and initialize the SDK to make sure a new connection can be set up. +/// This function is used by the client to shutdown the SDK and any connections it has. +/// After that it reinitializes the SDK so that it is ready to connect to a new Manus Core. +ClientReturnCode SDKClient::RestartSDK() +{ + const SDKReturnCode t_ShutDownResult = CoreSdk_ShutDown(); + if (t_ShutDownResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to shutdown the SDK. The value returned was {}.", (int32_t)t_ShutDownResult); + return ClientReturnCode::ClientReturnCode_FailedToShutDownSDK; + } + + const ClientReturnCode t_IntializeResult = InitializeSDK(); + if (t_IntializeResult != ClientReturnCode::ClientReturnCode_Success) + { + ClientLog::error("Failed to initialize the SDK functionality. The value returned was {}.", (int32_t)t_IntializeResult); + return ClientReturnCode::ClientReturnCode_FailedToInitialize; + } + + return ClientReturnCode::ClientReturnCode_Success; +} + +/// @brief Used to register the callbacks between sdk and core. +/// Callbacks that are registered functions that get called when a certain 'event' happens, such as data coming in from Manus Core. +/// All of these are optional, but depending on what data you require you may or may not need all of them. +ClientReturnCode SDKClient::RegisterAllCallbacks() +{ + // Register the callback for when manus core is connected to the SDK + // it is optional, but helps trigger your client nicely if needed. + // see the function OnConnectedCallback for more details + const SDKReturnCode t_RegisterConnectCallbackResult = CoreSdk_RegisterCallbackForOnConnect(*OnConnectedCallback); + if (t_RegisterConnectCallbackResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to register callback function for after connecting to Manus Core. The value returned was {}.", (int32_t)t_RegisterConnectCallbackResult); + return ClientReturnCode::ClientReturnCode_FailedToInitialize; + } + + // Register the callback for when manus core is disconnected to the SDK + // it is optional, but helps trigger your client nicely if needed. + // see OnDisconnectedCallback for more details. + const SDKReturnCode t_RegisterDisconnectCallbackResult = CoreSdk_RegisterCallbackForOnDisconnect(*OnDisconnectedCallback); + if (t_RegisterDisconnectCallbackResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to register callback function for after disconnecting from Manus Core. The value returned was {}.", (int32_t)t_RegisterDisconnectCallbackResult); + return ClientReturnCode::ClientReturnCode_FailedToInitialize; + } + + // Register the callback for logging from the SDK + // it is optional, but it might help display the logs at the right time/place + const SDKReturnCode t_RegisterLogCallbackResult = CoreSdk_RegisterCallbackForOnLog(*OnLogCallback); + if (t_RegisterLogCallbackResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to register callback function for logging from the SDK. The value returned was {}.", (int32_t)t_RegisterLogCallbackResult); + return ClientReturnCode::ClientReturnCode_FailedToInitialize; + } + + // Register the callback for when manus core is sending Skeleton data + // it is optional, but without it you can not see any resulting skeleton data. + // see OnSkeletonStreamCallback for more details. + const SDKReturnCode t_RegisterSkeletonCallbackResult = CoreSdk_RegisterCallbackForSkeletonStream(*OnSkeletonStreamCallback); + if (t_RegisterSkeletonCallbackResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to register callback function for processing skeletal data from Manus Core. The value returned was {}.", (int32_t)t_RegisterSkeletonCallbackResult); + return ClientReturnCode::ClientReturnCode_FailedToInitialize; + } + + // Register the callback for when manus core is sending landscape data + // it is optional, but this allows for a reactive adjustment of device information. + const SDKReturnCode t_RegisterLandscapeCallbackResult = CoreSdk_RegisterCallbackForLandscapeStream(*OnLandscapeCallback); + if (t_RegisterLandscapeCallbackResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to register callback for landscape from Manus Core. The value returned was {}.", (int32_t)t_RegisterLandscapeCallbackResult); + return ClientReturnCode::ClientReturnCode_FailedToInitialize; + } + + // Register the callback for when manus core is sending System messages + // This is usually not used by client applications unless they want to show errors/events from core. + // see OnSystemCallback for more details. + const SDKReturnCode t_RegisterSystemCallbackResult = CoreSdk_RegisterCallbackForSystemStream(*OnSystemCallback); + if (t_RegisterSystemCallbackResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to register callback function for system feedback from Manus Core. The value returned was {}.", (int32_t)t_RegisterSystemCallbackResult); + return ClientReturnCode::ClientReturnCode_FailedToInitialize; + } + + // Register the callback for when manus core is sending Ergonomics data + // it is optional, but helps trigger your client nicely if needed. + // see OnErgonomicsCallback for more details. + const SDKReturnCode t_RegisterErgonomicsCallbackResult = CoreSdk_RegisterCallbackForErgonomicsStream(*OnErgonomicsCallback); + if (t_RegisterErgonomicsCallbackResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to register callback function for ergonomics data from Manus Core. The value returned was {}.", (int32_t)t_RegisterErgonomicsCallbackResult); + return ClientReturnCode::ClientReturnCode_FailedToInitialize; + } + + // Register the callback for when manus core is sending Raw Skeleton data + // it is optional, but without it you can not see any resulting skeleton data. + // see OnSkeletonStreamCallback for more details. + const SDKReturnCode t_RegisterRawSkeletonCallbackResult = CoreSdk_RegisterCallbackForRawSkeletonStream(*OnRawSkeletonStreamCallback); + if (t_RegisterRawSkeletonCallbackResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to register callback function for processing raw skeletal data from Manus Core. The value returned was {}.", (int32_t)t_RegisterRawSkeletonCallbackResult); + return ClientReturnCode::ClientReturnCode_FailedToInitialize; + } + + // Register the callback for when manus core is sending Tracker data + // it is optional, but without it you can not see any resulting tracker data. + // see OnTrackerStreamCallback for more details. + const SDKReturnCode t_RegisterTrackerCallbackResult = CoreSdk_RegisterCallbackForTrackerStream(*OnTrackerStreamCallback); + if (t_RegisterTrackerCallbackResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to register callback function for processing tracker data from Manus Core. The value returned was {}.", (int32_t)t_RegisterTrackerCallbackResult); + return ClientReturnCode::ClientReturnCode_FailedToInitialize; + } + + // Register the callback for when manus core is sending Raw Skeleton data + // it is optional, but without it you can not see any resulting skeleton data. + // see OnGestureStreamCallback for more details. + const SDKReturnCode t_RegisterGestureCallbackResult = CoreSdk_RegisterCallbackForGestureStream(*OnGestureStreamCallback); + if (t_RegisterGestureCallbackResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to register callback function for processing gesture data from Manus Core. The value returned was {}.", (int32_t)t_RegisterGestureCallbackResult); + return ClientReturnCode::ClientReturnCode_FailedToInitialize; + } + + return ClientReturnCode::ClientReturnCode_Success; +} + +/// @brief Simple example of the SDK looking for manus core hosts on the network +/// and display them on screen. +ClientReturnCode SDKClient::LookingForHosts() +{ + ClientLog::print("Looking for hosts..."); + + // Underlying function will sleep for m_SecondsToFindHosts to allow servers to reply. + bool t_ConnectLocally = m_ConnectionType == ConnectionType::ConnectionType_Local; + const SDKReturnCode t_StartResult = CoreSdk_LookForHosts(m_SecondsToFindHosts, t_ConnectLocally); + if (t_StartResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to look for hosts. The error given was {}.", (int32_t)t_StartResult); + + return ClientReturnCode::ClientReturnCode_FailedToFindHosts; + } + + m_NumberOfHostsFound = 0; + const SDKReturnCode t_NumberResult = CoreSdk_GetNumberOfAvailableHostsFound(&m_NumberOfHostsFound); + if (t_NumberResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get the number of available hosts. The error given was {}.", (int32_t)t_NumberResult); + + return ClientReturnCode::ClientReturnCode_FailedToFindHosts; + } + + if (m_NumberOfHostsFound == 0) + { + ClientLog::warn("No hosts found."); + m_State = ClientState::ClientState_NoHostsFound; + + return ClientReturnCode::ClientReturnCode_FailedToFindHosts; + } + + m_AvailableHosts.reset(new ManusHost[m_NumberOfHostsFound]); + const SDKReturnCode t_HostsResult = CoreSdk_GetAvailableHostsFound(m_AvailableHosts.get(), m_NumberOfHostsFound); + if (t_HostsResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get the available hosts. The error given was {}.", (int32_t)t_HostsResult); + + return ClientReturnCode::ClientReturnCode_FailedToFindHosts; + } + + if (t_ConnectLocally) + { + m_State = ClientState::ClientState_ConnectingToCore; + return ClientReturnCode::ClientReturnCode_Success; + } + + m_State = ClientState::ClientState_PickingHost; + return ClientReturnCode::ClientReturnCode_Success; +} + +/// @brief When no available hosts are found the user can either retry or exit. +ClientReturnCode SDKClient::NoHostsFound() +{ + if (m_ConsoleClearTickCount == 0) + { + AdvanceConsolePosition(-1); + ClientLog::print("No hosts were found. Retry?"); + ClientLog::print("[R] retry"); + ClientLog::print("[ESC] exit"); + } + + if (GetKeyDown('R')) + { + ClientLog::print("Retrying."); + + m_State = ClientState::ClientState_LookingForHosts; + } + + // Note: escape is handled by default below. + return ClientReturnCode::ClientReturnCode_Success; +} + +/// @brief Print the found hosts and give the user the option to select one. +ClientReturnCode SDKClient::PickingHost() +{ + if (m_ConsoleClearTickCount == 0) + { + AdvanceConsolePosition(-1); + + ClientLog::print("[R] retry [ESC] exit"); + ClientLog::print("Pick a host to connect to."); + ClientLog::print("Found the following hosts:"); + + // Note: only 10 hosts are shown, to match the number of number keys, for easy selection. + for (unsigned int t_HostNumber = 0; t_HostNumber < 10 && t_HostNumber < m_NumberOfHostsFound; t_HostNumber++) + { + ClientLog::print( + "[{}] hostname \"{}\", IP address \"{}\" Version {}.{}.{}", + t_HostNumber, + m_AvailableHosts[t_HostNumber].hostName, + m_AvailableHosts[t_HostNumber].ipAddress, + m_AvailableHosts[t_HostNumber].manusCoreVersion.major, + m_AvailableHosts[t_HostNumber].manusCoreVersion.minor, + m_AvailableHosts[t_HostNumber].manusCoreVersion.patch); + } + } + + for (unsigned int t_HostNumber = 0; t_HostNumber < 10 && t_HostNumber < m_NumberOfHostsFound; t_HostNumber++) + { + if (GetKeyDown('0' + t_HostNumber)) + { + ClientLog::print("Selected host {}.", t_HostNumber); + + m_HostToConnectTo = t_HostNumber; + m_State = ClientState::ClientState_ConnectingToCore; + + break; + } + } + + if (GetKeyDown('R')) + { + ClientLog::print("Retrying."); + m_State = ClientState::ClientState_LookingForHosts; + } + + return ClientReturnCode::ClientReturnCode_Success; +} + +/// @brief After a connection option was selected, the client will now try to connect to manus core via the SDK. +ClientReturnCode SDKClient::ConnectingToCore() +{ + SDKReturnCode t_ConnectResult = SDKReturnCode::SDKReturnCode_Error; + switch (m_ConnectionType) + { + case ConnectionType::ConnectionType_Integrated: + { + ManusHost t_Empty; + ManusHost_Init(&t_Empty); + t_ConnectResult = CoreSdk_ConnectToHost(t_Empty); + break; + } + case ConnectionType::ConnectionType_Local: + { + t_ConnectResult = CoreSdk_ConnectToHost(m_AvailableHosts[0]); + break; + } + case ConnectionType::ConnectionType_Remote: + { + t_ConnectResult = CoreSdk_ConnectToHost(m_AvailableHosts[m_HostToConnectTo]); + break; + } + } + + if (t_ConnectResult == SDKReturnCode::SDKReturnCode_NotConnected) + { + m_State = ClientState::ClientState_NoHostsFound; + + return ClientReturnCode::ClientReturnCode_Success; // Differentiating between error and no connect + } + if (t_ConnectResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to connect to Core. The error given was {}.", (int32_t)t_ConnectResult); + + return ClientReturnCode::ClientReturnCode_FailedToConnect; + } + + m_State = ClientState::ClientState_DisplayingData; + + // Note: a log message from somewhere in the SDK during the connection process can cause text + // to permanently turn green after this step. Adding a sleep here of 2+ seconds "fixes" the + // issue. It seems to be caused by a threading issue somewhere, resulting in a log call being + // interrupted while it is printing the green [info] text. The log output then gets stuck in + // green mode. + + return ClientReturnCode::ClientReturnCode_Success; +} + + +/// @brief Some things happen before every display update, no matter what state. +/// They happen here, such as the updating of the landscape and the generated tracker +/// @return +ClientReturnCode SDKClient::UpdateBeforeDisplayingData() +{ + AdvanceConsolePosition(-1); + + m_SkeletonMutex.lock(); + if (m_NextSkeleton != nullptr) + { + if (m_Skeleton != nullptr)delete m_Skeleton; + m_Skeleton = m_NextSkeleton; + m_NextSkeleton = nullptr; + } + m_SkeletonMutex.unlock(); + + m_RawSkeletonMutex.lock(); + if (m_NextRawSkeleton != nullptr) + { + if (m_RawSkeleton != nullptr)delete m_RawSkeleton; + m_RawSkeleton = m_NextRawSkeleton; + m_NextRawSkeleton = nullptr; + } + m_RawSkeletonMutex.unlock(); + + m_TrackerMutex.lock(); + if (m_NextTrackerData != nullptr) + { + if (m_TrackerData != nullptr)delete m_TrackerData; + m_TrackerData = m_NextTrackerData; + m_NextTrackerData = nullptr; + } + m_TrackerMutex.unlock(); + + m_GestureMutex.lock(); + if (m_NewFirstLeftGloveGestures != nullptr) + { + if (m_FirstLeftGloveGestures != nullptr) delete m_FirstLeftGloveGestures; + m_FirstLeftGloveGestures = m_NewFirstLeftGloveGestures; + m_NewFirstLeftGloveGestures = nullptr; + } + if (m_NewFirstRightGloveGestures != nullptr) + { + if (m_FirstRightGloveGestures != nullptr) delete m_FirstRightGloveGestures; + m_FirstRightGloveGestures = m_NewFirstRightGloveGestures; + m_NewFirstRightGloveGestures = nullptr; + } + m_GestureMutex.unlock(); + + m_LandscapeMutex.lock(); + if (m_NewLandscape != nullptr) + { + if (m_Landscape != nullptr) + { + delete m_Landscape; + } + m_Landscape = m_NewLandscape; + m_NewLandscape = nullptr; + m_GestureLandscapeData.swap(m_NewGestureLandscapeData); + } + m_LandscapeMutex.unlock(); + + m_FirstLeftGloveID = 0; + m_FirstRightGloveID = 0; + if (m_Landscape == nullptr)return ClientReturnCode::ClientReturnCode_Success; + for (size_t i = 0; i < m_Landscape->gloveDevices.gloveCount; i++) + { + if (m_FirstLeftGloveID == 0 && m_Landscape->gloveDevices.gloves[i].side == Side::Side_Left) + { + m_FirstLeftGloveID = m_Landscape->gloveDevices.gloves[i].id; + continue; + } + if (m_FirstRightGloveID == 0 && m_Landscape->gloveDevices.gloves[i].side == Side::Side_Right) + { + m_FirstRightGloveID = m_Landscape->gloveDevices.gloves[i].id; + continue; + } + } + + return ClientReturnCode::ClientReturnCode_Success; +} + + +/// @brief Once the connections are made we loop this function +/// it calls all the input handlers for different aspects of the SDK +/// and then prints any relevant data of it. +ClientReturnCode SDKClient::DisplayingData() +{ + ClientLog::print("<
> [ESC] quit"); + ClientLog::print("[G] Go To Gloves & Dongle Menu"); + ClientLog::print("[S] Go To Skeleton Menu"); + ClientLog::print("[X] Go To Temporary Skeleton Menu"); + ClientLog::print("[T] Go To Tracker Menu"); + ClientLog::print("[D] Go To Landscape Time Info"); + ClientLog::print("[J] Go To Gestures Menu"); + ClientLog::print("[C] Go To Glove Calibration Menu"); + ClientLog::print("[P] Go To Pairing Menu"); + + AdvanceConsolePosition(10); + + GO_TO_DISPLAY('G', DisplayingDataGlove) + GO_TO_DISPLAY('S', DisplayingDataSkeleton) + GO_TO_DISPLAY('X', DisplayingDataTemporarySkeleton) + GO_TO_DISPLAY('T', DisplayingDataTracker) + GO_TO_DISPLAY('D', DisplayingLandscapeTimeData) + GO_TO_DISPLAY('J', DisplayingDataGestures) + GO_TO_DISPLAY('C', DisplayingGloveCalibration) + GO_TO_DISPLAY('P', DisplayingPairing) + + PrintSystemMessage(); + + // AdvanceConsolePosition(10); + // GO_TO_DISPLAY('G', DisplayingDataGlove) + // PrintSystemMessage(); + + return ClientReturnCode::ClientReturnCode_Success; +} + +/// @brief display the ergonomics data of the gloves, and handles haptic commands. +/// @return +ClientReturnCode SDKClient::DisplayingDataGlove() +{ + ClientLog::print("[Q] Back <> [ESC] quit"); + ClientLog::print("Haptic keys: left:([1]-[5] = pinky-thumb.) right:([6]-[0] = thumb-pinky.)"); + + AdvanceConsolePosition(3); + + GO_TO_MENU_IF_REQUESTED() + + HandleHapticCommands(); + + PrintErgonomicsData(); + PrintDongleData(); + PrintSystemMessage(); + + return ClientReturnCode::ClientReturnCode_Success; +} + +ClientReturnCode SDKClient::DisplayingDataSkeleton() +{ + ClientLog::print("[Q] Back <> [ESC] quit"); + ClientLog::print(" [B] Load Left Hand Skeleton [N] Load Right Hand Skeleton [M] Unload Skeleton"); + ClientLog::print(" [D] Toggle Send to DevTools ({})", m_SendToDevTools); + ClientLog::print(" left:([1]-[5] = pinky-thumb) right:([6]-[0] = thumb-pinky)"); + + AdvanceConsolePosition(4); + + GO_TO_MENU_IF_REQUESTED() + + HandleSkeletonCommands(); + HandleSkeletonHapticCommands(); + + PrintSkeletonData(); + + AdvanceConsolePosition(1); + PrintSystemMessage(); + + return ClientReturnCode::ClientReturnCode_Success; +} + +ClientReturnCode SDKClient::DisplayingDataTracker() +{ + ClientLog::print("[Q] Back <> [ESC] quit"); + ClientLog::print("[O] Toggle Test Tracker [G] Toggle per user tracker display [T] Set Tracker Offset to Left Wrist"); + + AdvanceConsolePosition(3); + + GO_TO_MENU_IF_REQUESTED() + HandleTrackerCommands(); + + PrintRawSkeletonData(); + + PrintTrackerData(); + PrintSystemMessage(); + + return ClientReturnCode::ClientReturnCode_Success; +} + +ClientReturnCode SDKClient::DisplayingDataTemporarySkeleton() +{ + ClientLog::print("[Q] Back <> [ESC] quit"); + ClientLog::print("[A] Auto allocate chains and load skeleton"); + ClientLog::print("[B] Build Temporary Skeleton [C] Clear Temporary Skeleton [D] Clear All Temporary Skeletons For The Current Session"); + ClientLog::print("[E] Save Temporary Skeleton To File, [F] Get Temporary Skeleton From File"); + + AdvanceConsolePosition(5); + + GO_TO_MENU_IF_REQUESTED() + + HandleTemporarySkeletonCommands(); + + PrintTemporarySkeletonInfo(); + GetTemporarySkeletonIfModified(); + + AdvanceConsolePosition(3); + PrintSystemMessage(); + + return ClientReturnCode::ClientReturnCode_Success; +} + +ClientReturnCode SDKClient::DisplayingLandscapeTimeData() +{ + ClientLog::print("[Q] Back <> [ESC] quit"); + + AdvanceConsolePosition(2); + + GO_TO_MENU_IF_REQUESTED() + + PrintLandscapeTimeData(); + + PrintSystemMessage(); + + return ClientReturnCode::ClientReturnCode_Success; +} + +ClientReturnCode SDKClient::DisplayingDataGestures() +{ + ClientLog::print("[Q] Back <> [ESC] quit"); + ClientLog::print("[H] Show other Hand"); + + AdvanceConsolePosition(3); + + GO_TO_MENU_IF_REQUESTED() + + HandleGesturesCommands(); + + PrintGestureData(); + + AdvanceConsolePosition(1); + + PrintSystemMessage(); + + return ClientReturnCode::ClientReturnCode_Success; +} + +ClientReturnCode SDKClient::DisplayingGloveCalibration() +{ + ClientLog::print("[Q] Back <> [ESC] quit"); + ClientLog::print("[H] Toggle other Hand [P] Add 1 to the calibration step [M] Remove 1 from the calibration step"); + ClientLog::print("[S] Start glove calibration [C] Cancel glove calibration [F] Finish glove calibration"); + ClientLog::print("[E] Execute calibration step"); + + AdvanceConsolePosition(5); + + GO_TO_MENU_IF_REQUESTED() + + HandleGloveCalibrationCommands(); + + PrintGloveCalibrationData(); + + AdvanceConsolePosition(3); + PrintSystemMessage(); + + return ClientReturnCode::ClientReturnCode_Success; +} + +ClientReturnCode SDKClient::DisplayingPairing() +{ + ClientLog::print("[Q] Back <> [ESC] quit"); + ClientLog::print("<> [P] Pair first available Glove"); + ClientLog::print("<> [U] Unpair first available Glove"); + + + GO_TO_MENU_IF_REQUESTED() + + HandlePairingCommands(); + + AdvanceConsolePosition(4); + PrintSystemMessage(); + + return ClientReturnCode::ClientReturnCode_Success; +} + +/// @brief When the SDK loses the connection with Core the user can either +/// close the sdk or try to reconnect to a local or to a remote host. +ClientReturnCode SDKClient::DisconnectedFromCore() +{ + if (m_Host == nullptr) { return ClientReturnCode::ClientReturnCode_FailedToConnect; } + + AdvanceConsolePosition(-1); + + auto t_Duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_TimeSinceLastDisconnect).count(); + ClientLog::print("The SDK lost connection with Manus Core {} seconds ago.", t_Duration); + ClientLog::print("[P] Pick a new host. [ESC] exit"); + + AdvanceConsolePosition(3); + + bool t_ConnectLocally = m_ConnectionType == ConnectionType::ConnectionType_Local; + if (t_ConnectLocally) + { + ClientLog::print("Automatically trying to reconnect to local host."); + + ClientReturnCode t_ReconnectResult = ReconnectingToCore(); + if (t_ReconnectResult != ClientReturnCode::ClientReturnCode_FailedToConnect) + { + return t_ReconnectResult; + } + } + else + { + ClientLog::print("[R] Try to reconnect to the last host {} at {}.", m_Host->hostName, m_Host->ipAddress); + if (GetKeyDown('R')) + { + ClientLog::print("Reconnecting"); + + ClientReturnCode t_ReconnectResult = ReconnectingToCore(m_SecondsToAttemptReconnecting, m_MaxReconnectionAttempts); + if (t_ReconnectResult != ClientReturnCode::ClientReturnCode_FailedToConnect) + { + return t_ReconnectResult; + } + } + } + + AdvanceConsolePosition(10); + + + if (GetKeyDown('P')) + { + ClientLog::print("Picking new host."); + + // Restarting and initializing CoreConnection to make sure a new connection can be set up + const ClientReturnCode t_RestartResult = RestartSDK(); + if (t_RestartResult != ClientReturnCode::ClientReturnCode_Success) + { + ClientLog::error("Failed to Restart CoreConnection."); + return ClientReturnCode::ClientReturnCode_FailedToRestart; + } + + m_State = ClientState::ClientState_LookingForHosts; + } + + return ClientReturnCode::ClientReturnCode_Success; +} + +/// @brief It is called when the sdk is disconnected from Core and the user select one of the options to reconnect. +ClientReturnCode SDKClient::ReconnectingToCore(int32_t p_ReconnectionTime, int32_t p_ReconnectionAttempts) +{ + if (p_ReconnectionTime <= 0) { p_ReconnectionTime = std::numeric_limits::max(); } + if (p_ReconnectionAttempts <= 0) { p_ReconnectionAttempts = std::numeric_limits::max(); } + + // Restarting and initializing CoreConnection to make sure a new connection can be set up + const ClientReturnCode t_RestartResult = RestartSDK(); + if (t_RestartResult != ClientReturnCode::ClientReturnCode_Success) + { + ClientLog::error("Failed to Restart CoreConnection."); + return ClientReturnCode::ClientReturnCode_FailedToRestart; + } + + std::chrono::high_resolution_clock::time_point t_Start = std::chrono::high_resolution_clock::now(); + int t_Attempt = 0; + while ((p_ReconnectionAttempts > 0) && (p_ReconnectionTime > 0)) + { + ClientLog::print("Trying to reconnect to {} at {}. Attempt {}.", m_Host->hostName, m_Host->ipAddress, t_Attempt); + ClientLog::print("Attempts remaining: {}. Seconds before time out: {}.", p_ReconnectionAttempts, p_ReconnectionTime); + + bool t_ConnectLocally = m_ConnectionType == ConnectionType::ConnectionType_Local; + if (t_ConnectLocally) + { + ClientReturnCode t_ConnectionResult = LookingForHosts(); + if (t_ConnectionResult == ClientReturnCode::ClientReturnCode_Success) + { + ClientLog::print("Reconnected to ManusCore."); + return ClientReturnCode::ClientReturnCode_Success; + } + } + else + { + SDKReturnCode t_ConnectionResult = CoreSdk_ConnectToHost(*m_Host.get()); + if (t_ConnectionResult == SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::print("Reconnected to ManusCore."); + return ClientReturnCode::ClientReturnCode_Success; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(m_SleepBetweenReconnectingAttemptsInMs)); + p_ReconnectionTime -= static_cast(std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - t_Start).count()); + --p_ReconnectionAttempts; + ++t_Attempt; + } + + ClientLog::print("Failed to reconnect to ManusCore."); + m_State = ClientState::ClientState_Disconnected; + return ClientReturnCode::ClientReturnCode_FailedToConnect; +} + + +/// @brief Prints the ergonomics data of a hand. +/// @param p_ErgoData +void SDKClient::PrintHandErgoData(ErgonomicsData& p_ErgoData, bool p_Left) +{ + const std::string t_FingerNames[NUM_FINGERS_ON_HAND] = { "[thumb] ", "[index] ", "[middle]", "[ring] ", "[pinky] " }; + const std::string t_FingerJointNames[NUM_FINGERS_ON_HAND] = { "mcp", "pip", "dip" }; + const std::string t_ThumbJointNames[NUM_FINGERS_ON_HAND] = { "cmc", "mcp", "ip " }; + + int t_DataOffset = 0; + if (!p_Left)t_DataOffset = 20; + + const std::string* t_JointNames = t_ThumbJointNames; + for (unsigned int t_FingerNumber = 0; t_FingerNumber < NUM_FINGERS_ON_HAND; t_FingerNumber++) + { + ClientLog::printWithPadding("{} {} spread: {}, {} stretch: {}, {} stretch: {}, {} stretch: {} ", 6, + t_FingerNames[t_FingerNumber], // Name of the finger. + t_JointNames[0], + RoundFloatValue(p_ErgoData.data[t_DataOffset], 2), + t_JointNames[0], + RoundFloatValue(p_ErgoData.data[t_DataOffset + 1], 2), + t_JointNames[1], + RoundFloatValue(p_ErgoData.data[t_DataOffset + 2], 2), + t_JointNames[2], + RoundFloatValue(p_ErgoData.data[t_DataOffset + 3], 2)); + t_JointNames = t_FingerJointNames; + t_DataOffset += 4; + } +} + +/// @brief Print the ergonomics data received from Core. +void SDKClient::PrintErgonomicsData() +{ + // for testing purposes we only look at the first 2 gloves available + ClientLog::print(" -- Ergo Timestamp {}:{}:{}.{} ~ {}/{}/{}(D/M/Y)", + m_ErgoTimestampInfo.hour, m_ErgoTimestampInfo.minute, m_ErgoTimestampInfo.second, m_ErgoTimestampInfo.fraction, + m_ErgoTimestampInfo.day, m_ErgoTimestampInfo.month, std::to_string(m_ErgoTimestampInfo.year)); + ClientLog::print(" -- Left Glove -- 0x{} - Angles in degrees", m_FirstLeftGloveID); + if (m_LeftGloveErgoData.id == m_FirstLeftGloveID) + { + PrintHandErgoData(m_LeftGloveErgoData, true); + } + else + { + ClientLog::print(" ...No Data..."); + } + ClientLog::print(" -- Right Glove -- 0x{} - Angles in degrees", m_FirstRightGloveID); + if (m_RightGloveErgoData.id == m_FirstRightGloveID) + { + PrintHandErgoData(m_RightGloveErgoData, false); + } + else + { + ClientLog::print(" ...No Data..."); + } + + AdvanceConsolePosition(14); +} + +std::string ConvertDeviceClassTypeToString(DeviceClassType p_Type) +{ + switch (p_Type) + { + case DeviceClassType_Dongle: + return "Dongle"; + case DeviceClassType_Glove: + return "Glove"; + case DeviceClassType_Glongle: + return "Glongle (Glove Dongle)"; + default: + return "Unknown"; + } +} + +std::string ConvertDeviceFamilyTypeToString(DeviceFamilyType p_Type) +{ + switch (p_Type) + { + case DeviceFamilyType_Prime1: + return "Prime 1"; + case DeviceFamilyType_Prime2: + return "Prime 2"; + case DeviceFamilyType_PrimeX: + return "Prime X"; + case DeviceFamilyType_Metaglove: + return "Metaglove"; + case DeviceFamilyType_Prime3: + return "Prime 3"; + case DeviceFamilyType_Virtual: + return "Virtual"; + case DeviceFamilyType_MetaglovePro: + return "Metaglove Pro"; + default: + return "Unknown"; + } +} + +/// @brief Print the ergonomics data received from Core. +void SDKClient::PrintDongleData() +{ + // get a dongle id + uint32_t t_DongleCount = m_Landscape->gloveDevices.dongleCount; + if (t_DongleCount == 0) return; // we got no gloves to work on anyway! + + DongleLandscapeData t_DongleData; + + for (uint32_t i = 0; i < t_DongleCount; i++) + { + t_DongleData = m_Landscape->gloveDevices.dongles[i]; + ClientLog::print(" -- Dongle -- 0x{}", t_DongleData.id); + + ClientLog::print(" Type: {} - {}", + ConvertDeviceClassTypeToString(t_DongleData.classType), + ConvertDeviceFamilyTypeToString(t_DongleData.familyType)); + ClientLog::print(" License: {}", t_DongleData.licenseType); + + AdvanceConsolePosition(4); + } +} + +/// @brief Prints the last received system messages received from Core. +void SDKClient::PrintSystemMessage() +{ + m_SystemMessageMutex.lock(); + ClientLog::print(""); //blank line + ClientLog::print("Received System data:{} / code:{}", m_SystemMessage, (int32_t)m_SystemMessageCode); + m_SystemMessageMutex.unlock(); +} + +/// @brief Prints the finalized skeleton data received from Core. +/// Since our console cannot render this data visually in 3d (its not in the scope of this client) +/// we only display a little bit of data of the skeletons we receive. +void SDKClient::PrintSkeletonData() +{ + if (m_Skeleton == nullptr || m_Skeleton->skeletons.size() == 0) + { + return; + } + + ClientLog::print("Received Skeleton data. skeletons:{} first skeleton id:{}", m_Skeleton->skeletons.size(), (int32_t)m_Skeleton->skeletons[0].info.id); + + AdvanceConsolePosition(2); +} + +void SDKClient::PrintRawSkeletonData() +{ + if (m_RawSkeleton == nullptr || m_RawSkeleton->skeletons.size() == 0) + { + return; + } + ClientLog::print("Received Skeleton glove data from Core. skeletons:{} first skeleton glove id:{}", m_RawSkeleton->skeletons.size(), m_RawSkeleton->skeletons[0].info.gloveId); + + if (m_FirstLeftGloveID == 0 && m_FirstRightGloveID == 0) return; // no gloves connected to core + + uint32_t t_NodeCount = 0; + uint32_t t_GloveId; + if (m_FirstLeftGloveID != 0) + { + t_GloveId = m_FirstLeftGloveID; + } + else + { + t_GloveId = m_FirstRightGloveID; + } + // we need to know the number of nodes in the hierarchy, so we can initialize an array with the correct size. + SDKReturnCode t_Result = CoreSdk_GetRawSkeletonNodeCount(t_GloveId, t_NodeCount); + if (t_Result != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get Raw Skeleton Node Count. The error given was {}.", (int32_t)t_Result); + return; + } + + // now get the hierarchy data, this can be used to reconstruct the positions of each node in case the user set up the system with a local coordinate system, and to know what each node is exactly. + // having a node position defined as local means that this will be related to its parent + NodeInfo* t_NodeInfo = new NodeInfo[t_NodeCount]; + t_Result = CoreSdk_GetRawSkeletonNodeInfoArray(t_GloveId, t_NodeInfo, t_NodeCount); + if (t_Result != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get Raw Skeleton Hierarchy. The error given was {}.", (int32_t)t_Result); + return; + } + + ClientLog::print("Fetched array with Raw Skeleton Hierarchy with {} nodes.", (int32_t)t_NodeCount); + AdvanceConsolePosition(2); +} + +/// @brief Prints the tracker data +/// Since our console cannot render this data visually in 3d (its not in the scope of this client) +/// we only display a little bit of data of the trackers we receive. +void SDKClient::PrintTrackerData() +{ + ClientLog::print("Tracker test active: {}.", m_TrackerTest); //To show that test tracker is being sent to core + ClientLog::print("Per user tracker display: {}.", m_TrackerDataDisplayPerUser); + + AdvanceConsolePosition(2); + + if (m_TrackerDataDisplayPerUser) + { + PrintTrackerDataPerUser(); + AdvanceConsolePosition(10); + } + else + { + PrintTrackerDataGlobal(); + AdvanceConsolePosition(3); + } + + // now, as a test, print the tracker data received from the stream + if (m_TrackerData == nullptr || m_TrackerData->trackerData.size() == 0) + { + return; + } + + ClientLog::print("Received Tracker data. number of received trackers:{} first tracker type:{}", m_TrackerData->trackerData.size(), (int32_t)m_TrackerData->trackerData[0].trackerType); +} + +/// @brief Prints the tracker data without taking users into account. +/// This shows how one can get the tracker data that is being streamed without caring about users. +void SDKClient::PrintTrackerDataGlobal() +{ + uint32_t t_NumberOfAvailabletrackers = 0; + SDKReturnCode t_TrackerResult = CoreSdk_GetNumberOfAvailableTrackers(&t_NumberOfAvailabletrackers); + if (t_TrackerResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get tracker data. The error given was {}.", (int32_t)t_TrackerResult); + return; + } + + ClientLog::print("received available trackers :{} ", t_NumberOfAvailabletrackers); + + if (t_NumberOfAvailabletrackers == 0) return; // nothing else to do. + TrackerId* t_TrackerId = new TrackerId[t_NumberOfAvailabletrackers]; + t_TrackerResult = CoreSdk_GetIdsOfAvailableTrackers(t_TrackerId, t_NumberOfAvailabletrackers); + if (t_TrackerResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get tracker data. The error given was {}.", (int32_t)t_TrackerResult); + return; + } +} + +/// @brief Prints the tracker data per user, this shows how to access this data for each user. +void SDKClient::PrintTrackerDataPerUser() +{ + uint32_t t_NumberOfAvailableUsers = 0; + SDKReturnCode t_UserResult = CoreSdk_GetNumberOfAvailableUsers(&t_NumberOfAvailableUsers); + if (t_UserResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get user count. The error given was {}.", (int32_t)t_UserResult); + return; + } + if (t_NumberOfAvailableUsers == 0) return; // nothing to get yet + + + for (uint32_t i = 0; i < t_NumberOfAvailableUsers; i++) + { + uint32_t t_NumberOfAvailabletrackers = 0; + SDKReturnCode t_TrackerResult = CoreSdk_GetNumberOfAvailableTrackersForUserIndex(&t_NumberOfAvailabletrackers, i); + if (t_TrackerResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get tracker data. The error given was {}.", (int32_t)t_TrackerResult); + return; + } + + if (t_NumberOfAvailabletrackers == 0) continue; + + ClientLog::print("received available trackers for user index[{}] :{} ", i, t_NumberOfAvailabletrackers); + + if (t_NumberOfAvailabletrackers == 0) return; // nothing else to do. + TrackerId* t_TrackerId = new TrackerId[t_NumberOfAvailabletrackers]; + t_TrackerResult = CoreSdk_GetIdsOfAvailableTrackersForUserIndex(t_TrackerId, i, t_NumberOfAvailabletrackers); + if (t_TrackerResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get tracker data. The error given was {}.", (int32_t)t_TrackerResult); + return; + } + } +} + +std::string GetFPSEnumName(TimecodeFPS p_FPS) +{ + switch (p_FPS) + { + case TimecodeFPS::TimecodeFPS_23_976: + return "23.976 FPS (24 dropframe)"; + case TimecodeFPS::TimecodeFPS_24: + return "24 FPS"; + case TimecodeFPS::TimecodeFPS_25: + return "25 FPS"; + case TimecodeFPS::TimecodeFPS_29_97: + return "29.97 FPS (30 dropframe)"; + case TimecodeFPS::TimecodeFPS_30: + return "30 FPS"; + case TimecodeFPS::TimecodeFPS_50: + return "50 FPS"; + case TimecodeFPS::TimecodeFPS_59_94: + return "59.94 FPS (60 dropframe)"; + case TimecodeFPS::TimecodeFPS_60: + return "60 FPS"; + default: + return "Undefined FPS"; + } +} + +void SDKClient::PrintLandscapeTimeData() +{ + ClientLog::print("Total count of Interfaces: {}", m_Landscape->time.interfaceCount); + ClientLog::print("Current Interface: {} {} at index {}", m_Landscape->time.currentInterface.name, m_Landscape->time.currentInterface.api, m_Landscape->time.currentInterface.index); + + ClientLog::print("FPS: {}", GetFPSEnumName(m_Landscape->time.fps)); + ClientLog::print("Fake signal: {} | Sync Pulse: {} | Sync Status: {}", m_Landscape->time.fakeTimecode, m_Landscape->time.useSyncPulse, m_Landscape->time.syncStatus); + ClientLog::print("Device keep alive: {} | Timecode Status: {}", m_Landscape->time.deviceKeepAlive, m_Landscape->time.timecodeStatus); + + AdvanceConsolePosition(6); +} + +void SDKClient::PrintGestureData() +{ + ClientGestures* t_Gest = m_FirstLeftGloveGestures; + std::string t_Side = "Left"; + if (!m_ShowLeftGestures) + { + t_Side = "Right"; + t_Gest = m_FirstRightGloveGestures; + } + + if (t_Gest == nullptr) + { + ClientLog::print("No Gesture information for first {} glove.", t_Side); + AdvanceConsolePosition(1); + return; + } + ClientLog::print("Total count of gestures for the {} glove: {}", t_Side, t_Gest->info.totalGestureCount); + uint32_t t_Max = t_Gest->info.totalGestureCount; + if (t_Max > 20) t_Max = 20; + ClientLog::print("Showing result of first {} gestures.", t_Max); + for (uint32_t i = 0; i < t_Max; i++) + { + char* t_Name = ""; + for (uint32_t g = 0; g < m_GestureLandscapeData.size(); g++) + { + if (m_GestureLandscapeData[g].id == t_Gest->probabilities[i].id) + { + t_Name = m_GestureLandscapeData[g].name; + } + } + ClientLog::print("Gesture {} ({}) has a probability of {}%.", t_Name, t_Gest->probabilities[i].id, t_Gest->probabilities[i].percent * 100.0f); + } + + AdvanceConsolePosition(t_Max + 2); +} + +void SDKClient::PrintGloveCalibrationData() +{ + std::string t_Side = "Left"; + if (!m_CalibrateLeftHand) + { + t_Side = "Right"; + } + ClientLog::print("Calibrating: {} - Step {} ", t_Side, m_CalibrationStep); + ClientLog::print("Glove: {}", m_CalibrationGloveId); + ClientLog::print("Number of steps: {}", m_NumberOfCalibrationSteps); + ClientLog::print("Title: {}", m_StepData.title); + ClientLog::print("Description: {}", m_StepData.description); + ClientLog::print("Time: {}", m_StepData.time); + ClientLog::print("In progress: {}", m_IsCalibrationInProgress); + ClientLog::print("Message: {}", m_CalibrationMessage); + + AdvanceConsolePosition(6); +} + +/// @brief Prints the type of the first chain generated by the AllocateChain function, this is used for testing. +void SDKClient::PrintSkeletonInfo() +{ + switch (m_ChainType) + { + case ChainType::ChainType_FingerIndex: + { + ClientLog::print("received Skeleton chain type: ChainType_FingerIndex"); + break; + } + case ChainType::ChainType_FingerMiddle: + { + ClientLog::print("received Skeleton chain type: ChainType_FingerMiddle"); + break; + } + case ChainType::ChainType_FingerPinky: + { + ClientLog::print("received Skeleton chain type: ChainType_FingerPinky"); + break; + } + case ChainType::ChainType_FingerRing: + { + ClientLog::print("received Skeleton chain type: ChainType_FingerRing"); + break; + } + case ChainType::ChainType_FingerThumb: + { + ClientLog::print("received Skeleton chain type: ChainType_FingerThumb"); + break; + } + case ChainType::ChainType_Hand: + { + ClientLog::print("received Skeleton chain type: ChainType_Hand"); + break; + } + case ChainType::ChainType_Head: + { + ClientLog::print("received Skeleton chain type: ChainType_Head"); + break; + } + case ChainType::ChainType_Leg: + { + ClientLog::print("received Skeleton chain type: ChainType_Leg"); + break; + } + case ChainType::ChainType_Neck: + { + ClientLog::print("received Skeleton chain type: ChainType_Neck"); + break; + } + case ChainType::ChainType_Pelvis: + { + ClientLog::print("received Skeleton chain type: ChainType_Pelvis"); + break; + } + case ChainType::ChainType_Shoulder: + { + ClientLog::print("received Skeleton chain type: ChainType_Shoulder"); + break; + } + case ChainType::ChainType_Spine: + { + ClientLog::print("received Skeleton chain type: ChainType_Spine"); + break; + } + case ChainType::ChainType_Arm: + { + ClientLog::print("received Skeleton chain type: ChainType_Arm"); + break; + } + case ChainType_Foot: + { + ClientLog::print("received Skeleton chain type: ChainType_Foot"); + break; + } + case ChainType_Toe: + { + ClientLog::print("received Skeleton chain type: ChainType_Toe"); + break; + } + case ChainType::ChainType_Invalid: + default: + { + ClientLog::print("received Skeleton chain type: ChainType_Invalid"); + break; + } + } + AdvanceConsolePosition(2); +} + +/// @brief This support function checks if a temporary skeleton related to the current session has been modified and gets it. +/// Whenever the Dev Tools saves the changes to the skeleton, it sets the boolean t_IsSkeletonModified to true for that skeleton. +/// With callback OnSystemCallback this application can be notified when one of its temporary skeletons has been modified, so it can get it and, potentially, load it. +void SDKClient::GetTemporarySkeletonIfModified() +{ + // if a temporary skeleton associated to the current session has been modified we can get it and, potentially, load it + if (m_ModifiedSkeletonIndex != UINT_MAX) + { + // get the temporary skeleton + uint32_t t_SessionId = m_SessionId; + SDKReturnCode t_Res = CoreSdk_GetTemporarySkeleton(m_ModifiedSkeletonIndex, t_SessionId); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get temporary skeleton. The error given was {}.", (int32_t)t_Res); + return; + } + + // At this point if we are satisfied with the modifications to the skeleton we can load it into Core. + // Remember to always call function CoreSdk_ClearTemporarySkeleton after loading a temporary skeleton, + // this will keep the temporary skeleton list in sync between Core and the SDK. + + //uint32_t t_ID = 0; + //SDKReturnCode t_Res = CoreSdk_LoadSkeleton(m_ModifiedSkeletonIndex, &t_ID); + //if (t_Res != SDKReturnCode::SDKReturnCode_Success) + //{ + // ClientLog::error("Failed to load skeleton. The error given was {}.", t_Res); + // return; + //} + //if (t_ID == 0) + //{ + // ClientLog::error("Failed to give skeleton an ID."); + //} + //m_LoadedSkeletons.push_back(t_ID); + //t_Res = CoreSdk_ClearTemporarySkeleton(m_ModifiedSkeletonIndex, m_SessionId); + //if (t_Res != SDKReturnCode::SDKReturnCode_Success) + //{ + // ClientLog::error("Failed to clear temporary skeleton. The error given was {}.", t_Res); + // return; + //} + m_ModifiedSkeletonIndex = UINT_MAX; + } +} +/// @brief This support function gets the temporary skeletons for all sessions connected to Core and it prints the total number of temporary skeletons associated to the current session. +/// Temporary skeletons are used for auto allocation and in the Dev Tools. +void SDKClient::PrintTemporarySkeletonInfo() +{ + ClientLog::print("Number of temporary skeletons in the SDK: {} ", m_TemporarySkeletons.size()); + + static uint32_t t_TotalNumberOfTemporarySkeletonsInCore = 0; + auto t_TimeSinceLastTemporarySkeletonUpdate = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_LastTemporarySkeletonUpdate).count(); + if (t_TimeSinceLastTemporarySkeletonUpdate < static_cast(MILLISECONDS_BETWEEN_TEMPORARY_SKELETONS_UPDATE)) + { + ClientLog::print("Total number of temporary skeletons in core: {} ", t_TotalNumberOfTemporarySkeletonsInCore); + return; + } + TemporarySkeletonCountForAllSessions t_TemporarySkeletonCountForAllSessions; + SDKReturnCode t_Res = CoreSdk_GetTemporarySkeletonCountForAllSessions(&t_TemporarySkeletonCountForAllSessions); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get all temporary skeletons. The error given was {}.", (int32_t)t_Res); + return; + } + + t_TotalNumberOfTemporarySkeletonsInCore = 0; + for (uint32_t i = 0; i < t_TemporarySkeletonCountForAllSessions.sessionsCount; i++) + { + t_TotalNumberOfTemporarySkeletonsInCore += t_TemporarySkeletonCountForAllSessions.temporarySkeletonCountForSessions[i].skeletonCount; + } + + // print total number of temporary skeletons: + ClientLog::print("Total number of temporary skeletons in core: {} ", t_TotalNumberOfTemporarySkeletonsInCore); + m_LastTemporarySkeletonUpdate = std::chrono::high_resolution_clock::now(); +} + +void SDKClient::HandlePairingCommands() +{ + if (GetKeyDown('P')) // press P to pair + { + PairGlove(); + } + + if (GetKeyDown('U')) // press U to Unpair + { + UnpairGlove(); + } +} + +/// @brief This showcases haptics support on gloves. +/// Send haptics commands to connected gloves if specific keys were pressed. +/// We have two ways of sending the vibration commands, based on the dongle id or based on the skeleton id. +void SDKClient::HandleHapticCommands() +{ + const int t_LeftHand = 0; + const int t_RightHand = 1; + + uint32_t t_GloveIDs[2] = { 0, 0 }; + + for (size_t i = 0; i < m_Landscape->gloveDevices.gloveCount; i++) + { + GloveLandscapeData t_GloveLandscapeData = m_Landscape->gloveDevices.gloves[i]; + if (t_GloveLandscapeData.side == Side_Left && t_GloveLandscapeData.isHaptics) + { + t_GloveIDs[t_LeftHand] = t_GloveLandscapeData.id; + break; + } + } + + for (size_t i = 0; i < m_Landscape->gloveDevices.gloveCount; i++) + { + GloveLandscapeData t_GloveLandscapeData = m_Landscape->gloveDevices.gloves[i]; + if (t_GloveLandscapeData.side == Side_Right && t_GloveLandscapeData.isHaptics) + { + t_GloveIDs[t_RightHand] = t_GloveLandscapeData.id; + break; + } + } + + ClientHapticSettings t_HapticState[NUMBER_OF_HANDS_SUPPORTED]{}; + + // The strange key number sequence here results from having gloves lie in front of you, and have the keys and haptics in the same order. + t_HapticState[t_LeftHand].shouldHapticFinger[0] = GetKey('5'); // left thumb + t_HapticState[t_LeftHand].shouldHapticFinger[1] = GetKey('4'); // left index + t_HapticState[t_LeftHand].shouldHapticFinger[2] = GetKey('3'); // left middle + t_HapticState[t_LeftHand].shouldHapticFinger[3] = GetKey('2'); // left ring + t_HapticState[t_LeftHand].shouldHapticFinger[4] = GetKey('1'); // left pinky + t_HapticState[t_RightHand].shouldHapticFinger[0] = GetKey('6'); // right thumb + t_HapticState[t_RightHand].shouldHapticFinger[1] = GetKey('7'); // right index + t_HapticState[t_RightHand].shouldHapticFinger[2] = GetKey('8'); // right middle + t_HapticState[t_RightHand].shouldHapticFinger[3] = GetKey('9'); // right ring + t_HapticState[t_RightHand].shouldHapticFinger[4] = GetKey('0'); // right pinky + + const float s_FullPower = 1.0f; + + for (unsigned int t_HandNumber = 0; t_HandNumber < NUMBER_OF_HANDS_SUPPORTED; t_HandNumber++) + { + //If the Glove ID is 0, no glove was found for that side + if (t_GloveIDs[t_HandNumber] == 0) + { + continue; + } + + float t_HapticsPowers[NUM_FINGERS_ON_HAND]{}; + for (unsigned int t_FingerNumber = 0; t_FingerNumber < NUM_FINGERS_ON_HAND; t_FingerNumber++) + { + t_HapticsPowers[t_FingerNumber] = t_HapticState[t_HandNumber].shouldHapticFinger[t_FingerNumber] ? s_FullPower : 0.0f; + } + + CoreSdk_VibrateFingersForGlove(t_GloveIDs[t_HandNumber], t_HapticsPowers); + } +} + +/// @brief Handles the console commands for the skeletons. +void SDKClient::HandleSkeletonCommands() +{ + if (GetKeyDown('B')) + { + LoadTestSkeleton(Side_Left); + } + if (GetKeyDown('N')) + { + LoadTestSkeleton(Side_Right); + } + if (GetKeyDown('M')) + { + UnloadTestSkeleton(); + } + if (GetKeyDown('D')) + { + //Toggle send to DevTools + m_SendToDevTools = !m_SendToDevTools; + } +} + +/// @brief This showcases haptics support on the skeletons. +/// Send haptics commands to the gloves connected to a skeleton if specific keys were pressed. +/// We have two ways of sending the vibration commands, based on the dongle id or based on the skeleton id. +void SDKClient::HandleSkeletonHapticCommands() +{ + if (m_Skeleton == nullptr || m_Skeleton->skeletons.size() == 0) + { + return; + } + + ClientHapticSettings t_HapticState[NUMBER_OF_HANDS_SUPPORTED]{}; + const int t_LeftHand = 0; + const int t_RightHand = 1; + + // The strange key number sequence here results from having gloves lie in front of you, and have the keys and haptics in the same order. + t_HapticState[t_LeftHand].shouldHapticFinger[0] = GetKey('5'); // left thumb + t_HapticState[t_LeftHand].shouldHapticFinger[1] = GetKey('4'); // left index + t_HapticState[t_LeftHand].shouldHapticFinger[2] = GetKey('3'); // left middle + t_HapticState[t_LeftHand].shouldHapticFinger[3] = GetKey('2'); // left ring + t_HapticState[t_LeftHand].shouldHapticFinger[4] = GetKey('1'); // left pinky + t_HapticState[t_RightHand].shouldHapticFinger[0] = GetKey('6'); // right thumb + t_HapticState[t_RightHand].shouldHapticFinger[1] = GetKey('7'); // right index + t_HapticState[t_RightHand].shouldHapticFinger[2] = GetKey('8'); // right middle + t_HapticState[t_RightHand].shouldHapticFinger[3] = GetKey('9'); // right ring + t_HapticState[t_RightHand].shouldHapticFinger[4] = GetKey('0'); // right pinky + + // Note: this timer is apparently not very accurate. + // It is good enough for this test client, but should probably be replaced for other uses. + static std::chrono::high_resolution_clock::time_point s_TimeOfLastHapticsCommandSent; + const std::chrono::high_resolution_clock::time_point s_Now = std::chrono::high_resolution_clock::now(); + const long long s_MillisecondsSinceLastHapticCommand = std::chrono::duration_cast(s_Now - s_TimeOfLastHapticsCommandSent).count(); + + if (s_MillisecondsSinceLastHapticCommand < static_cast(MINIMUM_MILLISECONDS_BETWEEN_HAPTICS_COMMANDS)) + { + return; + } + + const Side s_Hands[NUMBER_OF_HANDS_SUPPORTED] = { Side::Side_Left, Side::Side_Right }; + const float s_FullPower = 1.0f; + + // The preferred way of sending the haptics commands is based on skeleton id + + for (unsigned int t_HandNumber = 0; t_HandNumber < NUMBER_OF_HANDS_SUPPORTED; t_HandNumber++) + { + float t_HapticsPowers[NUM_FINGERS_ON_HAND]{}; + for (unsigned int t_FingerNumber = 0; t_FingerNumber < NUM_FINGERS_ON_HAND; t_FingerNumber++) + { + t_HapticsPowers[t_FingerNumber] = t_HapticState[t_HandNumber].shouldHapticFinger[t_FingerNumber] ? s_FullPower : 0.0f; + } + bool t_IsHaptics = false; + if (CoreSdk_DoesSkeletonGloveSupportHaptics(m_Skeleton->skeletons[0].info.id, s_Hands[t_HandNumber], &t_IsHaptics) != SDKReturnCode::SDKReturnCode_Success) + { + continue; + } + if (!t_IsHaptics) + { + continue; + } + CoreSdk_VibrateFingersForSkeleton(m_Skeleton->skeletons[0].info.id, s_Hands[t_HandNumber], t_HapticsPowers); + } +} + +/// @brief Handles the console commands for the temporary skeletons. +void SDKClient::HandleTemporarySkeletonCommands() +{ + if (GetKeyDown('A')) + { + AllocateChains(); + } + if (GetKeyDown('B')) + { + BuildTemporarySkeleton(); + } + if (GetKeyDown('C')) + { + ClearTemporarySkeleton(); + } + if (GetKeyDown('D')) + { + ClearAllTemporarySkeletons(); + } + if (GetKeyDown('E')) + { + SaveTemporarySkeletonToFile(); + } + if (GetKeyDown('F')) + { + GetTemporarySkeletonFromFile(); + } +} + +/// @brief This support function is used to set a test tracker and add it to the landscape. +void SDKClient::HandleTrackerCommands() +{ + if (GetKeyDown('O')) + { + m_TrackerTest = !m_TrackerTest; + } + + if (GetKeyDown('G')) + { + m_TrackerDataDisplayPerUser = !m_TrackerDataDisplayPerUser; + } + + if (GetKeyDown('T')) + { + SetTrackerOffset(); + } + + if (m_TrackerTest) + { + m_TrackerOffset += 0.0005f; + if (m_TrackerOffset >= 10.0f) + { + m_TrackerOffset = 0.0f; + } + + TrackerId t_TrackerId; + CopyString(t_TrackerId.id, sizeof(t_TrackerId.id), std::string("Test Tracker")); + TrackerData t_TrackerData = {}; + t_TrackerData.isHmd = false; + t_TrackerData.trackerId = t_TrackerId; + t_TrackerData.trackerType = TrackerType::TrackerType_Unknown; + t_TrackerData.position = { 0.0f, m_TrackerOffset, 0.0f }; + t_TrackerData.rotation = { 1.0f, 0.0f, 0.0f, 0.0f }; + t_TrackerData.quality = TrackerQuality::TrackingQuality_Trackable; + TrackerData t_TrackerDatas[MAX_NUMBER_OF_TRACKERS]; + t_TrackerDatas[0] = t_TrackerData; + + const SDKReturnCode t_TrackerSend = CoreSdk_SendDataForTrackers(t_TrackerDatas, 1); + if (t_TrackerSend != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to send tracker data. The error given was {}.", (int32_t)t_TrackerSend); + return; + } + } +} + +/// @brief This support function is used to set the tracker offset. It sets the tracker offset for the left hand tracker to the wrist. +void SDKClient::SetTrackerOffset() { + + TrackerOffset t_TrackerOffset; + + //Positions the tracker 3mm right, 58mm up and 43mm forward relative to the wrist. This value is illustrative and should be adjusted to the actual tracker position. + t_TrackerOffset.translation = { 0.003f, -0.058f, -0.043f }; + t_TrackerOffset.rotation = { 1.0f, 0.0f, 0.0f, 0.0f }; + t_TrackerOffset.entryType = TrackerOffsetType::TrackerOffsetType_LeftHandTrackerToWrist; + + auto t_UserId = m_Landscape->users.users[0].id; + + SDKReturnCode t_SetTrackerReturn = CoreSdk_SetTrackerOffset(t_UserId, &t_TrackerOffset); + if (t_SetTrackerReturn != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to set tracker offset. The error given was {}.", (int32_t)t_SetTrackerReturn); + return; + } +} + +/// @brief Handles the console commands for the gestures. +void SDKClient::HandleGesturesCommands() +{ + if (GetKeyDown('H')) + { + m_ShowLeftGestures = !m_ShowLeftGestures; + } +} + +void SDKClient::ExecuteGloveCalibrationStep(GloveCalibrationStepArgs p_Args) +{ + bool t_Result; + auto t_Res = CoreSdk_GloveCalibrationStartStep(p_Args, &t_Result); + + if (t_Res == SDKReturnCode_Success && t_Result) + m_CalibrationMessage = "Step finished"; + else + m_CalibrationMessage = "Step failed"; + + m_IsCalibrationInProgress = false; +} + + +/// @brief Handles the console commands for the glove calibration. +void SDKClient::HandleGloveCalibrationCommands() +{ + // Reset data + m_NumberOfCalibrationSteps = 0; + m_CalibrationGloveId = 0; + m_ValidStepData = false; + GloveCalibrationStepData_Init(&m_StepData); + + if (GetKeyDown('H')) + { + m_CalibrateLeftHand = !m_CalibrateLeftHand; + } + + // Get glove id + if (m_Landscape == nullptr || m_Landscape->users.userCount == 0) + return; + + uint32_t t_GloveID = m_Landscape->users.users[0].leftGloveID; + if (!m_CalibrateLeftHand) + t_GloveID = m_Landscape->users.users[0].rightGloveID; + + m_CalibrationGloveId = t_GloveID; + + // Check if glove ID is valid, if not return + if (m_CalibrationGloveId == 0) + return; + + GloveCalibrationArgs t_Args; + t_Args.gloveId = m_CalibrationGloveId; + + // Get the number of calibration steps + auto t_Res = CoreSdk_GloveCalibrationGetNumberOfSteps(t_Args, &m_NumberOfCalibrationSteps); + if (t_Res != SDKReturnCode_Success || m_NumberOfCalibrationSteps == 0) + return; + + if (GetKeyDown('P') && m_CalibrationStep < 9) + { + m_CalibrationStep++; + } + + if (GetKeyDown('M') && m_CalibrationStep != 0) + { + m_CalibrationStep--; + } + + // Clamp current step to max number of steps + if (m_CalibrationStep >= m_NumberOfCalibrationSteps) + m_CalibrationStep = m_NumberOfCalibrationSteps - 1; + + // Get step data + GloveCalibrationStepArgs t_StepArgs; + t_StepArgs.gloveId = t_Args.gloveId; + t_StepArgs.stepIndex = m_CalibrationStep; + t_Res = CoreSdk_GloveCalibrationGetStepData(t_StepArgs, &m_StepData); + if (t_Res != SDKReturnCode_Success) + return; + + // Dont allow starting/stopping/etc while in progress + if (m_IsCalibrationInProgress) + return; + + if (GetKeyDown('S')) // Start + { + bool t_Result = false; + t_Res = CoreSdk_GloveCalibrationStart(t_Args, &t_Result); + + if (t_Res == SDKReturnCode::SDKReturnCode_Success && t_Result) + m_CalibrationMessage = "Glove calibration started"; + else + m_CalibrationMessage = "Failed to start glove calibration"; + } + + if (GetKeyDown('C')) // Cancel + { + bool t_Result = false; + t_Res = CoreSdk_GloveCalibrationStop(t_Args, &t_Result); + + if (t_Res == SDKReturnCode::SDKReturnCode_Success && t_Result) + m_CalibrationMessage = "Glove calibration stopped"; + else + m_CalibrationMessage = "Failed to stop glove calibration"; + } + + if (GetKeyDown('F')) // Finish + { + bool t_Result = false; + t_Res = CoreSdk_GloveCalibrationFinish(t_Args, &t_Result); + + if (t_Res == SDKReturnCode::SDKReturnCode_Success && t_Result) + m_CalibrationMessage = "Glove calibration finished"; + else + m_CalibrationMessage = "Failed to finish glove calibration"; + } + + if (GetKeyDown('E')) // Execute step + { + m_CalibrationMessage = "Step " + std::to_string(m_CalibrationStep) + " in progress"; + m_IsCalibrationInProgress = true; + std::thread t_Thread(&SDKClient::ExecuteGloveCalibrationStep, this, t_StepArgs); + t_Thread.detach(); + } +} + +/// @brief Skeletons are pretty extensive in their data setup +/// so we have several support functions so we can correctly receive and parse the data, +/// this function helps setup the data. +/// @param p_Id the id of the created node setup +/// @param p_ParentId the id of the node parent +/// @param p_PosX X position of the node, this is defined with respect to the global coordinate system or the local one depending on +/// the parameter p_UseWorldCoordinates set when initializing the sdk, +/// @param p_PosY Y position of the node this is defined with respect to the global coordinate system or the local one depending on +/// the parameter p_UseWorldCoordinates set when initializing the sdk, +/// @param p_PosZ Z position of the node this is defined with respect to the global coordinate system or the local one depending on +/// the parameter p_UseWorldCoordinates set when initializing the sdk, +/// @param p_Name the name of the node setup +/// @return the generated node setup +NodeSetup SDKClient::CreateNodeSetup(uint32_t p_Id, uint32_t p_ParentId, float p_PosX, float p_PosY, float p_PosZ, std::string p_Name) +{ + NodeSetup t_Node; + NodeSetup_Init(&t_Node); + t_Node.id = p_Id; //Every ID needs to be unique per node in a skeleton. + CopyString(t_Node.name, sizeof(t_Node.name), p_Name); + t_Node.type = NodeType::NodeType_Joint; + //Every node should have a parent unless it is the Root node. + t_Node.parentID = p_ParentId; //Setting the node ID to its own ID ensures it has no parent. + t_Node.settings.usedSettings = NodeSettingsFlag::NodeSettingsFlag_None; + + t_Node.transform.position.x = p_PosX; + t_Node.transform.position.y = p_PosY; + t_Node.transform.position.z = p_PosZ; + return t_Node; +} + +ManusVec3 SDKClient::CreateManusVec3(float p_X, float p_Y, float p_Z) +{ + ManusVec3 t_Vec; + t_Vec.x = p_X; + t_Vec.y = p_Y; + t_Vec.z = p_Z; + return t_Vec; +} + +/// @brief This support function sets up the nodes for the skeleton hand +/// In order to have any 3d positional/rotational information from the gloves or body, +/// one needs to setup the skeleton on which this data should be applied. +/// In the case of this sample we create a Hand skeleton for which we want to get the calculated result. +/// The ID's for the nodes set here are the same IDs which are used in the OnSkeletonStreamCallback, +/// this allows us to create the link between Manus Core's data and the data we enter here. +bool SDKClient::SetupHandNodes(uint32_t p_SklIndex, Side p_Side) +{ + // Define number of fingers per hand and number of joints per finger + const uint32_t t_NumFingers = 5; + const uint32_t t_NumJoints = 4; + + // Because the skeleton is build up in world space coordinates all joints have to be defined in coordate space as well + // If it is required for the skeleton to work in local space, substract all previous positions from the vectors + static ManusVec3 s_LeftHandPositions[t_NumFingers * t_NumJoints] + { + CreateManusVec3(0.025320f, -0.024950f, 0.000000f), //Thumb CMC joint + CreateManusVec3(0.025320f + 0.032742f, -0.024950f, 0.000000f), //Thumb MCP joint + CreateManusVec3(0.025320f + 0.032742f + 0.028739f, -0.024950f, 0.000000f), //Thumb IP joint + CreateManusVec3(0.025320f + 0.032742f + 0.028739f + 0.028739f, -0.024950f, 0.000000f), //Thumb Tip joint + + CreateManusVec3(0.052904f, -0.011181f, 0.000000f), //Index MCP joint + CreateManusVec3(0.052904f + 0.038257f, -0.011181f, 0.000000f), //Index PIP joint + CreateManusVec3(0.052904f + 0.038257f + 0.020884f, -0.011181f, 0.000000f), //Index DIP joint + CreateManusVec3(0.052904f + 0.038257f + 0.020884f + 0.018759f, -0.011181f, 0.000000f), //Index Tip joint + + CreateManusVec3(0.051287f, 0.000000f, 0.000000f), //Middle MCP joint + CreateManusVec3(0.051287f + 0.041861f, 0.000000f, 0.000000f), //Middle PIP joint + CreateManusVec3(0.051287f + 0.041861f + 0.024766f, 0.000000f, 0.000000f), //Middle DIP joint + CreateManusVec3(0.051287f + 0.041861f + 0.024766f + 0.019683f, 0.000000f, 0.000000f), //Middle Tip joint + + CreateManusVec3(0.049802f, 0.011274f, 0.000000f ), //Ring MCP joint + CreateManusVec3(0.049802f + 0.039736f, 0.011274f, 0.000000f), //Ring PIP joint + CreateManusVec3(0.049802f + 0.039736f + 0.023564f, 0.011274f, 0.000000f), //Ring DIP joint + CreateManusVec3(0.049802f + 0.039736f + 0.023564f + 0.019868f, 0.011274f, 0.000000f), //Ring Tip joint + + CreateManusVec3(0.047309f, 0.020145f,0.000000f), //Pinky MCP joint + CreateManusVec3(0.047309f + 0.033175f, 0.020145f, 0.000000f), //Pinky PIP joint + CreateManusVec3(0.047309f + 0.033175f + 0.018020f, 0.020145f, 0.000000f), //Pinky DIP joint + CreateManusVec3(0.047309f + 0.033175f + 0.018020f + 0.019129f, 0.020145f, 0.000000f), //Pinky Tip joint + }; + + static ManusVec3 s_RightHandPositions[t_NumFingers * t_NumJoints] + { + CreateManusVec3(0.025320f, 0.024950f, 0.000000f), //Thumb CMC joint + CreateManusVec3(0.025320f + 0.032742f, 0.024950f, 0.000000f), //Thumb MCP joint + CreateManusVec3(0.025320f + 0.032742f + 0.028739f, 0.024950f, 0.000000f), //Thumb IP joint + CreateManusVec3(0.025320f + 0.032742f + 0.028739f + 0.028739f, 0.024950f, 0.000000f), //Thumb Tip joint + + CreateManusVec3(0.052904f, 0.011181f, 0.000000f), //Index MCP joint + CreateManusVec3(0.052904f + 0.038257f, 0.011181f, 0.000000f), //Index PIP joint + CreateManusVec3(0.052904f + 0.038257f + 0.020884f, 0.011181f, 0.000000f), //Index DIP joint + CreateManusVec3(0.052904f + 0.038257f + 0.020884f + 0.018759f, 0.011181f, 0.000000f), //Index Tip joint + + CreateManusVec3(0.051287f, 0.000000f, 0.000000f), //Middle MCP joint + CreateManusVec3(0.051287f + 0.041861f, 0.000000f, 0.000000f), //Middle PIP joint + CreateManusVec3(0.051287f + 0.041861f + 0.024766f, 0.000000f, 0.000000f), //Middle DIP joint + CreateManusVec3(0.051287f + 0.041861f + 0.024766f + 0.019683f, 0.000000f, 0.000000f), //Middle Tip joint + + CreateManusVec3(0.049802f, -0.011274f, 0.000000f),//Ring MCP joint + CreateManusVec3(0.049802f + 0.039736f, -0.011274f, 0.000000f), //Ring PIP joint + CreateManusVec3(0.049802f + 0.039736f + 0.023564f, -0.011274f, 0.000000f), //Ring DIP joint + CreateManusVec3(0.049802f + 0.039736f + 0.023564f + 0.019868f, -0.011274f, 0.000000f), //Ring Tip joint + + CreateManusVec3(0.047309f, -0.020145f, 0.000000f),//Pinky MCP joint + CreateManusVec3(0.047309f + 0.033175f, -0.020145f, 0.000000f), //Pinky PIP joint + CreateManusVec3(0.047309f + 0.033175f + 0.018020f, -0.020145f, 0.000000f), //Pinky DIP joint + CreateManusVec3(0.047309f + 0.033175f + 0.018020f + 0.019129f, -0.020145f, 0.000000f), //Pinky Tip joint + }; + + // Create an array with the initial position of each hand node. + // Note, these values are just an example of node positions and refer to the hand laying on a flat surface. + ManusVec3* t_Fingers; + if (p_Side == Side::Side_Left) + { + t_Fingers = s_LeftHandPositions; + } + else + { + t_Fingers = s_RightHandPositions; + } + + // skeleton entry is already done. just the nodes now. + // setup a very simple node hierarchy for fingers + // first setup the root node + // + // root, This node has ID 0 and parent ID 0, to indicate it has no parent. + SDKReturnCode t_Res = CoreSdk_AddNodeToSkeletonSetup(p_SklIndex, CreateNodeSetup(0, 0, 0, 0, 0, "Hand")); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Add Node To Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return false; + } + + // then loop for 5 fingers + int t_FingerId = 0; + for (uint32_t i = 0; i < t_NumFingers; i++) + { + uint32_t t_ParentID = 0; + // then the digits of the finger that are linked to the root of the finger. + for (uint32_t j = 0; j < t_NumJoints; j++) + { + t_Res = CoreSdk_AddNodeToSkeletonSetup(p_SklIndex, CreateNodeSetup(1 + t_FingerId + j, t_ParentID, t_Fingers[i * 4 + j].x, t_Fingers[i * 4 + j].y, t_Fingers[i * 4 + j].z, "fingerdigit")); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + printf("Failed to Add Node To Skeleton Setup. The error given %d.", t_Res); + return false; + } + t_ParentID = 1 + t_FingerId + j; + } + t_FingerId += t_NumJoints; + } + return true; +} + +/// @brief This function sets up some basic hand chains. +/// Chains are required for a Skeleton to be able to be animated, it basically tells Manus Core +/// which nodes belong to which body part and what data needs to be applied to which node. +/// @param p_SklIndex The index of the temporary skeleton on which the chains will be added. +/// @return Returns true if everything went fine, otherwise returns false. +bool SDKClient::SetupHandChains(uint32_t p_SklIndex, Side p_Side) +{ + // Add the Hand chain, this identifies the wrist of the hand + { + ChainSettings t_ChainSettings; + ChainSettings_Init(&t_ChainSettings); + t_ChainSettings.usedSettings = ChainType::ChainType_Hand; + t_ChainSettings.hand.handMotion = HandMotion::HandMotion_IMU; + t_ChainSettings.hand.fingerChainIdsUsed = 5; //we will have 5 fingers + t_ChainSettings.hand.fingerChainIds[0] = 1; //links to the other chains we will define further down + t_ChainSettings.hand.fingerChainIds[1] = 2; + t_ChainSettings.hand.fingerChainIds[2] = 3; + t_ChainSettings.hand.fingerChainIds[3] = 4; + t_ChainSettings.hand.fingerChainIds[4] = 5; + + ChainSetup t_Chain; + ChainSetup_Init(&t_Chain); + t_Chain.id = 0; //Every ID needs to be unique per chain in a skeleton. + t_Chain.type = ChainType::ChainType_Hand; + t_Chain.dataType = ChainType::ChainType_Hand; + t_Chain.side = p_Side; + t_Chain.dataIndex = 0; + t_Chain.nodeIdCount = 1; + t_Chain.nodeIds[0] = 0; //this links to the hand node created in the SetupHandNodes + t_Chain.settings = t_ChainSettings; + + SDKReturnCode t_Res = CoreSdk_AddChainToSkeletonSetup(p_SklIndex, t_Chain); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Add Chain To Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return false; + } + } + + // Add the 5 finger chains + const ChainType t_FingerTypes[5] = { ChainType::ChainType_FingerThumb, + ChainType::ChainType_FingerIndex, + ChainType::ChainType_FingerMiddle, + ChainType::ChainType_FingerRing, + ChainType::ChainType_FingerPinky }; + for (int i = 0; i < 5; i++) + { + ChainSettings t_ChainSettings; + ChainSettings_Init(&t_ChainSettings); + t_ChainSettings.usedSettings = t_FingerTypes[i]; + t_ChainSettings.finger.handChainId = 0; //This links to the wrist chain above. + //This identifies the metacarpal bone, if none exists, or the chain is a thumb it should be set to -1. + //The metacarpal bone should not be part of the finger chain, unless you are defining a thumb which does need it. + t_ChainSettings.finger.metacarpalBoneId = -1; + t_ChainSettings.finger.useLeafAtEnd = false; //this is set to true if there is a leaf bone to the tip of the finger. + ChainSetup t_Chain; + ChainSetup_Init(&t_Chain); + t_Chain.id = i + 1; //Every ID needs to be unique per chain in a skeleton. + t_Chain.type = t_FingerTypes[i]; + t_Chain.dataType = t_FingerTypes[i]; + t_Chain.side = p_Side; + t_Chain.dataIndex = 0; + if (i == 0) // Thumb + { + t_Chain.nodeIdCount = 4; //The amount of node id's used in the array + t_Chain.nodeIds[0] = 1; //this links to the hand node created in the SetupHandNodes + t_Chain.nodeIds[1] = 2; //this links to the hand node created in the SetupHandNodes + t_Chain.nodeIds[2] = 3; //this links to the hand node created in the SetupHandNodes + t_Chain.nodeIds[3] = 4; //this links to the hand node created in the SetupHandNodes + } + else // All other fingers + { + t_Chain.nodeIdCount = 4; //The amount of node id's used in the array + t_Chain.nodeIds[0] = (i * 4) + 1; //this links to the hand node created in the SetupHandNodes + t_Chain.nodeIds[1] = (i * 4) + 2; //this links to the hand node created in the SetupHandNodes + t_Chain.nodeIds[2] = (i * 4) + 3; //this links to the hand node created in the SetupHandNodes + t_Chain.nodeIds[3] = (i * 4) + 4; //this links to the hand node created in the SetupHandNodes + } + t_Chain.settings = t_ChainSettings; + + SDKReturnCode t_Res = CoreSdk_AddChainToSkeletonSetup(p_SklIndex, t_Chain); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + return false; + } + } + return true; +} + +/// @brief This function sets up a very minimalistic hand skeleton. +/// In order to have any 3d positional/rotational information from the gloves or body, +/// one needs to setup a skeleton on which this data can be applied. +/// In the case of this sample we create a Hand skeleton in order to get skeleton information +/// in the OnSkeletonStreamCallback function. This sample does not contain any 3D rendering, so +/// we will not be applying the returned data on anything. +void SDKClient::LoadTestSkeleton(Side p_Side) +{ + uint32_t t_SklIndex = 0; + + SkeletonSetupInfo t_SKL; + SkeletonSetupInfo_Init(&t_SKL); + t_SKL.type = SkeletonType::SkeletonType_Hand; + t_SKL.settings.scaleToTarget = true; + t_SKL.settings.useEndPointApproximations = true; + t_SKL.settings.targetType = SkeletonTargetType::SkeletonTargetType_UserIndexData; + //If the user does not exist then the added skeleton will not be animated. + //Same goes for any other skeleton made for invalid users/gloves. + t_SKL.settings.skeletonTargetUserIndexData.userIndex = 0; + + CopyString(t_SKL.name, sizeof(t_SKL.name), p_Side == Side::Side_Left ? std::string("LeftHand") : std::string("RightHand")); + + SDKReturnCode t_Res = CoreSdk_CreateSkeletonSetup(t_SKL, &t_SklIndex); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Create Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return; + } + m_TemporarySkeletons.push_back(t_SklIndex); + + // setup nodes and chains for the skeleton hand + if (!SetupHandNodes(t_SklIndex, p_Side)) return; + if (!SetupHandChains(t_SklIndex, p_Side)) return; + + if (m_SendToDevTools) + { + SendLoadedSkeleton(t_SklIndex); + } + + // load skeleton + uint32_t t_ID = 0; + t_Res = CoreSdk_LoadSkeleton(t_SklIndex, &t_ID); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to load skeleton. The error given was {}.", (int32_t)t_Res); + return; + } + RemoveIndexFromTemporarySkeletonList(t_SklIndex); + + if (t_ID == 0) + { + ClientLog::error("Failed to give skeleton an ID."); + } + m_LoadedSkeletons.push_back(t_ID); +} + +/// @brief This support function is used to unload a skeleton from Core. +void SDKClient::UnloadTestSkeleton() +{ + if (m_LoadedSkeletons.size() == 0) + { + ClientLog::error("There was no skeleton for us to unload."); + return; + } + SDKReturnCode t_Res = CoreSdk_UnloadSkeleton(m_LoadedSkeletons[0]); + m_LoadedSkeletons.erase(m_LoadedSkeletons.begin()); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to unload skeleton. The error given was {}.", (int32_t)t_Res); + + return; + } +} + +/// @brief +/// Sends the loaded skeleton to the Core SDK. +/// This function saves the temporary skeleton with the given session ID and returns an error if the operation fails. +void SDKClient::SendLoadedSkeleton(uint32_t p_SklIndex) +{ + // save the temporary skeleton + SDKReturnCode t_Res = CoreSdk_SaveTemporarySkeleton(p_SklIndex, m_SessionId, true); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to save temporary skeleton. The error given was {}.", (int32_t)t_Res); + } +} + + +/// @brief This support function sets up an incomplete hand skeleton and then uses manus core to allocate chains for it. +void SDKClient::AllocateChains() +{ + m_ChainType = ChainType::ChainType_Invalid; + + uint32_t t_SklIndex = 0; + + SkeletonSettings t_Settings; + SkeletonSettings_Init(&t_Settings); + t_Settings.scaleToTarget = true; + t_Settings.targetType = SkeletonTargetType::SkeletonTargetType_UserData; + t_Settings.skeletonTargetUserData.userID = 0; + + SkeletonSetupInfo t_SKL; + SkeletonSetupInfo_Init(&t_SKL); + t_SKL.id = 0; + t_SKL.type = SkeletonType::SkeletonType_Hand; + t_SKL.settings = t_Settings; + CopyString(t_SKL.name, sizeof(t_SKL.name), std::string("hand")); + + SDKReturnCode t_Res = CoreSdk_CreateSkeletonSetup(t_SKL, &t_SklIndex); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Create Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return; + } + m_TemporarySkeletons.push_back(t_SklIndex); + + // setup nodes for the skeleton hand + SetupHandNodes(t_SklIndex, Side::Side_Left); + + // allocate chains for skeleton + t_Res = CoreSdk_AllocateChainsForSkeletonSetup(t_SklIndex); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to allocate chains for skeleton. The error given was {}.", (int32_t)t_Res); + return; + } + + // get the skeleton info + SkeletonSetupArraySizes t_SkeletonInfo; + t_Res = CoreSdk_GetSkeletonSetupArraySizes(t_SklIndex, &t_SkeletonInfo); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get info about skeleton. The error given was {}.", (int32_t)t_Res); + return; + } + + ChainSetup* t_Chains = new ChainSetup[t_SkeletonInfo.chainsCount]; + // now get the chain data + t_Res = CoreSdk_GetSkeletonSetupChainsArray(t_SklIndex, t_Chains, t_SkeletonInfo.chainsCount); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get skeleton setup chains. The error given was {}.", (int32_t)t_Res); + delete[] t_Chains; + return; + } + // as proof store the first chain type + m_ChainType = t_Chains[0].dataType; + + // but since we want to cleanly load the skeleton without holding everything up + // we need to set its side first + for (size_t i = 0; i < t_SkeletonInfo.chainsCount; i++) + { + if (t_Chains[i].dataType == ChainType::ChainType_Hand) + { + t_Chains[i].side = Side::Side_Left; // we're just picking a side here. + + t_Res = CoreSdk_OverwriteChainToSkeletonSetup(t_SklIndex, t_Chains[i]); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to overwrite Chain To Skeleton Setup. The error given was {}.", (int32_t)t_Res); + delete[] t_Chains; + return; + } + break; // no need to continue checking the others. + } + } + // cleanup + delete[] t_Chains; + + // load skeleton so it is done. + uint32_t t_ID = 0; + t_Res = CoreSdk_LoadSkeleton(t_SklIndex, &t_ID); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to load skeleton. The error given was {}.", (int32_t)t_Res); + return; + } + RemoveIndexFromTemporarySkeletonList(t_SklIndex); + + if (t_ID == 0) + { + ClientLog::error("Failed to give skeleton an ID."); + } + m_LoadedSkeletons.push_back(t_ID); +} + +/// @brief This support function is used for the manual allocation of the skeleton chains with means of a temporary skeleton. +/// Temporary Skeletons can be helpful when the user wants to modify the skeletons chains or nodes more than once before retargeting, +/// as they can be saved in core and retrieved later by the user to apply further modifications. This can be done easily with means of the Dev Tools. +/// The current function contains an example that shows how to create a temporary skeleton, modify its chains, nodes and +/// skeleton info, and save it into Core. This process should be used during development. +/// After that, the final skeleton should be loaded into core for retargeting and for getting the actual data,as displayed in the LoadTestSkeleton +/// function. +void SDKClient::BuildTemporarySkeleton() +{ + // define the session Id for which we want to save + // in this example we want to save a skeleton for the current session so we use our own Session Id + uint32_t t_SessionId = m_SessionId; + + bool t_IsSkeletonModified = false; // this bool is set to true by the Dev Tools after saving any modification to the skeleton, + // this triggers the OnSyStemCallback which is used in the SDK to be notified about a change to its temporary skeletons. + // for the purpose of this example setting this bool to true is not really necessary. + + // first create a skeleton setup of type Body + uint32_t t_SklIndex = 0; + + SkeletonSettings t_Settings; + SkeletonSettings_Init(&t_Settings); + t_Settings.scaleToTarget = true; + t_Settings.targetType = SkeletonTargetType::SkeletonTargetType_UserData; + t_Settings.skeletonTargetUserData.userID = 0; // this needs to be a real user Id when retargeting, when editing the temporary skeleton this may (hopefully) not cause issues + + SkeletonSetupInfo t_SKL; + SkeletonSetupInfo_Init(&t_SKL); + t_SKL.id = 0; + t_SKL.type = SkeletonType::SkeletonType_Body; + t_SKL.settings = t_Settings; + CopyString(t_SKL.name, sizeof(t_SKL.name), std::string("body")); + + SDKReturnCode t_Res = CoreSdk_CreateSkeletonSetup(t_SKL, &t_SklIndex); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Create Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return; + } + m_TemporarySkeletons.push_back(t_SklIndex); + //Add 3 nodes to the skeleton setup + t_Res = CoreSdk_AddNodeToSkeletonSetup(t_SklIndex, CreateNodeSetup(0, 0, 0, 0, 0, "root")); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Add Node To Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return; + } + + t_Res = CoreSdk_AddNodeToSkeletonSetup(t_SklIndex, CreateNodeSetup(1, 0, 0, 1, 0, "branch")); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Add Node To Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return; + } + + t_Res = CoreSdk_AddNodeToSkeletonSetup(t_SklIndex, CreateNodeSetup(2, 1, 0, 2, 0, "leaf")); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Add Node To Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return; + } + + //Add one chain of type Leg to the skeleton setup + ChainSettings t_ChainSettings; + ChainSettings_Init(&t_ChainSettings); + t_ChainSettings.usedSettings = ChainType::ChainType_Leg; + t_ChainSettings.leg.footForwardOffset = 0; + t_ChainSettings.leg.footSideOffset = 0; + t_ChainSettings.leg.reverseKneeDirection = false; + t_ChainSettings.leg.kneeRotationOffset = 0; + + ChainSetup t_Chain; + ChainSetup_Init(&t_Chain); + t_Chain.id = 0; + t_Chain.type = ChainType::ChainType_Leg; + t_Chain.dataType = ChainType::ChainType_Leg; + t_Chain.dataIndex = 0; + t_Chain.nodeIdCount = 3; + t_Chain.nodeIds[0] = 0; + t_Chain.nodeIds[1] = 1; + t_Chain.nodeIds[2] = 2; + t_Chain.settings = t_ChainSettings; + t_Chain.side = Side::Side_Left; + + t_Res = CoreSdk_AddChainToSkeletonSetup(t_SklIndex, t_Chain); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Add Chain To Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return; + } + + // save the temporary skeleton + t_Res = CoreSdk_SaveTemporarySkeleton(t_SklIndex, t_SessionId, t_IsSkeletonModified); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to save temporary skeleton. The error given was {}.", (int32_t)t_Res); + return; + } + + // if we want to go on with the modifications to the same temporary skeleton + // get the skeleton + t_Res = CoreSdk_GetTemporarySkeleton(t_SklIndex, t_SessionId); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get temporary skeleton. The error given was {}.", (int32_t)t_Res); + return; + } + + // now add second chain to the same temporary skeleton + t_ChainSettings.usedSettings = ChainType::ChainType_Head; + + t_Chain.id = 1; + t_Chain.type = ChainType::ChainType_Head; + t_Chain.dataType = ChainType::ChainType_Head; + t_Chain.dataIndex = 0; + t_Chain.nodeIdCount = 1; + t_Chain.nodeIds[0] = 0; + t_Chain.settings = t_ChainSettings; + t_Chain.side = Side::Side_Center; + + t_Res = CoreSdk_AddChainToSkeletonSetup(t_SklIndex, t_Chain); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Add Chain To Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return; + } + + // save the temporary skeleton + t_Res = CoreSdk_SaveTemporarySkeleton(t_SklIndex, t_SessionId, t_IsSkeletonModified); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to save temporary skeleton. The error given was {}.", (int32_t)t_Res); + return; + } + + // get the skeleton info (number of nodes and chains for that skeleton) + SkeletonSetupArraySizes t_SkeletonInfo; + t_Res = CoreSdk_GetSkeletonSetupArraySizes(t_SklIndex, &t_SkeletonInfo); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get info about skeleton. The error given was {}.", (int32_t)t_Res); + return; + } + + // now get the chain data + ChainSetup* t_Chains = new ChainSetup[t_SkeletonInfo.chainsCount]; + t_Res = CoreSdk_GetSkeletonSetupChainsArray(t_SklIndex, t_Chains, t_SkeletonInfo.chainsCount); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get skeleton setup chains. The error given was {}.", (int32_t)t_Res); + return; + } + + // get the node data + NodeSetup* t_Nodes = new NodeSetup[t_SkeletonInfo.nodesCount]; + t_Res = CoreSdk_GetSkeletonSetupNodesArray(t_SklIndex, t_Nodes, t_SkeletonInfo.nodesCount); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get skeleton setup nodes. The error given was {}.", (int32_t)t_Res); + return; + } + + // just as an example try to get the skeleton setup info + SkeletonSetupInfo t_SKeletonSetupInfo; + SkeletonSetupInfo_Init(&t_SKeletonSetupInfo); + t_Res = CoreSdk_GetSkeletonSetupInfo(t_SklIndex, &t_SKeletonSetupInfo); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to overwrite Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return; + } + + // if we want to modify the skeleton setup or if we want to apply some changes to the chains or nodes: + // first overwrite the existing skeleton setup and then re-add all the chains and nodes to it + SkeletonSettings_Init(&t_Settings); + t_Settings.targetType = SkeletonTargetType::SkeletonTargetType_GloveData; + + t_SKL.settings = t_Settings; + CopyString(t_SKL.name, sizeof(t_SKL.name), std::string("body2")); + + // this way we overwrite the temporary skeleton with index t_SklIndex with the modified skeleton setup + t_Res = CoreSdk_OverwriteSkeletonSetup(t_SklIndex, t_SKL); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to overwrite Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return; + } + + // modify chains and nodes + t_Chains[0].side = Side::Side_Right; + t_Nodes[0].type = NodeType::NodeType_Mesh; + + // add all the existing nodes to the new skeleton setup + for (size_t i = 0; i < t_SkeletonInfo.nodesCount; i++) + { + t_Res = CoreSdk_AddNodeToSkeletonSetup(t_SklIndex, t_Nodes[i]); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Add Node To Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return; + } + } + + // then add all the existing chains to the new skeleton setup + for (size_t i = 0; i < t_SkeletonInfo.chainsCount; i++) + { + t_Res = CoreSdk_AddChainToSkeletonSetup(t_SklIndex, t_Chains[i]); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Add Chains To Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return; + } + } + + // cleanup + delete[] t_Chains; + delete[] t_Nodes; + + // save temporary skeleton + // in the Dev Tools this bool is set to true when saving the temporary skeleton, this triggers OnSystemCallback which + // notifies the SDK sessions about a modifications to one of their temporary skeletons. + // setting the bool to true in this example is not really necessary, it's just for testing purposes. + t_IsSkeletonModified = true; + t_Res = CoreSdk_SaveTemporarySkeleton(t_SklIndex, t_SessionId, t_IsSkeletonModified); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to save temporary skeleton. The error given was {}.", (int32_t)t_Res); + return; + } +} + +/// @brief This support function is used to clear a temporary skeleton from the temporary skeleton list, for example it can be used when the +/// max number of temporary skeletons has been reached for a specific session. +void SDKClient::ClearTemporarySkeleton() +{ + // clear the first element of the temporary skeleton list + if (m_TemporarySkeletons.size() == 0) + { + ClientLog::error("There are no Temporary Skeletons to clear!"); + return; + } + uint32_t t_SklIndex = m_TemporarySkeletons[0]; + SDKReturnCode t_Res = CoreSdk_ClearTemporarySkeleton(t_SklIndex, m_SessionId); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Clear Temporary Skeleton. The error given was {}.", (int32_t)t_Res); + return; + } + m_TemporarySkeletons.erase(m_TemporarySkeletons.begin()); +} + +/// @brief This support function is used to clear all temporary skeletons associated to the current SDK session, it can be used when the +/// max number of temporary skeletons has been reached for the current session and we want to make room for more. +void SDKClient::ClearAllTemporarySkeletons() +{ + if (m_TemporarySkeletons.size() == 0) + { + ClientLog::error("There are no Temporary Skeletons to clear!"); + return; + } + SDKReturnCode t_Res = CoreSdk_ClearAllTemporarySkeletons(); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Clear All Temporary Skeletons. The error given was {}.", (int32_t)t_Res); + return; + } + m_TemporarySkeletons.clear(); +} + +void SDKClient::SaveTemporarySkeletonToFile() +{ + // this example shows how to save a temporary skeleton to a file + // first create a temporary skeleton: + + // define the session Id for which we want to save + uint32_t t_SessionId = m_SessionId; + + bool t_IsSkeletonModified = false; // setting this bool to true is not necessary here, it is mostly used by the Dev Tools + // to notify the SDK sessions about their skeleton being modified. + + // first create a skeleton setup + uint32_t t_SklIndex = 0; + + SkeletonSetupInfo t_SKL; + SkeletonSetupInfo_Init(&t_SKL); + t_SKL.type = SkeletonType::SkeletonType_Hand; + t_SKL.settings.scaleToTarget = true; + t_SKL.settings.targetType = SkeletonTargetType::SkeletonTargetType_GloveData; + t_SKL.settings.skeletonTargetUserIndexData.userIndex = 0; + + CopyString(t_SKL.name, sizeof(t_SKL.name), std::string("LeftHand")); + + SDKReturnCode t_Res = CoreSdk_CreateSkeletonSetup(t_SKL, &t_SklIndex); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Create Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return; + } + m_TemporarySkeletons.push_back(t_SklIndex); + + // setup nodes and chains for the skeleton hand + if (!SetupHandNodes(t_SklIndex, Side_Left)) return; + if (!SetupHandChains(t_SklIndex, Side_Left)) return; + + // save the temporary skeleton + t_Res = CoreSdk_SaveTemporarySkeleton(t_SklIndex, t_SessionId, t_IsSkeletonModified); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to save temporary skeleton. The error given was {}.", (int32_t)t_Res); + return; + } + + // now compress the temporary skeleton data and get the size of the compressed data: + uint32_t t_TemporarySkeletonLengthInBytes; + + t_Res = CoreSdk_CompressTemporarySkeletonAndGetSize(t_SklIndex, t_SessionId, &t_TemporarySkeletonLengthInBytes); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to compress temporary skeleton and get size. The error given was {}.", (int32_t)t_Res); + return; + } + unsigned char* t_TemporarySkeletonData = new unsigned char[t_TemporarySkeletonLengthInBytes]; + + // get the array of bytes with the compressed temporary skeleton data, remember to always call function CoreSdk_CompressTemporarySkeletonAndGetSize + // before trying to get the compressed temporary skeleton data + t_Res = CoreSdk_GetCompressedTemporarySkeletonData(t_TemporarySkeletonData, t_TemporarySkeletonLengthInBytes); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to get compressed temporary skeleton data. The error given was {}.", (int32_t)t_Res); + return; + } + + // now save the data into a .mskl file + // as an example we save the temporary skeleton in a folder called ManusTemporarySkeleton inside the documents directory + // get the path for the documents directory + std::string t_DirectoryPathString = GetDocumentsDirectoryPath_UTF8(); + + // create directory name and file name for storing the temporary skeleton + std::string t_DirectoryPath = + t_DirectoryPathString + + s_SlashForFilesystemPath + + "ManusTemporarySkeleton"; + + CreateFolderIfItDoesNotExist(t_DirectoryPath); + + std::string t_DirectoryPathAndFileName = + t_DirectoryPath + + s_SlashForFilesystemPath + + "TemporarySkeleton.mskl"; + + // write the temporary skeleton data to .mskl file + std::ofstream t_File = GetOutputFileStream(t_DirectoryPathAndFileName); + t_File.write((char*)t_TemporarySkeletonData, t_TemporarySkeletonLengthInBytes); + t_File.close(); + + t_Res = CoreSdk_ClearTemporarySkeleton(t_SklIndex, t_SessionId); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Clear Temporary Skeleton after saving. The error given was {}.", (int32_t)t_Res); + return; + } + RemoveIndexFromTemporarySkeletonList(t_SklIndex); +} + + +void SDKClient::GetTemporarySkeletonFromFile() +{ + // this example shows how to load a temporary skeleton data from a file + + // as an example we try to get the temporary skeleton data previously saved as .mskl file in directory Documents/ManusTemporarySkeleton + // get the path for the documents directory + std::string t_DirectoryPathString = GetDocumentsDirectoryPath_UTF8(); + + // check if directory exists + std::string t_DirectoryPath = + t_DirectoryPathString + + s_SlashForFilesystemPath + + "ManusTemporarySkeleton"; + + if (!DoesFolderOrFileExist(t_DirectoryPath)) + { + ClientLog::warn("Failed to read from client file, the mentioned directory does not exist"); + return; + } + + // create string with file name + std::string t_DirectoryPathAndFileName = + t_DirectoryPath + + s_SlashForFilesystemPath + + "TemporarySkeleton.mskl"; + + // read from file + std::ifstream t_File = GetInputFileStream(t_DirectoryPathAndFileName); + + if (!t_File) + { + ClientLog::warn("Failed to read from client file, the file does not exist in the mentioned directory"); + return; + } + + // get file dimension + t_File.seekg(0, t_File.end); + int t_FileLength = (int)t_File.tellg(); + t_File.seekg(0, t_File.beg); + + // get temporary skeleton data from file + unsigned char* t_TemporarySkeletonData = new unsigned char[t_FileLength]; + t_File.read((char*)t_TemporarySkeletonData, t_FileLength); + t_File.close(); + + + // save the zipped temporary skeleton information, they will be used internally for sending the data to Core + uint32_t t_TemporarySkeletonLengthInBytes = t_FileLength; + + if (t_TemporarySkeletonData == nullptr) + { + ClientLog::warn("Failed to read the compressed temporary skeleton data from file"); + delete[] t_TemporarySkeletonData; + return; + } + + // create a skeleton setup where we will store the temporary skeleton retrieved from file + SkeletonSetupInfo t_SKL; + SkeletonSetupInfo_Init(&t_SKL); + uint32_t t_SklIndex = 0; + SDKReturnCode t_Res = CoreSdk_CreateSkeletonSetup(t_SKL, &t_SklIndex); + if (t_Res != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to Create Skeleton Setup. The error given was {}.", (int32_t)t_Res); + return; + } + m_TemporarySkeletons.push_back(t_SklIndex); + + // associate the retrieved temporary skeleton to the current session id + uint32_t t_SessionId = m_SessionId; + + // load the temporary skeleton data retrieved from the zipped file and save it with index t_SklIndex and session id of the current session + SDKReturnCode t_Result = CoreSdk_GetTemporarySkeletonFromCompressedData(t_SklIndex, t_SessionId, t_TemporarySkeletonData, t_TemporarySkeletonLengthInBytes); + if (t_Result != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::warn("Failed to load temporary skeleton data from client file in Core, the error code was: {}.", (int32_t)t_Result); + return; + } + + delete[] t_TemporarySkeletonData; +} + +void SDKClient::TestTimestamp() +{ + ManusTimestamp t_TS; + ManusTimestamp_Init(&t_TS); + ManusTimestampInfo t_TSInfo; + ManusTimestampInfo_Init(&t_TSInfo); + t_TSInfo.fraction = 30; + t_TSInfo.second = 15; + t_TSInfo.minute = 30; + t_TSInfo.hour = 12; + t_TSInfo.day = 1; + t_TSInfo.month = 8; + t_TSInfo.year = 2012; + t_TSInfo.timecode = true; + + CoreSdk_SetTimestampInfo(&t_TS, t_TSInfo); + + ManusTimestampInfo t_TSInfo2; + CoreSdk_GetTimestampInfo(t_TS, &t_TSInfo2); +} + +void SDKClient::RemoveIndexFromTemporarySkeletonList(uint32_t p_Idx) +{ + for (int i = 0; i < m_TemporarySkeletons.size(); i++) + { + if (m_TemporarySkeletons[i] == p_Idx) + { + m_TemporarySkeletons.erase(m_TemporarySkeletons.begin() + i); + } + } +} + +void SDKClient::PairGlove() +{ + bool t_GloveFound = false; + uint32_t t_GloveToPairId = -1; + + for (size_t i = 0; i < m_Landscape->gloveDevices.gloveCount; i++) + { + if (m_Landscape->gloveDevices.gloves[i].pairedState == DevicePairedState_Unpaired) + { + t_GloveToPairId = m_Landscape->gloveDevices.gloves[i].id; + t_GloveFound = true; + break; + } + } + + if (!t_GloveFound) + { + ClientLog::warn("No glove available for pairing found."); + return; + } + + bool t_Paired; + SDKReturnCode t_PairGloveResult = CoreSdk_PairGlove(t_GloveToPairId, &t_Paired); + if (t_PairGloveResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to pair glove with ID: {}. The error given was {}", t_GloveToPairId, (int32_t)t_PairGloveResult); + return; + } + + if (!t_Paired) + { + ClientLog::error("Failed to pair glove with ID: {}", t_GloveToPairId); + return; + } + + ClientLog::print("Succesfully paired glove with ID: {}", t_GloveToPairId); + + return; +} + +void SDKClient::UnpairGlove() +{ + bool t_GloveFound = false; + uint32_t t_GloveToUnPairId = -1; + + for (size_t i = 0; i < m_Landscape->gloveDevices.gloveCount; i++) + { + if (m_Landscape->gloveDevices.gloves[i].id == m_Landscape->gloveDevices.gloves[i].dongleID) + continue; + + if (m_Landscape->gloveDevices.gloves[i].pairedState == DevicePairedState_Paired) + { + t_GloveToUnPairId = m_Landscape->gloveDevices.gloves[i].id; + t_GloveFound = true; + break; + } + } + + if (!t_GloveFound) + { + ClientLog::error("No glove to unpair found"); + return; + } + + bool t_Unpaired; + SDKReturnCode t_UnpairGloveResult = CoreSdk_UnpairGlove(t_GloveToUnPairId, &t_Unpaired); + if (t_UnpairGloveResult != SDKReturnCode::SDKReturnCode_Success) + { + ClientLog::error("Failed to unpair glove with ID: {}. The error given was ", t_GloveToUnPairId, (int32_t)t_UnpairGloveResult); + return; + } + + ClientLog::print("Succesfully unpaired glove with ID: {}", t_GloveToUnPairId); + return; +} +/// @brief Prints the type of the first chain generated by the AllocateChain function, this is used for testing. +void SDKClient::PrintLogs() +{ + std::vector t_SDKLogs; + s_Instance->m_LogMutex.lock(); + t_SDKLogs.swap(s_Instance->m_Logs); + s_Instance->m_LogMutex.unlock(); + + for (size_t i = 0; i < t_SDKLogs.size(); i++) + { + ClientLog::print(t_SDKLogs[i]->string.c_str()); + delete t_SDKLogs[i]; + } +} diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient.hpp b/gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient.hpp new file mode 100644 index 0000000..edbdc1b --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient.hpp @@ -0,0 +1,349 @@ +#ifndef _SDK_CLIENT_HPP_ +#define _SDK_CLIENT_HPP_ + +// Set up a Doxygen group. +/** @addtogroup SDKClient + * @{ + */ + +#include // Used for haptic commands. +#include // Used for rounding float values. +#include // Used for printing glove data, and converting glove IDs to strings. +#include // Used for smart pointers. +#include +#include +#include +#include +#include + +#include "ClientPlatformSpecific.hpp" +#include "ManusSDK.h" + +extern std::map> output_map; + + +/// @brief Constant expression: number of hands supported by demo. +constexpr unsigned int NUMBER_OF_HANDS_SUPPORTED = 2; + +/// @brief Constant expression used to define the time between two possible haptics commands sent +constexpr unsigned long long int MINIMUM_MILLISECONDS_BETWEEN_HAPTICS_COMMANDS = 20; + +/// @brief Constant expression used to define the time between two updates of the temporary skeleton count printing +constexpr unsigned long long int MILLISECONDS_BETWEEN_TEMPORARY_SKELETONS_UPDATE = 1000; + +/// @brief The type of connection to core. +enum class ConnectionType : int +{ + ConnectionType_Invalid = 0, + ConnectionType_Integrated, + ConnectionType_Local, + ConnectionType_Remote, + ClientState_MAX_CLIENT_STATE_SIZE +}; + +/// @brief The current state of the client. +enum class ClientState : int +{ + ClientState_Starting = 0, + ClientState_LookingForHosts, + ClientState_NoHostsFound, + ClientState_PickingHost, + ClientState_ConnectingToCore, + ClientState_DisplayingData, + ClientState_Disconnected, + + ClientState_MAX_CLIENT_STATE_SIZE +}; + +/// @brief Values that can be returned by this application. +enum class ClientReturnCode : int +{ + ClientReturnCode_Success = 0, + ClientReturnCode_FailedPlatformSpecificInitialization, + ClientReturnCode_FailedToResizeWindow, + ClientReturnCode_FailedToInitialize, + ClientReturnCode_FailedToFindHosts, + ClientReturnCode_FailedToConnect, + ClientReturnCode_UnrecognizedStateEncountered, + ClientReturnCode_FailedToShutDownSDK, + ClientReturnCode_FailedPlatformSpecificShutdown, + ClientReturnCode_FailedToRestart, + ClientReturnCode_FailedWrongTimeToGetData, + + ClientReturnCode_MAX_CLIENT_RETURN_CODE_SIZE +}; + +struct SDKLog +{ + LogSeverity severity; + std::string string; +}; + +/// @brief Haptic settings for a single glove. +/// +/// This is used to store if a specific motor on the glove should be enabled or not. +struct ClientHapticSettings +{ + bool shouldHapticFinger[NUM_FINGERS_ON_HAND]; +}; + +/// @brief Used to store the information about the final animated skeletons. +class ClientSkeleton +{ +public: + SkeletonInfo info; + std::vector nodes; +}; + +/// @brief Used to store all the final animated skeletons received from Core. +class ClientSkeletonCollection +{ +public: + std::vector skeletons; +}; + +/// @brief Used to store the information about the skeleton data coming from the estimation system in Core. +class ClientRawSkeleton +{ +public: + RawSkeletonInfo info; + std::vector nodes; +}; + +/// @brief Used to store all the skeleton data coming from the estimation system in Core. +class ClientRawSkeletonCollection +{ +public: + std::vector skeletons; +}; + +/// @brief Used to store all the tracker data coming from Core. +class TrackerDataCollection +{ +public: + std::vector trackerData; +}; + +/// @brief Used to store the information about the final animated skeletons. +class ClientGestures +{ +public: + GestureProbabilities info; + std::vector probabilities; +}; + +class SDKClient : public SDKClientPlatformSpecific +{ +public: + SDKClient(); + ~SDKClient(); + + ClientReturnCode Initialize(); + ClientReturnCode Run(); + ClientReturnCode ShutDown(); + + //Callbacks + static void OnConnectedCallback(const ManusHost* const p_Host); + static void OnDisconnectedCallback(const ManusHost* const p_Host); + static void OnLogCallback(LogSeverity p_Severity, const char* const p_Log, uint32_t p_Length); + static void OnSkeletonStreamCallback(const SkeletonStreamInfo* const p_Skeleton); + static void OnLandscapeCallback(const Landscape* const p_Landscape); + static void OnSystemCallback(const SystemMessage* const p_SystemMessage); + static void OnErgonomicsCallback(const ErgonomicsStream* const p_Ergo); + static void OnRawSkeletonStreamCallback(const SkeletonStreamInfo* const p_RawSkeletonStreamInfo); + static void OnTrackerStreamCallback(const TrackerStreamInfo* const p_TrackerStreamInfo); + static void OnGestureStreamCallback(const GestureStreamInfo* const p_GestureStream); + + float RoundFloatValue(float p_Value, int p_NumDecimalsToKeep); + void AdvanceConsolePosition(short int p_Y); + +protected: + virtual ClientReturnCode InitializeSDK(); + virtual ClientReturnCode RestartSDK(); + virtual ClientReturnCode RegisterAllCallbacks(); + + virtual ClientReturnCode LookingForHosts(); + virtual ClientReturnCode NoHostsFound(); + virtual ClientReturnCode PickingHost(); + virtual ClientReturnCode ConnectingToCore(); + + virtual ClientReturnCode UpdateBeforeDisplayingData(); + + virtual ClientReturnCode DisplayingData(); + virtual ClientReturnCode DisplayingDataGlove(); + virtual ClientReturnCode DisplayingDataSkeleton(); + virtual ClientReturnCode DisplayingDataTracker(); + virtual ClientReturnCode DisplayingDataTemporarySkeleton(); + virtual ClientReturnCode DisplayingLandscapeTimeData(); + virtual ClientReturnCode DisplayingDataGestures(); + virtual ClientReturnCode DisplayingGloveCalibration(); + virtual ClientReturnCode DisplayingPairing(); + + virtual ClientReturnCode DisconnectedFromCore(); + virtual ClientReturnCode ReconnectingToCore(int32_t p_ReconnectionTime = 0, int32_t p_ReconnectionAttempts = 0); + + void PrintHandErgoData(ErgonomicsData& p_ErgoData, bool p_Left); + void PrintErgonomicsData(); + void PrintDongleData(); + void PrintSystemMessage(); + void PrintSkeletonData(); + void PrintRawSkeletonData(); + + + void PrintTrackerData(); + void PrintTrackerDataGlobal(); + void PrintTrackerDataPerUser(); + + void PrintLandscapeTimeData(); + + void PrintGestureData(); + void PrintGloveCalibrationData(); + + void PrintSkeletonInfo(); + void PrintTemporarySkeletonInfo(); + void HandlePairingCommands(); + void GetTemporarySkeletonIfModified(); + + void HandleHapticCommands(); + + void HandleSkeletonCommands(); + void HandleSkeletonHapticCommands(); + void HandleTemporarySkeletonCommands(); + void HandleTrackerCommands(); + void SetTrackerOffset(); + void HandleGesturesCommands(); + void HandleGloveCalibrationCommands(); + + static NodeSetup CreateNodeSetup(uint32_t p_Id, uint32_t p_ParentId, float p_PosX, float p_PosY, float p_PosZ, std::string p_Name); + bool SetupHandNodes(uint32_t p_SklIndex, Side p_Side); + bool SetupHandChains(uint32_t p_SklIndex, Side p_Side); + + void LoadTestSkeleton(Side p_Side); + void UnloadTestSkeleton(); + void SendLoadedSkeleton(uint32_t p_SklIndex); + + void AllocateChains(); + + void BuildTemporarySkeleton(); + void ClearTemporarySkeleton(); + void ClearAllTemporarySkeletons(); + void SaveTemporarySkeletonToFile(); + void GetTemporarySkeletonFromFile(); + + void TestTimestamp(); + + void RemoveIndexFromTemporarySkeletonList(uint32_t p_Idx); + void PairGlove(); + void UnpairGlove(); + static ManusVec3 CreateManusVec3(float p_X, float p_Y, float p_Z); + + void ExecuteGloveCalibrationStep(GloveCalibrationStepArgs p_Args); + + void PrintLogs(); + +protected: + static SDKClient* s_Instance; + + bool m_RequestedExit = false; + ConnectionType m_ConnectionType = ConnectionType::ConnectionType_Invalid; + + //Console + uint32_t m_ConsoleClearTickCount = 0; + const short int m_ConsoleWidth = 220; + const short int m_ConsoleHeight = 55; + const short int m_ConsoleScrollback = 500; + int m_ConsoleCurrentOffset = 0; + + SessionType m_ClientType = SessionType::SessionType_CoreSDK; + + ClientState m_State = ClientState::ClientState_Starting; + ClientState m_PreviousState = m_State; + + std::function m_CurrentInteraction = nullptr; + + uint32_t m_HostToConnectTo = 0; + uint32_t m_NumberOfHostsFound = 0; + uint32_t m_SecondsToFindHosts = 2; + + int32_t m_SecondsToAttemptReconnecting = 60; + int32_t m_MaxReconnectionAttempts = 10; + uint32_t m_SleepBetweenReconnectingAttemptsInMs = 100; + + std::unique_ptr m_AvailableHosts = nullptr; + std::unique_ptr m_Host; + + //Data + uint32_t m_SessionId = 0; + + bool m_RumblingWrist[NUMBER_OF_HANDS_SUPPORTED] = { false, false }; + + std::vector m_LoadedSkeletons; + std::vector m_TemporarySkeletons; + + std::mutex m_SkeletonMutex; + std::mutex m_RawSkeletonMutex; + + ClientSkeletonCollection* m_NextSkeleton = nullptr; + ClientSkeletonCollection* m_Skeleton = nullptr; + + ClientRawSkeletonCollection* m_NextRawSkeleton = nullptr; + ClientRawSkeletonCollection* m_RawSkeleton = nullptr; + + std::mutex m_TrackerMutex; + + TrackerDataCollection* m_NextTrackerData = nullptr; + TrackerDataCollection* m_TrackerData = nullptr; + + std::chrono::time_point m_TimeSinceLastDisconnect; + std::chrono::time_point m_LastTemporarySkeletonUpdate = std::chrono::high_resolution_clock::now(); + + std::mutex m_SystemMessageMutex; + std::string m_SystemMessage = ""; + SystemMessageType m_SystemMessageCode = SystemMessageType::SystemMessageType_Unknown; + uint32_t m_ModifiedSkeletonIndex = UINT_MAX; + + ManusTimestampInfo m_ErgoTimestampInfo; + ErgonomicsData m_LeftGloveErgoData; + ErgonomicsData m_RightGloveErgoData; + + ChainType m_ChainType = ChainType::ChainType_Invalid; + + bool m_TrackerTest = false; + bool m_TrackerDataDisplayPerUser = false; + float m_TrackerOffset = 0.0f; + + std::mutex m_LandscapeMutex; + Landscape* m_NewLandscape = nullptr; + Landscape* m_Landscape = nullptr; + std::vector m_NewGestureLandscapeData; + std::vector m_GestureLandscapeData; + + uint32_t m_FirstLeftGloveID = 0; + uint32_t m_FirstRightGloveID = 0; + + std::mutex m_GestureMutex; + ClientGestures* m_NewFirstLeftGloveGestures = nullptr; + ClientGestures* m_FirstLeftGloveGestures = nullptr; + + ClientGestures* m_NewFirstRightGloveGestures = nullptr; + ClientGestures* m_FirstRightGloveGestures = nullptr; + + bool m_ShowLeftGestures = true; + + bool m_CalibrateLeftHand = true; + uint32_t m_CalibrationStep = 0; + uint32_t m_CalibrationGloveId = 0; + uint32_t m_NumberOfCalibrationSteps = 0; + bool m_ValidStepData = false; + GloveCalibrationStepData m_StepData; + bool m_IsCalibrationInProgress = false; + std::string m_CalibrationMessage = ""; + bool m_SendToDevTools = false; + + std::mutex m_LogMutex; + std::vector m_Logs; +}; + +// Close the Doxygen group. +/** @} */ +#endif diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient_Linux.code-workspace b/gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient_Linux.code-workspace new file mode 100644 index 0000000..ef9f5d2 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient_Linux.code-workspace @@ -0,0 +1,7 @@ +{ + "folders": [ + { + "path": "." + } + ] +} \ No newline at end of file diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient_Linux.vcxproj b/gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient_Linux.vcxproj new file mode 100644 index 0000000..205c4d6 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient_Linux.vcxproj @@ -0,0 +1,249 @@ + + + + + Debug + ARM + + + Release + ARM + + + Debug + ARM64 + + + Release + ARM64 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + {DADD0545-4F93-41DE-B0B7-C28B8497D16B} + Linux + SDKClient_Linux + 15.0 + Linux + 1.0 + Generic + {D51BCBC9-82E9-4017-911E-C93873C4EA2B} + + + + true + Remote_Clang_1_0 + + + false + Remote_Clang_1_0 + + + true + Remote_GCC_1_0 + + + false + Remote_GCC_1_0 + + + false + Remote_Clang_1_0 + + + true + Remote_Clang_1_0 + + + + + + + + $(RemoteRootDir)/$(RemoteOutRelDir)/$(TargetName)$(TargetExt) + + + $(SolutionDir)Output\$(Platform)\$(Configuration)\ + $(SolutionDir)Temp\$(ProjectGuid)\$(Platform)\$(Configuration)\ + 8 + + + $(SolutionDir)Output\$(Platform)\$(Configuration)\ + $(SolutionDir)Temp\$(ProjectGuid)\$(Platform)\$(Configuration)\ + 8 + + + $(SolutionDir)Output\$(Platform)\$(Configuration)\ + $(SolutionDir)Temp\$(ProjectGuid)\$(Platform)\$(Configuration)\ + 8 + + + $(SolutionDir)Output\$(Platform)\$(Configuration)\ + $(SolutionDir)Temp\$(ProjectGuid)\$(Platform)\$(Configuration)\ + 8 + + + $(SolutionDir)Output\$(Platform)\$(Configuration)\ + $(SolutionDir)Temp\$(ProjectGuid)\$(Platform)\$(Configuration)\ + 8 + + + $(SolutionDir)Output\$(Platform)\$(Configuration)\ + $(SolutionDir)Temp\$(ProjectGuid)\$(Platform)\$(Configuration)\ + 8 + + + + ~/ManusSDK/include;%(AdditionalIncludeDirectories) + c++17 + + + ~/ManusSDK/lib/libManusSDK.so;%(AdditionalDependencies) + ncurses + -pthread + + + rem Copy SDK client base files + copy /Y "$(SolutionDir)SDKClient_Windows\Main.cpp" "$(ProjectDir)\Main.cpp" + copy /Y "$(SolutionDir)SDKClient_Windows\SDKClient.hpp" "$(ProjectDir)\SDKClient.hpp" + copy /Y "$(SolutionDir)SDKClient_Windows\SDKClient.cpp" "$(ProjectDir)\SDKClient.cpp" + + rem Copy platform specific files + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\ClientPlatformSpecific.hpp" "$(ProjectDir)\ClientPlatformSpecific.hpp" + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\Linux\ClientPlatformSpecific.cpp" "$(ProjectDir)\ClientPlatformSpecific.cpp" + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\Linux\ClientPlatformSpecificTypes.hpp" "$(ProjectDir)\ClientPlatformSpecificTypes.hpp" + + + + + ~/ManusSDK/include;%(AdditionalIncludeDirectories) + c++17 + + + ~/ManusSDK/lib/libManusSDK.so;%(AdditionalDependencies) + ncurses + -pthread + + + rem Copy SDK client base files + copy /Y "$(SolutionDir)SDKClient_Windows\Main.cpp" "$(ProjectDir)\Main.cpp" + copy /Y "$(SolutionDir)SDKClient_Windows\SDKClient.hpp" "$(ProjectDir)\SDKClient.hpp" + copy /Y "$(SolutionDir)SDKClient_Windows\SDKClient.cpp" "$(ProjectDir)\SDKClient.cpp" + + rem Copy platform specific files + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\ClientPlatformSpecific.hpp" "$(ProjectDir)\ClientPlatformSpecific.hpp" + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\Linux\ClientPlatformSpecific.cpp" "$(ProjectDir)\ClientPlatformSpecific.cpp" + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\Linux\ClientPlatformSpecificTypes.hpp" "$(ProjectDir)\ClientPlatformSpecificTypes.hpp" + + + + + ~/ManusSDK/include;%(AdditionalIncludeDirectories) + c++17 + + + ~/ManusSDK/lib/libManusSDK.so;%(AdditionalDependencies) + ncurses + -pthread + + + rem Copy SDK client base files + copy /Y "$(SolutionDir)SDKClient_Windows\Main.cpp" "$(ProjectDir)\Main.cpp" + copy /Y "$(SolutionDir)SDKClient_Windows\SDKClient.hpp" "$(ProjectDir)\SDKClient.hpp" + copy /Y "$(SolutionDir)SDKClient_Windows\SDKClient.cpp" "$(ProjectDir)\SDKClient.cpp" + + rem Copy platform specific files + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\ClientPlatformSpecific.hpp" "$(ProjectDir)\ClientPlatformSpecific.hpp" + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\Linux\ClientPlatformSpecific.cpp" "$(ProjectDir)\ClientPlatformSpecific.cpp" + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\Linux\ClientPlatformSpecificTypes.hpp" "$(ProjectDir)\ClientPlatformSpecificTypes.hpp" + + + + + ~/ManusSDK/include;%(AdditionalIncludeDirectories) + c++17 + + + ~/ManusSDK/lib/libManusSDK.so;%(AdditionalDependencies) + ncurses + -pthread + + + rem Copy SDK client base files + copy /Y "$(SolutionDir)SDKClient_Windows\Main.cpp" "$(ProjectDir)\Main.cpp" + copy /Y "$(SolutionDir)SDKClient_Windows\SDKClient.hpp" "$(ProjectDir)\SDKClient.hpp" + copy /Y "$(SolutionDir)SDKClient_Windows\SDKClient.cpp" "$(ProjectDir)\SDKClient.cpp" + + rem Copy platform specific files + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\ClientPlatformSpecific.hpp" "$(ProjectDir)\ClientPlatformSpecific.hpp" + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\Linux\ClientPlatformSpecific.cpp" "$(ProjectDir)\ClientPlatformSpecific.cpp" + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\Linux\ClientPlatformSpecificTypes.hpp" "$(ProjectDir)\ClientPlatformSpecificTypes.hpp" + + + + + ~/ManusSDK/include;%(AdditionalIncludeDirectories) + c++17 + FMT_HEADER_ONLY;DEBUG;_DEBUG;%(PreprocessorDefinitions) + + + ~/ManusSDK/lib/libManusSDK.so;%(AdditionalDependencies) + ncurses + -pthread + + + rem Copy SDK client base files + copy /Y "$(SolutionDir)SDKClient_Windows\Main.cpp" "$(ProjectDir)\Main.cpp" + copy /Y "$(SolutionDir)SDKClient_Windows\SDKClient.hpp" "$(ProjectDir)\SDKClient.hpp" + copy /Y "$(SolutionDir)SDKClient_Windows\SDKClient.cpp" "$(ProjectDir)\SDKClient.cpp" + + rem Copy platform specific files + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\ClientPlatformSpecific.hpp" "$(ProjectDir)\ClientPlatformSpecific.hpp" + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\Linux\ClientPlatformSpecific.cpp" "$(ProjectDir)\ClientPlatformSpecific.cpp" + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\Linux\ClientPlatformSpecificTypes.hpp" "$(ProjectDir)\ClientPlatformSpecificTypes.hpp" + + + + + ~/ManusSDK/include;%(AdditionalIncludeDirectories) + c++17 + FMT_HEADER_ONLY;NDEBUG;%(PreprocessorDefinitions) + + + ~/ManusSDK/lib/libManusSDK.so;%(AdditionalDependencies) + ncurses + -pthread + + + rem Copy SDK client base files + copy /Y "$(SolutionDir)SDKClient_Windows\Main.cpp" "$(ProjectDir)\Main.cpp" + copy /Y "$(SolutionDir)SDKClient_Windows\SDKClient.hpp" "$(ProjectDir)\SDKClient.hpp" + copy /Y "$(SolutionDir)SDKClient_Windows\SDKClient.cpp" "$(ProjectDir)\SDKClient.cpp" + + rem Copy platform specific files + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\ClientPlatformSpecific.hpp" "$(ProjectDir)\ClientPlatformSpecific.hpp" + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\Linux\ClientPlatformSpecific.cpp" "$(ProjectDir)\ClientPlatformSpecific.cpp" + copy /Y "$(SolutionDir)SDKClient_Windows\PlatformSpecific\Linux\ClientPlatformSpecificTypes.hpp" "$(ProjectDir)\ClientPlatformSpecificTypes.hpp" + + + + + \ No newline at end of file diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient_Linux.vcxproj.filters b/gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient_Linux.vcxproj.filters new file mode 100644 index 0000000..85be791 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/SDKClient_Linux.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + + + + + + Platform Specific + + + + + + + Platform Specific + + + Platform Specific + + + + \ No newline at end of file diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/ClientPlatformSpecific.d b/gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/ClientPlatformSpecific.d new file mode 100644 index 0000000..95cf4b3 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/ClientPlatformSpecific.d @@ -0,0 +1,4 @@ +objects/ClientPlatformSpecific.o: ClientPlatformSpecific.cpp \ + ClientPlatformSpecific.hpp ClientPlatformSpecificTypes.hpp \ + ManusSDK/include/ManusSDK.h ManusSDK/include/ManusSDKTypes.h \ + ManusSDK/include/ManusSDKTypeInitializers.h ClientLogging.hpp diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/ClientPlatformSpecific.o b/gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/ClientPlatformSpecific.o new file mode 100644 index 0000000000000000000000000000000000000000..6f7208eb7617d8be7ce974a7162d9d98763f9214 GIT binary patch literal 562984 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

D9A=-5b1B*kS_Oaq!{ReW`h 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�vT=gQxf|9Z>* zn{-u( z|BbM0jTWNC%HLN1rBwge|K+JfW&8Qdp})G1QK?A(H-~=4G;{o-e!fLN(9&Li>3xm9 zY`-c**egZ(+sa>edink%Xwm=Pp+CEl`Uf2Ptu6Y~Ec)pe+sj`}`f~gv+s{J||ILp4 zrT;F6|4#C!+n}L3m;Uc6e_Q>>QTg%wcfFj5E9dSpJ(V`j@B)cAcW^Kib9{ec6A>@=tg8$5ql_ zKWAgpzl!|hOd6rTQvZGBZ>#@ODnFm6bp6k^l)q7HyZ$|-f4@!+UQ+)&-7?KgG?7u zwx1%>r`yovRbIcj^0(FBe9~tfU4IKK`ePmXTPvxb?9eZ^=aiNl% zH?sa0JM_0$^cPw5KXT}|rX62V`>CNW&{f!S`>z-I_o*QNs>mB+HZz$h>@3!b4a_B$rs6Sm-_!_NFP`3KpSxNtw9sb{ve?{lt zD(V2U>HkLl6^&p1r}S<6Zy?nl9)I^*>M!WfKQF=5Qbqc?%HO7+V$ok=(f`n)--+}q zs=v!Gv)5mL@~6i@Lc3hoHNHA4e_Q@DEcxqmNp*7OdcDh`A0+)e6jSe|{z8X-5$UIp zzwSR*Tl8D00_vEu|K9JYKdC><;r|QyQ!}J2%J=`%mA|e2$|!$4|LD(Z%KH1lq2Fte zkyM9w*h}XBFNc2AEYpARe$w@~#-iV|gT4MTNIy1Az5M>`s{C!`?`F~0-@ho!zs#Y( zlJsT!m*qd~@ZVHP|KVx}>6o(pyhQ$7e_Rc+|6cF#|Cs!9*^!pge~t3D)n6jjUkdr@ z{kPFlf5#m9{RbPzw&c$XssD#Vzr?ct^!Fdi{ns+aUVnL{{|*b&QtCHV{z zr_2AiMgLicevcu>v7+`Zfnelh7ct3dxphySbOuez78mn{DwNB&zZ`9EXHe^^I*{g;tGO~ZzAkopPA z-&TJ+NnbT@@1@(%a~A#A9r_)H8o!Fp|D_In-%aKBpZ@+vdHmE@&q*j;uKf)q{dy*i z&|htfuTc(ver2hR5{b-B+OBVfI4*mN`Up-zC_LB8i zLp>*Ant@Iz|$iHaSnV#eNvyH#bf4ln6_0HphY=?fwvyJ|`YA4pu zaOih?%;*<^{@WJ)*$(|r&o%ltMV(mxMu-0Ptww+Q1;@*;zkgJge=+HEKg(*#U5x(D zOtWOJLU=D;u5kFbF4jkt&&S_m=dGwe?8nQ;$-hdtjIZbs*pQZ-KjWHR zOr1*q1LVJ(3+nK%ru@DBj|0+jJ=Ndd`UdFw)89YJ`W#l5M`~-2(wAxftMQ$F?N)|+ s)l&M`P)zon3i(?9X#8Irq1TSTv>v|BQ7bn8`3KGZZ>-9PFX{jP0CEE~%m4rY literal 0 HcmV?d00001 diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/Main.d b/gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/Main.d new file mode 100644 index 0000000..0c16f29 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/Main.d @@ -0,0 +1,124 @@ +objects/Main.o: Main.cpp ClientLogging.hpp SDKClient.hpp \ + ClientPlatformSpecific.hpp ClientPlatformSpecificTypes.hpp \ + ManusSDK/include/ManusSDK.h ManusSDK/include/ManusSDKTypes.h \ + ManusSDK/include/ManusSDKTypeInitializers.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/pybind11.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/detail/class.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/attr.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/detail/common.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/Python.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/patchlevel.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pyconfig.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pymacconfig.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pyport.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/exports.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pymacro.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pymath.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pymem.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/pymem.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/object.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/object.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/objimpl.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/objimpl.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/typeslots.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pyhash.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/pydebug.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/bytearrayobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/bytearrayobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/bytesobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/bytesobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/unicodeobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/unicodeobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/longobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/longintrepr.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/boolobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/floatobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/complexobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/rangeobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/memoryobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/tupleobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/tupleobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/listobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/listobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/dictobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/dictobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/odictobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/enumobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/setobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/methodobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/methodobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/moduleobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/funcobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/classobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/fileobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/fileobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pycapsule.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/code.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/code.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pyframe.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/traceback.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/traceback.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/sliceobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cellobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/iterobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/initconfig.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/genobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pystate.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/pystate.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/abstract.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/abstract.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/descrobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/genericaliasobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/warnings.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/weakrefobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/structseq.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/namespaceobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/picklebufobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/pytime.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/codecs.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pyerrors.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/pyerrors.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pythread.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/context.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/modsupport.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/compile.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/compile.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pythonrun.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/pythonrun.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pylifecycle.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/pylifecycle.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/ceval.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/ceval.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/sysmodule.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/sysmodule.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/osmodule.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/intrcheck.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/import.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/import.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/bltinmodule.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/eval.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/pyctype.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pystrtod.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pystrcmp.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/fileutils.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/fileutils.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/pyfpe.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/tracemalloc.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/frameobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/cpython/frameobject.h \ + /home/gear/miniconda3/envs/gr00t_wbc/include/python3.10/pythread.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/cast.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/detail/descr.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/detail/type_caster_base.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/pytypes.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/buffer_info.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/detail/cpp_conduit.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/detail/internals.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/detail/typeid.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/detail/value_and_holder.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/options.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/detail/exception_translation.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/detail/init.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/gil.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/gil_safe_call_once.h \ + /home/gear/miniconda3/envs/gr00t_wbc/lib/python3.10/site-packages/pybind11/include/pybind11/typing.h diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/Main.o b/gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/Main.o new file mode 100644 index 0000000000000000000000000000000000000000..fbf06ed4ed9c643d214109ccb397b873d1bd9ec5 GIT binary patch literal 3823648 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%0AC4gRR(TH>EvC) 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 z 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%0AQ_VLr3yII{ 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ðKGi&Y6mIMLX}Di&<^OZIt?%#PDKDvi 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≥)rH{~SMnAH#pZYup#l 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 B*MI;3 literal 0 HcmV?d00001 diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/SDKClient.d b/gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/SDKClient.d new file mode 100644 index 0000000..281f130 --- /dev/null +++ b/gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/SDKClient.d @@ -0,0 +1,5 @@ +objects/SDKClient.o: SDKClient.cpp SDKClient.hpp \ + ClientPlatformSpecific.hpp ClientPlatformSpecificTypes.hpp \ + ManusSDK/include/ManusSDK.h ManusSDK/include/ManusSDKTypes.h \ + ManusSDK/include/ManusSDKTypeInitializers.h \ + ManusSDK/include/ManusSDKTypes.h ClientLogging.hpp diff --git a/gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/SDKClient.o b/gr00t_wbc/control/teleop/device/SDKClient_Linux/objects/SDKClient.o new file mode 100644 index 0000000000000000000000000000000000000000..3a05d8e4921195c7ea721d6082235b53470927b1 GIT binary patch literal 1904960 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∓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