From 6b850c782674d0905d3f9eac964d21abf3b7c666 Mon Sep 17 00:00:00 2001 From: Kassam Dakhlalah Date: Tue, 4 Aug 2026 16:09:17 +0400 Subject: [PATCH] Update 2026-08-04 16:09:16 --- CMakeLists.txt | 13 +- README.md | 311 ++-- SETUP_G1.md | 104 ++ build.sh | 29 + docker/Dockerfile | 70 + docker/README.md | 57 + docker/build_image.sh | 80 + docker/docker-compose.yml | 18 + docker/run.sh | 42 + docker/start.sh | 44 + docker/stop.sh | 2 + example/arm_action.cpp | 91 + example/arm_raise.cpp | 170 ++ example/greet.cpp | 28 + example/greet_common.hpp | 102 + example/hand_bridge.cpp | 208 +++ example/hand_diag.cpp | 290 +++ example/hand_forcetest.cpp | 135 ++ example/hand_gestures.cpp | 188 ++ example/hand_pose.cpp | 61 + example/hand_readtest.cpp | 49 + example/hand_record.cpp | 185 ++ example/hand_setid.cpp | 119 ++ example/hand_wrtest.cpp | 57 + example/handshake.cpp | 23 + example/like.cpp | 56 + example/thumbup.cpp | 23 + include/SerialPort.h | 7 +- include/inspire.h | 187 +- inspire_g1.cpp | 579 +++++- manage.sh | 141 ++ .../g1_record_replay.cpython-313.pyc | Bin 0 -> 66749 bytes recorder/arm_home.jsonl | 482 +++++ recorder/g1_record_replay.py | 1103 +++++++++++ robot.sh | 46 + start.sh | 143 ++ udev/99-inspire-hands.rules | 12 + vendor/unitree_sdk2_python/.gitignore | 39 + vendor/unitree_sdk2_python/LICENSE | 29 + vendor/unitree_sdk2_python/README zh.md | 121 ++ vendor/unitree_sdk2_python/README.md | 113 ++ .../a2/audio/a2_audio_client_example.py | 39 + .../a2/audio/a2_audio_client_play_wav.py | 38 + .../example/a2/audio/test.wav | Bin 0 -> 132246 bytes .../example/a2/audio/wav.py | 166 ++ .../example/a2/sport/a2_sport_client.py | 152 ++ .../example/a2/sport/a2_sport_state.py | 53 + .../example/b2/camera/camera_opencv.py | 51 + .../example/b2/camera/capture_image.py | 51 + .../example/b2/high_level/b2_sport_client.py | 106 ++ .../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/audio/g1_audio_client_play_wav.py | 35 + .../example/g1/audio/test.wav | Bin 0 -> 132246 bytes .../example/g1/audio/wav.py | 166 ++ .../g1/high_level/g1_arm5_sdk_dds_example.py | 192 ++ .../g1/high_level/g1_arm7_sdk_dds_example.py | 194 ++ .../g1/high_level/g1_arm_action_example.py | 141 ++ .../g1/high_level/g1_loco_client_example.py | 117 ++ .../g1/low_level/g1_low_level_example.py | 205 ++ .../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 | 155 ++ .../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 ++ .../h2/high_level/h2_arm_sdk_dds_example.py | 198 ++ .../h2/high_level/h2_loco_client_example.py | 111 ++ .../h2/low_level/h2_ankle_swing_example.py | 206 +++ .../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 | 38 + .../obstacles_avoid/obstacles_avoid_switch.py | 94 + .../example/vui_client/vui_client_example.py | 77 + .../wireless_controller.py | 131 ++ vendor/unitree_sdk2_python/pyproject.toml | 3 + vendor/unitree_sdk2_python/setup.py | 21 + .../unitree_sdk2py/__init__.py | 15 + .../unitree_sdk2py/a2/__init__.py | 1 + .../unitree_sdk2py/a2/audio/__init__.py | 0 .../unitree_sdk2py/a2/audio/audio_api.py | 26 + .../unitree_sdk2py/a2/audio/audio_client.py | 74 + .../unitree_sdk2py/a2/sport/__init__.py | 0 .../unitree_sdk2py/a2/sport/sport_api.py | 41 + .../unitree_sdk2py/a2/sport/sport_client.py | 182 ++ .../unitree_sdk2py/b2/__init__.py | 0 .../unitree_sdk2py/b2/back_video/__init__.py | 0 .../b2/back_video/back_video_api.py | 16 + .../b2/back_video/back_video_client.py | 23 + .../unitree_sdk2py/b2/front_video/__init__.py | 0 .../b2/front_video/front_video_api.py | 16 + .../b2/front_video/front_video_client.py | 23 + .../unitree_sdk2py/b2/robot_state/__init__.py | 0 .../b2/robot_state/robot_state_api.py | 28 + .../b2/robot_state/robot_state_client.py | 119 ++ .../unitree_sdk2py/b2/sport/__init__.py | 0 .../unitree_sdk2py/b2/sport/sport_api.py | 45 + .../unitree_sdk2py/b2/sport/sport_client.py | 219 +++ .../unitree_sdk2py/b2/vui/__init__.py | 0 .../unitree_sdk2py/b2/vui/vui_api.py | 21 + .../unitree_sdk2py/b2/vui/vui_client.py | 86 + .../unitree_sdk2py/comm/__init__.py | 0 .../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 | 301 +++ .../unitree_sdk2py/core/channel_config.py | 25 + .../unitree_sdk2py/core/channel_name.py | 34 + .../unitree_sdk2py/g1/__init__.py | 0 .../unitree_sdk2py/g1/arm/__init__.py | 0 .../g1/arm/g1_arm_action_api.py | 19 + .../g1/arm/g1_arm_action_client.py | 56 + .../unitree_sdk2py/g1/audio/__init__.py | 0 .../unitree_sdk2py/g1/audio/g1_audio_api.py | 24 + .../g1/audio/g1_audio_client.py | 71 + .../unitree_sdk2py/g1/loco/__init__.py | 0 .../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 | 80 + .../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 | 63 + .../unitree_sdk2py/go2/sport/sport_client.py | 363 ++++ .../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/__init__.py | 0 .../unitree_sdk2py/h1/loco/__init__.py | 0 .../unitree_sdk2py/h1/loco/h1_loco_api.py | 31 + .../unitree_sdk2py/h1/loco/h1_loco_client.py | 83 + .../unitree_sdk2py/h2/__init__.py | 0 .../unitree_sdk2py/h2/loco/__init__.py | 0 .../unitree_sdk2py/h2/loco/h2_loco_api.py | 40 + .../unitree_sdk2py/h2/loco/h2_loco_client.py | 249 +++ .../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 | 268 +++ .../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 | 35 + .../unitree_hg/msg/dds_/_MainBoardState_.py | 30 + .../idl/unitree_hg/msg/dds_/_MotorCmd_.py | 33 + .../idl/unitree_hg/msg/dds_/_MotorState_.py | 36 + .../unitree_hg/msg/dds_/_PressSensorState_.py | 30 + .../idl/unitree_hg/msg/dds_/__init__.py | 19 + .../unitree_sdk2py/rpc/__init__.py | 0 .../unitree_sdk2py/rpc/client.py | 111 ++ .../unitree_sdk2py/rpc/client_base.py | 128 ++ .../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 | Bin 0 -> 7928 bytes .../unitree_sdk2py/utils/lib/crc_amd64.so | Bin 0 -> 15136 bytes .../unitree_sdk2py/utils/singleton.py | 11 + .../unitree_sdk2py/utils/thread.py | 83 + .../unitree_sdk2py/utils/timerfd.py | 45 + web/__pycache__/hand_web.cpython-312.pyc | Bin 0 -> 54775 bytes web/__pycache__/hand_web.cpython-313.pyc | Bin 0 -> 106698 bytes web/hand_web.py | 1648 +++++++++++++++++ 297 files changed, 19635 insertions(+), 160 deletions(-) create mode 100644 SETUP_G1.md create mode 100755 build.sh create mode 100644 docker/Dockerfile create mode 100644 docker/README.md create mode 100755 docker/build_image.sh create mode 100644 docker/docker-compose.yml create mode 100755 docker/run.sh create mode 100755 docker/start.sh create mode 100755 docker/stop.sh create mode 100644 example/arm_action.cpp create mode 100644 example/arm_raise.cpp create mode 100644 example/greet.cpp create mode 100644 example/greet_common.hpp create mode 100644 example/hand_bridge.cpp create mode 100644 example/hand_diag.cpp create mode 100644 example/hand_forcetest.cpp create mode 100644 example/hand_gestures.cpp create mode 100644 example/hand_pose.cpp create mode 100644 example/hand_readtest.cpp create mode 100644 example/hand_record.cpp create mode 100644 example/hand_setid.cpp create mode 100644 example/hand_wrtest.cpp create mode 100644 example/handshake.cpp create mode 100644 example/like.cpp create mode 100644 example/thumbup.cpp create mode 100755 manage.sh create mode 100644 recorder/__pycache__/g1_record_replay.cpython-313.pyc create mode 100644 recorder/arm_home.jsonl create mode 100644 recorder/g1_record_replay.py create mode 100755 robot.sh create mode 100755 start.sh create mode 100644 udev/99-inspire-hands.rules create mode 100644 vendor/unitree_sdk2_python/.gitignore create mode 100644 vendor/unitree_sdk2_python/LICENSE create mode 100644 vendor/unitree_sdk2_python/README zh.md create mode 100644 vendor/unitree_sdk2_python/README.md create mode 100644 vendor/unitree_sdk2_python/example/a2/audio/a2_audio_client_example.py create mode 100644 vendor/unitree_sdk2_python/example/a2/audio/a2_audio_client_play_wav.py create mode 100644 vendor/unitree_sdk2_python/example/a2/audio/test.wav create mode 100644 vendor/unitree_sdk2_python/example/a2/audio/wav.py create mode 100644 vendor/unitree_sdk2_python/example/a2/sport/a2_sport_client.py create mode 100644 vendor/unitree_sdk2_python/example/a2/sport/a2_sport_state.py create mode 100644 vendor/unitree_sdk2_python/example/b2/camera/camera_opencv.py create mode 100644 vendor/unitree_sdk2_python/example/b2/camera/capture_image.py create mode 100644 vendor/unitree_sdk2_python/example/b2/high_level/b2_sport_client.py create mode 100644 vendor/unitree_sdk2_python/example/b2/low_level/b2_stand_example.py create mode 100644 vendor/unitree_sdk2_python/example/b2/low_level/unitree_legged_const.py create mode 100644 vendor/unitree_sdk2_python/example/b2w/camera/camera_opencv.py create mode 100644 vendor/unitree_sdk2_python/example/b2w/camera/capture_image.py create mode 100644 vendor/unitree_sdk2_python/example/b2w/high_level/b2w_sport_client.py create mode 100644 vendor/unitree_sdk2_python/example/b2w/low_level/b2w_stand_example.py create mode 100644 vendor/unitree_sdk2_python/example/b2w/low_level/unitree_legged_const.py create mode 100644 vendor/unitree_sdk2_python/example/g1/audio/g1_audio_client_example.py create mode 100644 vendor/unitree_sdk2_python/example/g1/audio/g1_audio_client_play_wav.py create mode 100644 vendor/unitree_sdk2_python/example/g1/audio/test.wav create mode 100644 vendor/unitree_sdk2_python/example/g1/audio/wav.py create mode 100644 vendor/unitree_sdk2_python/example/g1/high_level/g1_arm5_sdk_dds_example.py create mode 100644 vendor/unitree_sdk2_python/example/g1/high_level/g1_arm7_sdk_dds_example.py create mode 100644 vendor/unitree_sdk2_python/example/g1/high_level/g1_arm_action_example.py create mode 100644 vendor/unitree_sdk2_python/example/g1/high_level/g1_loco_client_example.py create mode 100644 vendor/unitree_sdk2_python/example/g1/low_level/g1_low_level_example.py create mode 100644 vendor/unitree_sdk2_python/example/g1/readme.md create mode 100644 vendor/unitree_sdk2_python/example/go2/front_camera/camera_opencv.py create mode 100644 vendor/unitree_sdk2_python/example/go2/front_camera/capture_image.py create mode 100644 vendor/unitree_sdk2_python/example/go2/high_level/go2_sport_client.py create mode 100644 vendor/unitree_sdk2_python/example/go2/high_level/go2_utlidar_switch.py create mode 100644 vendor/unitree_sdk2_python/example/go2/low_level/go2_stand_example.py create mode 100644 vendor/unitree_sdk2_python/example/go2/low_level/unitree_legged_const.py create mode 100644 vendor/unitree_sdk2_python/example/go2w/high_level/go2w_sport_client.py create mode 100644 vendor/unitree_sdk2_python/example/go2w/low_level/go2w_stand_example.py create mode 100644 vendor/unitree_sdk2_python/example/go2w/low_level/unitree_legged_const.py create mode 100644 vendor/unitree_sdk2_python/example/h1/high_level/h1_loco_client_example.py create mode 100644 vendor/unitree_sdk2_python/example/h1/low_level/h1_low_level_example.py create mode 100644 vendor/unitree_sdk2_python/example/h1/low_level/unitree_legged_const.py create mode 100644 vendor/unitree_sdk2_python/example/h1_2/low_level/h1_2_low_level_example.py create mode 100644 vendor/unitree_sdk2_python/example/h2/high_level/h2_arm_sdk_dds_example.py create mode 100644 vendor/unitree_sdk2_python/example/h2/high_level/h2_loco_client_example.py create mode 100644 vendor/unitree_sdk2_python/example/h2/low_level/h2_ankle_swing_example.py create mode 100644 vendor/unitree_sdk2_python/example/helloworld/publisher.py create mode 100644 vendor/unitree_sdk2_python/example/helloworld/subscriber.py create mode 100644 vendor/unitree_sdk2_python/example/helloworld/user_data.py create mode 100644 vendor/unitree_sdk2_python/example/motionSwitcher/motion_switcher_example.py create mode 100644 vendor/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_move.py create mode 100644 vendor/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_switch.py create mode 100644 vendor/unitree_sdk2_python/example/vui_client/vui_client_example.py create mode 100644 vendor/unitree_sdk2_python/example/wireless_controller/wireless_controller.py create mode 100644 vendor/unitree_sdk2_python/pyproject.toml create mode 100644 vendor/unitree_sdk2_python/setup.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/a2/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/a2/audio/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/a2/audio/audio_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/a2/audio/audio_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/a2/sport/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/a2/sport/sport_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/a2/sport/sport_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/back_video/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/front_video/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/sport/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/vui/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/comm/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/core/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/core/channel.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/core/channel_config.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/core/channel_name.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/g1/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/g1/arm/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/g1/arm/g1_arm_action_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/g1/arm/g1_arm_action_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/g1/audio/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/g1/loco/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/sport/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/video/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/video/video_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/video/video_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/vui/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/h1/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/h1/loco/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/h1/loco/h1_loco_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/h1/loco/h1_loco_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/h2/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/h2/loco/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/h2/loco/h2_loco_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/h2/loco/h2_loco_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/dds_/_Time_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/dds_/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/default.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Point32_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PointStamped_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Point_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Pose2D_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseStamped_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseWithCovarianceStamped_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseWithCovariance_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Pose_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_QuaternionStamped_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Quaternion_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistStamped_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistWithCovarianceStamped_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistWithCovariance_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Twist_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Vector3_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_MapMetaData_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_OccupancyGrid_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_Odometry_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/PointField_Constants/_PointField_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/PointField_Constants/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/_PointCloud2_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/_PointField_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/_Header_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/_String_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestHeader_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestIdentity_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestLease_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestPolicy_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_Request_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_ResponseHeader_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_ResponseStatus_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_Response_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_AudioData_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_BmsCmd_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_BmsState_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Error_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Go2FrontVideoData_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_HeightMap_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_IMUState_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_InterfaceConfig_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LidarState_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LowCmd_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LowState_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorCmd_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorCmds_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorState_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorStates_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_PathPoint_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Req_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Res_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_SportModeState_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_TimeSpec_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_UwbState_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_UwbSwitch_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_WirelessController_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/.idlpy_manifest create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/.idlpy_manifest create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/.idlpy_manifest create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_BmsCmd_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_BmsState_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_HandCmd_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_HandState_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_IMUState_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_LowCmd_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_LowState_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MainBoardState_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MotorCmd_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MotorState_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_PressSensorState_.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/rpc/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/rpc/client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/rpc/client_base.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/rpc/client_stub.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/rpc/internal.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/rpc/lease_client.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/rpc/lease_server.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/rpc/request_future.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/rpc/server.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/rpc/server_base.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/rpc/server_stub.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/client/obstacles_avoid_client_example.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/client/robot_service_client_example.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/client/sport_client_example.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/client/video_client_example.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/client/vui_client_example.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/crc/test_crc.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/helloworld/helloworld.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/helloworld/publisher.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/helloworld/subscriber.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/lowlevel_control.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/read_lowstate.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/sub_lowstate.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/unitree_go2_const.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_api.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_client_example.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_server_example.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/utils/__init__.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/utils/bqueue.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/utils/clib_lookup.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/utils/crc.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/utils/future.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/utils/hz_sample.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/utils/joystick.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/utils/lib/crc_aarch64.so create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/utils/lib/crc_amd64.so create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/utils/singleton.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/utils/thread.py create mode 100644 vendor/unitree_sdk2_python/unitree_sdk2py/utils/timerfd.py create mode 100644 web/__pycache__/hand_web.cpython-312.pyc create mode 100644 web/__pycache__/hand_web.cpython-313.pyc create mode 100644 web/hand_web.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 307706a..7db6bc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,4 +32,15 @@ add_executable(inspire_g1 inspire_g1.cpp) add_executable(inspire_h1 inspire_h1.cpp) # example -add_executable(hand_example example/hand_example.cpp) \ No newline at end of file +add_executable(hand_setid example/hand_setid.cpp) +add_executable(hand_example example/hand_example.cpp) +add_executable(hand_gestures example/hand_gestures.cpp) +add_executable(arm_raise example/arm_raise.cpp) +add_executable(arm_action example/arm_action.cpp) +add_executable(greet example/greet.cpp) +add_executable(handshake example/handshake.cpp) +add_executable(thumbup example/thumbup.cpp) +add_executable(hand_pose example/hand_pose.cpp) +add_executable(hand_record example/hand_record.cpp) +add_executable(hand_bridge example/hand_bridge.cpp) +add_executable(like example/like.cpp) \ No newline at end of file diff --git a/README.md b/README.md index 81f2cb2..0f9480f 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,202 @@ -
-

DFX Inspire Hand service

- - Unitree LOGO - -
+# Inspire RH56 dual-hand stack for the Unitree G1 -# 1. 📦 Introduction +Runs the two Inspire RH56DFX hands on the G1's PC2 and puts a web dashboard in front of +them: finger sliders, saved shapes, force-follow, live diagnostics, arm actions, and +**arm + hand record / replay**. -[Unitree Robot RH56DFX Inspire Hand](https://support.unitree.com/home/en/H1_developer/Dexterous_hand) Controller. +Everything runs in **one Docker container on the robot**. The workstation is only where you +edit the source and push it. -
- -
+Forked from Unitree's [DFX inspire hand service](https://support.unitree.com/home/en/H1_developer/Dexterous_hand); +the service protocol section at the bottom is theirs. +--- -The user sends `unitree_go::msg::dds::MotorCmds_` messages to the `rt/inspire/cmd` topic to control the dexterous hand, -and receives `unitree_go::msg::dds::MotorStates_` messages from the `rt/inspire/state` topic to obtain its status. +## Run it + +```bash +./start.sh +``` + +That is the whole thing. It builds the image the first time (a few minutes), starts the +container, waits until the dashboard actually answers, and prints the URL: + +``` +>> up + service=True bridge=True iface=eth0 ports=/dev/ttyUSB1, /dev/ttyUSB2 + recorder: ready (record/replay panel enabled) + http://192.168.123.164:8088 + http://10.255.254.88:8088 +``` + +| command | does | +|---|---| +| `./start.sh` | start (builds the image if missing) | +| `./start.sh stop` | stop the container | +| `./start.sh restart` | stop + start | +| `./start.sh status` | running? serving what? hands alive? | +| `./start.sh logs` | follow the container log | +| `./start.sh rebuild` | force an image rebuild, then start | + +**Run it on the robot.** Run it on the workstation and it rsyncs this folder to the robot +and runs itself there — the hands, the DDS link to the arm and Docker are all on the robot, +so there is nothing to run locally. Override the target with `ROBOT=unitree@10.255.254.88`. + +Only `./start.sh` is meant to be run by hand. `docker/start.sh` is the container's internal +entrypoint; running it directly starts the services natively, outside Docker. + +--- + +## What you get at `:8088` + +| card | what it does | +|---|---| +| **Fingers** | 12 sliders, presets, save/load named shapes | +| **Force follow** | press a fingertip and that finger closes under your hand — the RH56 cannot be back-driven, so this admittance loop is the only way to shape it by hand | +| **Joint tracker** | angle traces + per-finger force, current, force limit, temperature, ERROR/STATUS | +| **Diagnostics** | live read-back and a close→open self-test, reported per hand | +| **Read back** | copy the hand's current pose into the sliders | +| **Arm + hand combo** | built-in arm actions fused with a hand pose ⚠ arm moves | +| **Record / replay** | record the arm + hand, replay it, manage the library ⚠ arm moves | +| **Library** | manage saved shapes and combos | + +--- + +## Record / replay + +Runs [`recorder/g1_record_replay.py`](recorder/g1_record_replay.py) as a child process over a +pty — the same script you would run from a terminal, so a take made in the browser and one +made from the CLI are the same file in `DataG1/`. + +**A take:** arms hold for ~3 s, then go **limp** so you pose them by hand. Fingers *cannot* +be posed by hand, so they are driven by the in-take keys or your saved shapes. At the end the +recorder offers a preview (⚠ replays the take — the arm moves) and then a save; both are +answered in the panel. + +**Stop** sends SIGINT, so the recorder walks the arm home and releases it. It does not kill. + +**Replay** has ⏸ Pause / ▶ Resume. Pause freezes the playback clock but keeps publishing the +frame it stopped on — the arm holds the pose rather than dropping, because `rt/arm_sdk` needs +a continuous command. + +### In-take keys — and when they do nothing + +`o` both to rest · `c` both closed · `[` `]` right open/close · `;` `'` left open/close · +`f` arm/disarm follow · `1`-`9` your saved shapes. + +Which of them the recorder acts on depends on `--fingers`. Measured against the real recorder +while watching `rt/inspire/cmd`: + +| `--fingers` | what the keys do | records | +|---|---|---| +| `touch` *(default)* | **nothing** — the recorder holds the rest pose all take | how far a finger closes, from how hard you press it | +| `position` | drive the hand for real | the finger's measured angle | +| `external` | nothing — the recorder publishes no hand commands at all | the angle reached by the `:8088` sliders | +| `follow` | only `f` and the shape keys reach the hand | the angle you push a finger into | + +The panel greys out keys the current mode ignores and says why, so a dead button is never a +mystery. Note `[` and `;` also un-park thumb_rot, while `o` returns it to tucked. + +### The take library + +Per take: **▶ replay · ⬇ download · ⧉ duplicate · ✎ rename · 🗑 delete**, plus **⬆ Upload** +(validated: the first line must be the recorder's own meta header) and **🗑 Delete all**. +`arm_home.jsonl` is hidden and protected — it is the home pose every take returns through, +not a recording. + +Takes live on the **host** at `~/hand_data/DataG1`, bind-mounted into the container. +`docker/run.sh` begins with `docker rm -f`, so anything kept inside the image would be +destroyed on every restart. + +--- + +## Where things are + +| | | +|---|---| +| Workstation (canonical source) | `~/Robotics_workspace/yslootahtech/Project/G1/DFX_inspire_service` | +| Robot | `~/DFX_inspire_service` | +| In the container | `/opt/hand` (service + dashboard) · `/opt/recorder` (recorder + `DataG1`) | +| Host data (survives rebuilds) | `~/hand_data` — `hand_poses.txt`, `hand_combos.json`, `DataG1/` | + +``` +inspire_g1 RS-485 -> both hands, publishes rt/inspire/state +hand_bridge TCP 127.0.0.1:7799 -> rt/inspire/cmd, plus the force-follow loop at 30Hz +hand_web.py the dashboard on :8088, and it spawns the recorder +``` + +All three run inside the single `inspire-hand` container (`--network host --privileged +-v /dev:/dev` — DDS needs the host network, the CH340 adapters re-enumerate). + +### What is in the image + +Self-contained from a clean checkout: `recorder/` (the recorder + home pose), +`vendor/unitree_sdk2_python/`, `web/`, `example/`, `include/`. + +The build still reads three things from the **robot's own system install**, because they are +compiled libraries rather than source: + +- `~/unitree_sdk2` — the C++ static lib the binaries link against +- `/usr/local/{include,lib}` — CycloneDDS headers and runtime +- `~/cyclonedds/install/include/idl` — IDL headers; the Python `cyclonedds` wheel compiles a + `_idlpy` extension against them, and a normal CycloneDDS install does not put them in + `/usr/local` + +So: **runtime is 100% Docker; building the image needs the robot's SDK.** Override the +locations with `UNITREE_SDK2_DIR`, `CYCLONEDDS_SRC`, `RECORDER_SRC`, `UNITREE_SDK2PY_DIR`. + +`web/` is copied in the last layer, so editing the dashboard rebuilds in seconds instead of +recompiling every binary. + +--- + +## Troubleshooting + +**`recorder: NOT found`** — the image was built without `recorder/` or `vendor/`. Rebuild: +`./start.sh rebuild`. + +**Stop does nothing during a take** — the dashboard was started with SIGINT ignored (any +process backgrounded by a non-interactive shell inherits `SIG_IGN`, and it survives `exec`). +`hand_web.py` repairs this at startup; if the repair fails the panel says so. Restart it. + +**Fingers don't move (thumb might)** — usually a latched actuator fault from two `inspire_g1` +running at once. `./start.sh restart` re-runs `ClearError`. Still stuck: power-cycle the +hands (24 V). + +**"Port drift" warning** — the service opened different ttys than are live now; the CH340 +adapters re-enumerate on the hub. Restart. + +**A finger reads as dead (0 current, no movement)** — its force-sensor zero has drifted past +the grip-force limit, so the RH56 refuses to drive it. `inspire_g1` measures the resting +offset at startup and raises that hand's limit to clear it; the log names the fingers. The +real fix is re-zeroing the sensor with the hand unloaded. + +**`rt/inspire/state` reads all zeros** — cosmetic; the CH340 adapters echo their own TX, so +position read-back is unreliable. Commands are unaffected. + +**Two `inspire_g1` instances** — they collide on the RS-485 bus. `docker/run.sh` kills +host-native copies before starting the container. Kill with `pkill -x inspire_g1`, never +`pkill -f`. + +--- + +## Native (non-Docker) path + +Kept for bring-up and debugging; `manage.sh` runs the services directly on the robot +(`./manage.sh status|start|restart|dashboard|...`). It needs `./build.sh` first, since the +compiled `build/` is not shipped. Do not run it while the container is up — two writers on +the RS-485 bus corrupt the hands. + +Serial setup, udev rules and hand-side wiring: [SETUP_G1.md](SETUP_G1.md). +Docker specifics: [docker/README.md](docker/README.md). + +--- + +## Service protocol (upstream) + +Send `unitree_go::msg::dds::MotorCmds_` to `rt/inspire/cmd`; read +`unitree_go::msg::dds::MotorStates_` from `rt/inspire/state`. Only `q` is meaningful — +`1.00` = finger open, `0.00` = closed. ```mermaid graph LR @@ -23,101 +204,11 @@ A(user) --rt/inspire/cmd--> B(H1 or G1) B --rt/inspire/state--> A ``` -The IDL data is an array containing joint-level values for all 12 motors of both hands. -Currently the dexterous hand only supports joint control, i.e. only the parameter `q` makes sense in the idl format. The others are reserved. +| id | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | +|---|---|---|---|---|---|---|---|---|---|---|---|---| +| hand | right | right | right | right | right | right | left | left | left | left | left | left | +| joint | pinky | ring | middle | index | thumb-bend | thumb-rot | pinky | ring | middle | index | thumb-bend | thumb-rot | - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Id01234567891011
JointRight HandLeft Hand
pinkyringmiddleindexthumb-bendthumb-rotationpinkyringmiddleindexthumb-bendthumb-rotation
-
- - -# 2. 🚀 Launch - -## unitree h1 -```bash -sudo apt install libboost-all-dev libspdlog-dev -# Build project -mkdir build & cd build -cmake .. -make -j6 -# Terminal 1. Run h1 inspire hand service -sudo ./inspire_h1 -s /dev/ttyUSB0 -# Terminal 2. Run example -./hand_example -``` - -## unitree g1 -```bash -sudo apt install libboost-all-dev libspdlog-dev -# Build project -mkdir build & cd build -cmake .. -make -j6 -# Terminal 1. Run g1 inspire hand service -# The serial port name is hard-coded; if it doesn’t match your setup, please edit it directly in the source. -sudo ./inspire_g1 -# Terminal 2. Run example -./hand_example -``` - -# FAQ -1. Error when `make -j6` - ```bash - ... - /usr/bin/ld: inspire_ctrl.cpp:(.text._Z14serialize_intoIN10unitree_go3msg4dds_12MotorStates_EN3org7eclipse10cyclonedds4core3cdr14xcdr_v2_streamEEbPvmRKT_b[_Z14serialize_intoIN10unitree_go3msg4dds_12MotorStates_EN3org7eclipse10cyclonedds4core3cdr14xcdr_v2_streamEEbPvmRKT_b]+0x2be): undefined reference to `org::eclipse::cyclonedds::core::cdr::xcdr_v2_stream::finish_member(org::eclipse::cyclonedds::core::cdr::entity_properties&, bool)' - /usr/bin/ld: inspire_ctrl.cpp:(.text._Z14serialize_intoIN10unitree_go3msg4dds_12MotorStates_EN3org7eclipse10cyclonedds4core3cdr14xcdr_v2_streamEEbPvmRKT_b[_Z14serialize_intoIN10unitree_go3msg4dds_12MotorStates_EN3org7eclipse10cyclonedds4core3cdr14xcdr_v2_streamEEbPvmRKT_b]+0x2d9): undefined reference to `org::eclipse::cyclonedds::core::cdr::xcdr_v2_stream::finish_struct(org::eclipse::cyclonedds::core::cdr::entity_properties&)' - - ``` - please compile and install `unitree_sdk2`: - ```bash - cd ~ - git clone https://github.com/unitreerobotics/unitree_sdk2 - cd unitree_sdk2 - mkdir build & cd build - cmake .. - sudo make install - ``` -2. Error when run `sudo ./inspire_h1 -s /dev/ttyUSB0` or `sudo ./inspire_g1` - ```bash - --- Unitree Robotics --- - Inspire Hand Controller - Open serial port /dev/ttyUSB* failed - ``` - For **Unitree h1**, use the `-s` parameter to change the serial port name. - For **Unitree g1**, modify the serial port name directly in the source code. +`hand_bridge` also serves this over TCP on `127.0.0.1:7799` (`S` = state+force, `R` = state, +`F` = force-follow), which is how the dashboard talks to the hands without a DDS stack of its +own. diff --git a/SETUP_G1.md b/SETUP_G1.md new file mode 100644 index 0000000..7a9f8ec --- /dev/null +++ b/SETUP_G1.md @@ -0,0 +1,104 @@ +# Inspire RH56 dual-hand bring-up on Unitree G1 (PC2) + +Setup notes specific to **this** robot. Upstream README assumes a clean +`unitree_sdk2` install in `/usr/local` and hard-codes `/dev/ttyUSB1`+`/dev/ttyUSB2`; +neither holds here, so we adjust as below. + +Canonical (workstation): `…/yslootahtech/Project/G1/DFX_inspire_service` +Deploy to PC2 `unitree@192.168.123.164:~/DFX_inspire_service` via `rsync`, then `./build.sh`. +(wifi `10.255.254.86` is flaky; the eth0 IP `192.168.123.164` is the reliable path.) + +## 0. Easiest way to drive everything — `manage.sh` +```bash +./manage.sh # status: service + devices + adapters +./manage.sh start|stop|restart +./manage.sh open|close|box|menu # fingers only +./manage.sh like|handshake|thumbup # arm + hand (ARM MOVES) +./manage.sh arm "shake hand" # built-in arm action; armlist to list +./manage.sh help +``` +It keeps exactly **one** `inspire_g1` running (duplicates corrupt the RS-485 bus) and +auto-starts the service for gesture commands. + +## Architecture (unchanged from upstream) +`hand_example`/your app → DDS topic `rt/inspire/cmd` (`MotorCmds_`, 12 motors: +0–5 right, 6–11 left) → **`inspire_g1`** → RS-485 (CH340 USB adapters, 115200 8N1, +Inspire slave id 1) → hands. State flows back on `rt/inspire/state`. +Finger order per hand: pinky, ring, middle, index, thumb-bend, thumb-rotation. +`q` ∈ [0,1]: **0 = closed, 1 = open**. + +## 1. Build (no system changes) +`unitree_sdk2` in `/usr/local` is stale (no go2 IDL). `build.sh` links against the +newer `~/unitree_sdk2` source tree instead: +```bash +cd ~/DFX_inspire_service +./build.sh # -> build/inspire_g1, inspire_h1, hand_example +``` + +## 2. Stable serial names + no-sudo access (one-time, needs sudo) +Both hand adapters are CH340s with identical USB serial, so we pin names by +physical port and grant access via udev: +```bash +sudo cp ~/DFX_inspire_service/udev/99-inspire-hands.rules /etc/udev/rules.d/ +sudo udevadm control --reload-rules && sudo udevadm trigger +ls -l /dev/inspire_right /dev/inspire_left # both should now exist +``` +`inspire_g1.cpp` opens `/dev/inspire_right` + `/dev/inspire_left`. +Mapping (corrected after live test): right → USB port `1-2.2.1.1`, left → `1-2.2.3`. + +## 3. Run + test +```bash +cd ~/DFX_inspire_service/build +./inspire_g1 # Terminal 1 (no sudo needed after the udev rule) +./hand_example # Terminal 2 -> both hands open/close ~1 Hz +``` +Stop with Ctrl+C in each. If a hand doesn't move: check 24 V power, RS-485 A/B +polarity, and that the hand is at Inspire slave id 1. + +## Record / replay panel (dashboard) + +The dashboard's **Record / replay** card runs `g1_record_replay.py` as a child process — the +same script as the CLI, so a take made in the browser and one made from a terminal are the +same file in `DataG1/`. It needs the recorder present on whatever host serves the dashboard: + +```bash +# where g1_record_replay.py lives (searched: ../Manual_Recorder, ~/Manual_Recorder) +export RECORDER_DIR=~/Manual_Recorder +# an interpreter that can import unitree_sdk2py (on a workstation: the g1_env conda python) +export RECORDER_PY=python3 +./manage.sh dashboard # prints the resolved recorder path, or NOT FOUND +``` + +The panel starts a take, relays the in-take keys (`o c [ ] ; '`, `f`, and the saved shapes as +keys 1-9), answers the recorder's preview/save prompts, and lists `DataG1/` for replay, +rename and delete. Two things it works around, both load-bearing: + +- **Keys are paced ~150 ms apart.** The recorder reads them with `select()` + buffered + `sys.stdin.read(1)`; a burst lands in Python's buffer, `select()` then reports nothing + ready and every key after the first is stranded. Measured: 3 keys 10 ms apart → 2 arrived. +- **Stop sends SIGINT, and the dashboard repairs SIGINT first.** A process backgrounded by a + non-interactive shell inherits `SIGINT = SIG_IGN`, which survives `exec` — and `manage.sh` + starts the dashboard exactly that way, so without the repair Stop would silently do + nothing. The panel shows a warning if the repair did not hold. SIGINT (not SIGKILL) is + what lets the recorder walk the arm home and release it. + +Replay and preview **move the arm** and both require an explicit confirmation. Starting a +take disarms force-follow, since the bridge and the recorder would otherwise both publish +`rt/inspire/cmd` every frame. + +## If you move an adapter to a different USB socket +The udev `KERNELS` value changes. Re-discover and update the rule: +```bash +udevadm info -a -n /dev/ttyUSB0 | grep -m1 'KERNELS=="[0-9]' +``` + +## Troubleshooting +- **`/dev/inspire_*` missing / no CH340 in `lsusb`** → the hand USB-RS485 adapters are + unplugged (or robot rebooted with them out). Re-plug them; udev recreates the names. +- **Fingers don't move (thumb might)** → usually a latched actuator fault from running + two `inspire_g1` at once. `inspire_g1` now runs `ClearError` + sets speed/force on + startup; `./manage.sh restart` re-applies it. If still stuck, power-cycle the hands (24 V). +- **Two `inspire_g1` instances** → they collide on the serial bus; `./manage.sh status` + flags it, `restart` fixes it. Kill with `pkill -x inspire_g1` (never `pkill -f`). +- **`rt/inspire/state` reads all-zeros** → cosmetic; CH340 adapters echo their TX so + position read-back is unreliable. Does not affect commands sent to the hand. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..ee1f1c0 --- /dev/null +++ b/build.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Build DFX_inspire_service on the G1 onboard PC (PC2 / Jetson, aarch64). +# +# Why the extra flags: the factory unitree_sdk2 installed in /usr/local is stale +# and lacks the go2 IDL headers (MotorCmds_/MotorStates_) + matching symbols. +# We point the compiler/linker at the newer unitree_sdk2 source tree instead, +# without touching /usr/local (no `sudo make install`). +# +# Override the SDK location with: UNITREE_SDK2_DIR=/path/to/unitree_sdk2 ./build.sh +set -euo pipefail + +SDK="${UNITREE_SDK2_DIR:-$HOME/unitree_sdk2}" +ARCH="$(uname -m)" # aarch64 on the G1 Jetson + +if [ ! -f "${SDK}/lib/${ARCH}/libunitree_sdk2.a" ]; then + echo "ERROR: unitree_sdk2 not found at ${SDK} (expected ${SDK}/lib/${ARCH}/libunitree_sdk2.a)" >&2 + echo " Set UNITREE_SDK2_DIR to the unitree_sdk2 source tree." >&2 + exit 1 +fi + +cd "$(dirname "$0")" +mkdir -p build && cd build +cmake .. \ + -DCMAKE_CXX_FLAGS="-I${SDK}/include" \ + -DCMAKE_EXE_LINKER_FLAGS="-L${SDK}/lib/${ARCH}" +make -j"$(nproc)" + +echo +echo "Built: $(pwd)/inspire_g1 inspire_h1 hand_example" diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..75b0c95 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,70 @@ +# Inspire hand service + web dashboard, self-contained (aarch64 / Jetson). +# Build context is assembled by build_image.sh (pulls unitree_sdk2 + CycloneDDS +# from the robot). Run with: --network host --privileged -v /dev:/dev +FROM arm64v8/ubuntu:20.04 + +ENV DEBIAN_FRONTEND=noninteractive TZ=UTC +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential cmake \ + libboost-all-dev libspdlog-dev libeigen3-dev \ + python3 python3-flask \ + iproute2 procps ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# CycloneDDS (ddscxx + iceoryx) headers & runtime libs from the robot's /usr/local +COPY deps/usr_local_include/ /usr/local/include/ +COPY deps/usr_local_lib/ /usr/local/lib/ +RUN ldconfig + +# unitree_sdk2 headers + prebuilt static lib (for build.sh linking) +COPY deps/unitree_sdk2/ /opt/unitree_sdk2/ + +# Record/replay panel: the recorder is a python child process that speaks DDS itself, so +# the image needs the bindings as well as the script. Skipped cleanly when build_image.sh +# could not find the sources (empty dirs) — the panel then reports "recorder not found". +# NOTE: this layer pip-installs, so the robot needs network access at BUILD time. Offline +# alternative: copy the robot's own site-packages in the same way the C libs are copied. +COPY deps/recorder/ /opt/recorder/ +COPY deps/unitree_sdk2_python/ /opt/unitree_sdk2_python/ +# cyclonedds is pinned to what unitree_sdk2py demands (0.10.2) — installing anything else +# just gets downgraded when the SDK lands. It is built from sdist here, so it compiles +# against the robot's own CycloneDDS headers (0.10.5) and links its libddsc. +ARG CYCLONEDDS_PY=0.10.2 +ENV CYCLONEDDS_HOME=/usr/local +# The SDK is COPIED into dist-packages rather than pip-installed, and both alternatives +# were tried on the robot first: +# * a wheel install loses four subpackages — b2, comm, g1, h1 ship with no __init__.py, +# so find_packages() drops them and the package's own +# `from . import idl, utils, core, rpc, go2, b2` dies with "cannot import name 'b2'"; +# * `pip install -e` reports success (runs setup.py develop) but never lands on sys.path +# in this image — the very next line still gets "No module named 'unitree_sdk2py'". +# A copy is deterministic and keeps every subdirectory, including the ones that resolve as +# implicit namespace packages. numpy comes from apt; opencv-python (its other declared +# dependency, ~100MB+) is skipped because nothing in the SDK imports cv2 — verified. +# The destination is derived from the freshly-installed cyclonedds rather than from +# sysconfig: on Debian/Ubuntu sysconfig's purelib is /usr/lib/python3.8/site-packages, +# which is not even on sys.path (it is dist-packages here), so the copy landed nowhere. +RUN if [ -d /opt/unitree_sdk2_python/unitree_sdk2py ]; then \ + apt-get update && apt-get install -y --no-install-recommends \ + python3-pip python3-dev python3-numpy && \ + pip3 install --no-cache-dir "cyclonedds==${CYCLONEDDS_PY}" && \ + cp -r /opt/unitree_sdk2_python/unitree_sdk2py \ + "$(python3 -c 'import cyclonedds,os;print(os.path.dirname(os.path.dirname(cyclonedds.__file__)))')/" && \ + python3 -c "import unitree_sdk2py, cyclonedds; print('python DDS ok')" && \ + rm -rf /var/lib/apt/lists/* ; \ + else echo "no unitree_sdk2_python in context — record/replay disabled"; fi +ENV RECORDER_DIR=/opt/recorder RECORDER_PY=python3 + +# the service sources (everything except web/), then build the binaries in-image +COPY app/ /opt/hand/ +WORKDIR /opt/hand +RUN chmod +x build.sh docker/start.sh manage.sh 2>/dev/null; \ + UNITREE_SDK2_DIR=/opt/unitree_sdk2 ./build.sh + +# The dashboard goes in LAST, on purpose: it is the part that changes every other day, and +# from here a redeploy is a seconds-long layer copy instead of a full C++ recompile. +COPY app_web/ /opt/hand/web/ + +ENV HAND_REPO=/opt/hand +EXPOSE 8088 +CMD ["/opt/hand/docker/start.sh"] diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..bed46c7 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,57 @@ +# Dockerized Inspire hand service + dashboard + +One self-contained image (aarch64) running `inspire_g1` + `hand_bridge` + the Flask +dashboard. Build ON the robot (it pulls `unitree_sdk2` + CycloneDDS from the host). + +## Build (once, on the robot) +```bash +cd ~/DFX_inspire_service +./docker/build_image.sh # assembles context + docker build -> inspire-hand:latest +``` + +## Run +```bash +./docker/run.sh # or: docker compose -f docker/docker-compose.yml up -d +# dashboard -> http://:8088 +./docker/stop.sh +``` + +`run.sh` first stops any host-native `inspire_g1`/`hand_bridge` — running both the +container and the host service would put **two writers on the RS-485 bus** and +corrupt the hands. + +## Why these flags +- `--network host` — CycloneDDS (rt/inspire/cmd) and the arm actions (rt/arm_sdk, + reaching the G1 motion controller on eth0) need the host network. +- `--privileged -v /dev:/dev` — the CH340 `ttyUSB*` adapters re-enumerate on the hub; + mounting `/dev` + privileged lets the in-container dynamic detection find them. +- `IFACE=eth0` — interface for arm actions (must be eth0). Fingers work on either. + +## Record / replay in the image +The dashboard's Record/Replay card runs `g1_record_replay.py` as a child process, so the +image needs the script **and** a python that can import `unitree_sdk2py`. `build_image.sh` +pulls both into the context: + +```bash +RECORDER_SRC=~/Manual_Recorder \ +UNITREE_SDK2PY_DIR=~/unitree_sdk2_python \ + ./docker/build_image.sh +``` + +Neither is required — leave them out and the image still builds, with the panel reporting +*"recorder not found"*. `start.sh` prints which case you got. + +- **That layer pip-installs (`cyclonedds`, `unitree_sdk2py`), so the robot needs network at + BUILD time.** Offline alternative: copy the robot's own python site-packages into the + image the same way `build_image.sh` copies the C libs. +- **Takes are a host volume** — `run.sh` mounts `$HAND_DATA_DIR/DataG1` to + `/opt/recorder/DataG1`, because `run.sh` starts with `docker rm -f` and would otherwise + destroy every recording ever made. +- **`arm_home.jsonl` is seeded on first start**, not baked into `DataG1/` — the bind-mount + would hide a baked copy, and without it every take skips its return-to-home. It is hidden + from the takes list and cannot be deleted from the panel: it is a home pose, not a take. + +## Notes +- Sudo/udev not needed inside the container (privileged). +- Saved poses/combos persist via `-v $HAND_DATA_DIR:/opt/hand/data` + `HAND_DATA` (set by + `run.sh`). They used to live in the image and were wiped by every rebuild. diff --git a/docker/build_image.sh b/docker/build_image.sh new file mode 100755 index 0000000..9cf6234 --- /dev/null +++ b/docker/build_image.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +# Assemble the build context (pull unitree_sdk2 + CycloneDDS from this robot) and +# build the self-contained image. Run ON the robot (aarch64). +# ./docker/build_image.sh +set -euo pipefail + +HERE="$(cd "$(dirname "$0")" && pwd)" # .../DFX_inspire_service/docker +REPO="$(dirname "$HERE")" # .../DFX_inspire_service +SDK="${UNITREE_SDK2_DIR:-$HOME/unitree_sdk2}" +CTX="$HERE/.ctx" +IMG="${IMG:-inspire-hand:latest}" + +[ -f "$SDK/lib/aarch64/libunitree_sdk2.a" ] || { echo "ERROR: unitree_sdk2 not at $SDK"; exit 1; } + +echo ">> assembling build context in $CTX" +rm -rf "$CTX" +mkdir -p "$CTX/deps/usr_local_include" "$CTX/deps/usr_local_lib" "$CTX/deps/unitree_sdk2" "$CTX/app" + +# unitree_sdk2 headers + static lib +cp -r "$SDK/include" "$CTX/deps/unitree_sdk2/" +cp -r "$SDK/lib" "$CTX/deps/unitree_sdk2/" + +# CycloneDDS headers: C (dds, ddsc), C++ (ddscxx) and iceoryx +for d in dds ddsc ddscxx iceoryx; do cp -r "/usr/local/include/$d" "$CTX/deps/usr_local_include/"; done + +# The IDL headers (idl/, idlc/) are needed ONLY by the python binding: pip building +# cyclonedds compiles a _idlpy extension against idl/retcode.h, and `make install` of the +# C++ side does not put them in /usr/local/include. Without them the image build dies at +# idlpy/src/context.h:17: fatal error: idl/retcode.h: No such file or directory +# CYCLONEDDS_SRC points at the checkout whose install/ prefix has them (0.10.5 here). +CDDS_SRC="${CYCLONEDDS_SRC:-$HOME/cyclonedds}" +for d in idl idlc; do + if [ -d "/usr/local/include/$d" ]; then cp -r "/usr/local/include/$d" "$CTX/deps/usr_local_include/" + elif [ -d "$CDDS_SRC/install/include/$d" ]; then cp -r "$CDDS_SRC/install/include/$d" "$CTX/deps/usr_local_include/" + fi +done +# CycloneDDS shared runtime libs +cp -P /usr/local/lib/libddsc* /usr/local/lib/libddscxx* \ + /usr/local/lib/libcyclonedds* /usr/local/lib/libdds_security_* \ + "$CTX/deps/usr_local_lib/" 2>/dev/null || true + +# The app, with web/ split into its own context dir. The Dockerfile copies web/ AFTER the +# C++ build, so editing the dashboard rebuilds in seconds instead of recompiling every +# binary — and the dashboard is what changes most often. +rsync -a --exclude 'build/' --exclude 'docker/.ctx/' --exclude 'web/' "$REPO/" "$CTX/app/" +rsync -a "$REPO/web/" "$CTX/app_web/" +cp "$HERE/Dockerfile" "$CTX/Dockerfile" + +# ---- recorder for the dashboard's Record/Replay panel --------------------------------- +# The panel runs g1_record_replay.py as a CHILD PROCESS, so the script and a python that +# can import unitree_sdk2py both have to be inside the image. Both dirs are always created +# (empty is fine) because the Dockerfile COPYs them unconditionally — with an empty one the +# image still builds and the panel simply reports "recorder not found". +# Both live IN this project (recorder/ and vendor/), so a clean checkout builds a complete +# image with no environment set and nothing fetched from elsewhere on the machine. The env +# vars remain as overrides for anyone pointing at their own working copy. +REC="${RECORDER_SRC:-$REPO/recorder}" +SDKPY="${UNITREE_SDK2PY_DIR:-$REPO/vendor/unitree_sdk2_python}" +mkdir -p "$CTX/deps/recorder/seed" "$CTX/deps/unitree_sdk2_python" +if [ -f "$REC/g1_record_replay.py" ] && [ -f "$SDKPY/setup.py" ]; then + cp "$REC/g1_record_replay.py" "$CTX/deps/recorder/" + # The home pose is seeded rather than shipped in DataG1/, because run.sh bind-mounts + # DataG1 over the top and would hide anything baked there. + for h in "$REC/arm_home.jsonl" "$REC/DataG1/arm_home.jsonl"; do + [ -f "$h" ] && { cp "$h" "$CTX/deps/recorder/seed/"; break; } + done + rsync -a --exclude '.git/' --exclude '*.egg-info/' "$SDKPY/" "$CTX/deps/unitree_sdk2_python/" + echo ">> recorder included: $REC + unitree_sdk2py: $SDKPY" +else + echo ">> NOTE: record/replay panel will be DISABLED in this image." + [ -f "$REC/g1_record_replay.py" ] || echo " missing $REC/g1_record_replay.py (set RECORDER_SRC)" + [ -f "$SDKPY/setup.py" ] || echo " missing $SDKPY/setup.py (set UNITREE_SDK2PY_DIR)" +fi + +echo ">> docker build -t $IMG (this compiles the binaries in-image; takes a few min)" +cd "$CTX" +docker build -t "$IMG" . +rm -rf "$CTX" +echo ">> done: $IMG" +echo " run with: $REPO/docker/run.sh" diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..f60c5e3 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,18 @@ +# Run the pre-built image (build first: ./docker/build_image.sh). +# docker compose -f docker/docker-compose.yml up -d +services: + inspire-hand: + image: inspire-hand:latest + container_name: inspire-hand + network_mode: host # CycloneDDS + arm actions need the host network (eth0) + privileged: true # + /dev mount so the container sees the CH340 ttyUSB* + volumes: + - /dev:/dev + # Compose does NOT expand a bare ~ in a volume path (it would create a directory + # literally named "~"), so the default goes through $HOME. + - ${HAND_DATA_DIR:-${HOME}/hand_data}:/opt/hand/data # shapes + combos + - ${HAND_DATA_DIR:-${HOME}/hand_data}/DataG1:/opt/recorder/DataG1 # takes + environment: + - IFACE=eth0 + - HAND_DATA=/opt/hand/data + restart: unless-stopped diff --git a/docker/run.sh b/docker/run.sh new file mode 100755 index 0000000..cea5380 --- /dev/null +++ b/docker/run.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# Run the containerized hand stack (service + bridge + dashboard). +# Stops any host-native inspire_g1/bridge first — two services would collide on +# the RS-485 bus. Needs --network host (DDS) + --privileged -v /dev:/dev (serial). +set -e +IMG="${IMG:-inspire-hand:latest}" +IFACE="${IFACE:-eth0}" + +echo ">> removing old container, then killing host-native services (avoid double inspire_g1)" +docker rm -f inspire-hand 2>/dev/null || true # kill container's procs first +pkill -x inspire_g1 2>/dev/null || true # now only host natives remain +pkill -x hand_bridge 2>/dev/null || true +pkill -f hand_web.py 2>/dev/null || true +sleep 1; pkill -9 -x inspire_g1 2>/dev/null || true + +# INSPIRE_RIGHT_PATH pins which physical USB socket is the RIGHT hand. Both hands answer on +# RS-485 id 1, so without this the side is inferred from USB path ORDER — correct today, but +# it would flip undetected if the two dongles were swapped between sockets. +# Operator-verified at the robot 2026-08-03: usb 1-2.2.1.1 is the physical RIGHT hand. +# (An earlier wiggle test concluded the opposite; the operator watching the robot corrected +# it. Flip this one value if the sides ever read swapped again — nothing else needs changing.) +RIGHT_PATH="${INSPIRE_RIGHT_PATH:-1-2.2.1.1}" +# Saved shapes/combos on the HOST, so rebuilding the image no longer wipes the library. +DATA_DIR="${HAND_DATA_DIR:-$HOME/hand_data}" +# Recordings live on the HOST for the same reason the shape library does: a `docker rm -f` +# in this script would otherwise destroy every take ever made. +mkdir -p "$DATA_DIR" "$DATA_DIR/DataG1" + +docker run -d --name inspire-hand --restart unless-stopped \ + --network host --privileged -v /dev:/dev \ + -v "$DATA_DIR":/opt/hand/data \ + -v "$DATA_DIR/DataG1":/opt/recorder/DataG1 \ + -e HAND_DATA=/opt/hand/data \ + -e INSPIRE_RIGHT_PATH="$RIGHT_PATH" \ + -e IFACE="$IFACE" "$IMG" +echo ">> right hand pinned to usb $RIGHT_PATH · data dir $DATA_DIR · takes $DATA_DIR/DataG1" + +sleep 4 +docker logs --tail 24 inspire-hand || true +echo ">> inspire_g1 instances (want 1, the container's): $(pgrep -xc inspire_g1)" +ip=$(hostname -I | awk '{print $1}') +echo ">> dashboard: http://$ip:8088" diff --git a/docker/start.sh b/docker/start.sh new file mode 100755 index 0000000..a3d307e --- /dev/null +++ b/docker/start.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# Container entrypoint: supervised inspire_g1 + hand_bridge + Flask dashboard. +set -u +cd /opt/hand +export HAND_REPO=/opt/hand +IFACE="${IFACE:-eth0}" +echo "[inspire-hand] iface=$IFACE ttyUSB: $(ls /dev/ttyUSB* 2>/dev/null | tr '\n' ' ')" + +# Supervisor: (re)start inspire_g1 whenever the CH340 adapters are present but the +# service isn't running. Self-heals across the flaky-hub disconnect/reconnect cycles. +( + while true; do + if ! pgrep -x inspire_g1 >/dev/null 2>&1; then + if ls /dev/ttyUSB* >/dev/null 2>&1; then + echo "[supervisor] adapters present, starting inspire_g1: $(ls /dev/ttyUSB* | tr '\n' ' ')" + ./build/inspire_g1 >/tmp/inspire_g1.log 2>&1 & + sleep 2 + sed 's/^/[inspire_g1] /' /tmp/inspire_g1.log + fi + fi + sleep 3 + done +) & + +# TCP->DDS bridge for the dashboard (stays up regardless; commands no-op if service down) +setsid ./build/hand_bridge "$IFACE" 7799 >/tmp/hand_bridge.log 2>&1 /dev/null | grep -vc arm_home) takes)" +else + echo "[inspire-hand] recorder: NOT in image — record/replay panel disabled" +fi + +echo "[inspire-hand] dashboard on :8088" +exec python3 web/hand_web.py --port 8088 diff --git a/docker/stop.sh b/docker/stop.sh new file mode 100755 index 0000000..b76d37f --- /dev/null +++ b/docker/stop.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +docker rm -f inspire-hand 2>/dev/null && echo "inspire-hand stopped" || echo "inspire-hand not running" diff --git a/example/arm_action.cpp b/example/arm_action.cpp new file mode 100644 index 0000000..bb5f83e --- /dev/null +++ b/example/arm_action.cpp @@ -0,0 +1,91 @@ +/** + * @file arm_action.cpp + * @brief Trigger Unitree's built-in G1 upper-body actions (shake hand, etc.). + * + * Uses unitree_sdk2 G1ArmActionClient. The action server drives rt/arm_sdk for + * you (balance-aware, Unitree-designed motions) — do NOT run arm_raise at the + * same time (error 7400 = rt/arm_sdk occupied). + * + * ./arm_action list # list actions + * ./arm_action "shake hand" # by name + * ./arm_action 27 [iface] # by id (default iface: eth0) + * ./arm_action "release arm" # = 99, returns the arm to normal + * + * After most actions the arm HOLDS until you send "release arm" (99). + * Actions require the robot FSM id in {500,501,801} (check rt/sportmodestate). + */ + +#include "unitree/robot/g1/arm/g1_arm_action_client.hpp" +#include "unitree/robot/g1/arm/g1_arm_action_error.hpp" + +#include +#include +#include + +using namespace unitree::robot::g1; + +int main(int argc, char **argv) +{ + if (argc < 2) { + std::cout << "Usage: ./arm_action [networkInterface]\n"; + return 1; + } + std::string action = argv[1]; + std::string iface = argc > 2 ? argv[2] : "eth0"; + + unitree::robot::ChannelFactory::Instance()->Init(0, iface); + auto client = std::make_shared(); + client->Init(); + client->SetTimeout(10.f); + + if (action == "list") { + std::cout << "Built-in actions (id name):\n"; + for (const auto &kv : client->action_map) + std::cout << " " << kv.second << "\t" << kv.first << "\n"; + std::string data; + if (client->GetActionList(data) == 0) + std::cout << "\nServer-reported list:\n" << data << std::endl; + return 0; + } + + // Resolve action id from a number or a name. + int32_t id = 0; + try { + id = std::stoi(action); + } catch (const std::exception &) { + auto it = client->action_map.find(action); + if (it == client->action_map.end()) { + std::cerr << "Unknown action '" << action << "'. Try: ./arm_action list\n"; + return 1; + } + id = it->second; + } + + std::cout << "Executing action id " << id << " ..." << std::endl; + int32_t ret = client->ExecuteAction(id); + if (ret == 0) { + std::cout << "OK. (Arm holds until you send \"release arm\" / 99.)" << std::endl; + return 0; + } + + switch (ret) { + case UT_ROBOT_ARM_ACTION_ERR_ARMSDK: + std::cerr << "Error 7400: " << UT_ROBOT_ARM_ACTION_ERR_ARMSDK_DESC + << " (something else holds rt/arm_sdk — e.g. arm_raise)." << std::endl; + break; + case UT_ROBOT_ARM_ACTION_ERR_HOLDING: + std::cerr << "Error 7401: " << UT_ROBOT_ARM_ACTION_ERR_HOLDING_DESC << std::endl; + break; + case UT_ROBOT_ARM_ACTION_ERR_INVALID_ACTION_ID: + std::cerr << "Error 7402: " << UT_ROBOT_ARM_ACTION_ERR_INVALID_ACTION_ID_DESC << std::endl; + break; + case UT_ROBOT_ARM_ACTION_ERR_INVALID_FSM_ID: + std::cerr << "Error 7404: invalid FSM id. Actions need fsm id {500,501,801} " + "(check rt/sportmodestate; in 801 only fsm mode {0,3})." << std::endl; + break; + default: + std::cerr << "Execute action failed, error code: " << ret << std::endl; + break; + } + return ret; +} diff --git a/example/arm_raise.cpp b/example/arm_raise.cpp new file mode 100644 index 0000000..e9dcccf --- /dev/null +++ b/example/arm_raise.cpp @@ -0,0 +1,170 @@ +/** + * @file arm_raise.cpp + * @brief SAFE standalone test: raise the G1 RIGHT arm via the arm_sdk interface. + * + * Based on unitree_sdk2 example g1_arm7_sdk_dds_example.cpp, hardened for safety: + * - aborts if rt/lowstate is not received (never commands from bogus zeros) + * - ENTER-gated phases (engage -> raise -> lower) + * - Ctrl+C ramps the blend weight to 0 (gracefully hands the arm back) + * + * Moves ONLY the right arm; left arm + waist are held at their measured pose. + * + * ./arm_raise [networkInterface] (default: eth0) + * + * !!! THE ARM PHYSICALLY MOVES. Clear the space, stop any other arm controller, + * keep the E-stop in hand. !!! + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static const std::string kTopicArmSDK = "rt/arm_sdk"; +static const std::string kTopicState = "rt/lowstate"; + +enum JointIndex { + kLeftHipPitch, kLeftHipRoll, kLeftHipYaw, kLeftKnee, kLeftAnkle, kLeftAnkleRoll, + kRightHipPitch, kRightHipRoll, kRightHipYaw, kRightKnee, kRightAnkle, kRightAnkleRoll, + kWaistYaw, kWaistRoll, kWaistPitch, + kLeftShoulderPitch, kLeftShoulderRoll, kLeftShoulderYaw, kLeftElbow, + kLeftWristRoll, kLeftWristPitch, kLeftWristYaw, + kRightShoulderPitch, kRightShoulderRoll, kRightShoulderYaw, kRightElbow, + kRightWristRoll, kRightWristPitch, kRightWristYaw, + kNotUsedJoint, kNotUsedJoint1, kNotUsedJoint2, kNotUsedJoint3, kNotUsedJoint4, kNotUsedJoint5 +}; + +static std::atomic g_stop{false}; +static void onSigint(int) { g_stop = true; } + +int main(int argc, char **argv) +{ + const std::string iface = argc > 1 ? argv[1] : "eth0"; + std::signal(SIGINT, onSigint); + + unitree::robot::ChannelFactory::Instance()->Init(0, iface); + + unitree::robot::ChannelPublisherPtr pub; + pub.reset(new unitree::robot::ChannelPublisher(kTopicArmSDK)); + pub->InitChannel(); + + static unitree_hg::msg::dds_::LowState_ state; + std::atomic got{false}; + unitree::robot::ChannelSubscriberPtr sub; + sub.reset(new unitree::robot::ChannelSubscriber(kTopicState)); + sub->InitChannel([&](const void *m) { + std::memcpy(&state, m, sizeof(state)); + got = true; + }, 1); + + // SAFETY 1: require fresh state before touching the arm. + std::cout << "Waiting for " << kTopicState << " on '" << iface << "' ..." << std::endl; + for (int i = 0; i < 300 && !got; i++) std::this_thread::sleep_for(std::chrono::milliseconds(10)); + if (!got) { + std::cerr << "ERROR: no " << kTopicState << " received. Is the robot controller up, and is '" + << iface << "' the interface to the robot? Aborting (no arm command sent)." << std::endl; + return 1; + } + + const std::array arm = { + kLeftShoulderPitch, kLeftShoulderRoll, kLeftShoulderYaw, kLeftElbow, + kLeftWristRoll, kLeftWristPitch, kLeftWristYaw, + kRightShoulderPitch, kRightShoulderRoll, kRightShoulderYaw, kRightElbow, + kRightWristRoll, kRightWristPitch, kRightWristYaw, + kWaistYaw, kWaistRoll, kWaistPitch}; + const int RIGHT0 = 7; // right arm occupies arm[7..13] + + const float kp = 60.f, kd = 1.5f, dt = 0.02f; + const float max_delta = 0.5f * dt; // 0.5 rad/s velocity limit + const auto sleep_t = std::chrono::milliseconds(20); + + std::array start{}; + for (int i = 0; i < 17; i++) start[i] = state.motor_state().at(arm[i]).q(); + + // Right-arm raise target [ShPitch, ShRoll, ShYaw, Elbow, WrRoll, WrPitch, WrYaw]. + // Conservative greeting raise (arm out + forearm up). Tune here. + const std::array rraise = {0.0f, -1.0f, 0.0f, 1.4f, 0.0f, 0.0f, 0.0f}; + std::array target = start; + for (int j = 0; j < 7; j++) target[RIGHT0 + j] = rraise[j]; + + std::array des = start; + float weight = 0.f; + unitree_hg::msg::dds_::LowCmd_ msg; + + auto publish = [&](float w) { + msg.motor_cmd().at(kNotUsedJoint).q(w); + for (int i = 0; i < 17; i++) { + auto &mc = msg.motor_cmd().at(arm[i]); + mc.q(des[i]); mc.dq(0.f); mc.kp(kp); mc.kd(kd); mc.tau(0.f); + } + pub->Write(msg); + }; + + auto release = [&]() { + std::cout << "Releasing arm (weight -> 0) ..." << std::endl; + while (weight > 0.f) { + weight = std::max(0.f, weight - 0.015f); + publish(weight); + std::this_thread::sleep_for(sleep_t); + } + publish(0.f); + }; + + // Move 'des' toward 'goal' (velocity limited). Returns false if interrupted. + auto move_to = [&](const std::array &goal, int hold_steps) -> bool { + for (int step = 0; step < 600; step++) { + if (g_stop) return false; + float maxerr = 0.f; + for (int i = 0; i < 17; i++) { + float d = std::clamp(goal[i] - des[i], -max_delta, max_delta); + des[i] += d; + maxerr = std::max(maxerr, std::abs(goal[i] - des[i])); + } + publish(1.f); + std::this_thread::sleep_for(sleep_t); + if (maxerr < 1e-3f) { // arrived -> hold + for (int h = 0; h < hold_steps && !g_stop; h++) { + publish(1.f); + std::this_thread::sleep_for(sleep_t); + } + return !g_stop; + } + } + return !g_stop; + }; + + std::cout << "\n*** G1 RIGHT-ARM RAISE TEST ***\n" + << "The right arm WILL move. Ensure clear space + E-stop ready.\n" + << "Press ENTER to engage the arm (no motion yet) ... " << std::flush; + std::cin.get(); + if (g_stop) { release(); return 0; } + + // Phase 1: engage — ramp weight to 1 while commanding the measured pose (no motion). + while (weight < 1.f && !g_stop) { + weight = std::min(1.f, weight + 0.015f); + publish(weight); + std::this_thread::sleep_for(sleep_t); + } + if (g_stop) { release(); return 0; } + + std::cout << "Engaged. Press ENTER to RAISE the right arm ... " << std::flush; + std::cin.get(); + if (g_stop || !move_to(target, 100)) { release(); return 0; } + + std::cout << "Raised. Press ENTER to LOWER the arm ... " << std::flush; + std::cin.get(); + move_to(start, 25); + + release(); + std::cout << "Done." << std::endl; + return 0; +} diff --git a/example/greet.cpp b/example/greet.cpp new file mode 100644 index 0000000..6f9704d --- /dev/null +++ b/example/greet.cpp @@ -0,0 +1,28 @@ +/** + * @file greet.cpp + * @brief Combined entry point for the fused arm+hand greetings (see also the + * standalone ./handshake and ./thumbup binaries). + * + * ./greet [handshake|thumbup] [networkInterface] (default iface: eth0) + * + * Needs ./inspire_g1 running for the fingers. The ARM physically moves — + * clear space + E-stop ready. + */ +#include "greet_common.hpp" + +int main(int argc, char **argv) +{ + const std::string what = argc > 1 ? argv[1] : "handshake"; + const std::string iface = argc > 2 ? argv[2] : "eth0"; + + auto arm = greet_init(iface); + Hand hand; + usleep(300000); + + if (what == "handshake") runHandshake(*arm, hand); + else if (what == "thumbup") runThumbup(*arm, hand); + else { std::cout << "Usage: ./greet [handshake|thumbup] [networkInterface]\n"; return 1; } + + std::cout << "done." << std::endl; + return 0; +} diff --git a/example/greet_common.hpp b/example/greet_common.hpp new file mode 100644 index 0000000..a4f690e --- /dev/null +++ b/example/greet_common.hpp @@ -0,0 +1,102 @@ +/** + * @file greet_common.hpp + * @brief Shared arm-action + Inspire-hand greeting routines (handshake / thumbup). + * + * Arm uses Unitree's built-in actions (G1ArmActionClient, balance-aware). + * Hand uses the Inspire DDS bridge -> REQUIRES ./inspire_g1 running for fingers. + */ +#pragma once + +#include "unitree/robot/g1/arm/g1_arm_action_client.hpp" +#include "unitree/robot/g1/arm/g1_arm_action_error.hpp" +#include +#include + +#include +#include +#include +#include +#include + +using Vec6 = Eigen::Matrix; + +// Inspire hand command publisher (open-loop; needs inspire_g1 running). +class Hand +{ +public: + Hand() + { + pub_ = std::make_shared>("rt/inspire/cmd"); + pub_->InitChannel(); + cmd_.cmds().resize(12); + } + void set(const Vec6 &right, const Vec6 &left) + { + for (size_t i = 0; i < 6; i++) { cmd_.cmds()[i].q() = right(i); cmd_.cmds()[i + 6].q() = left(i); } + pub_->Write(cmd_); + } +private: + unitree_go::msg::dds_::MotorCmds_ cmd_; + unitree::robot::ChannelPublisherPtr pub_; +}; + +inline Vec6 gv(float p, float r, float m, float i, float tb, float tr) { Vec6 v; v << p, r, m, i, tb, tr; return v; } +inline void ghold(int ms) { usleep(ms * 1000); } + +// q in [0,1], 0=closed 1=open; order pinky,ring,middle,index,thumb_bend,thumb_rot +static const Vec6 G_OPEN = gv(1, 1, 1, 1, 1, 1); +static const Vec6 G_THUMBUP = gv(0, 0, 0, 0, 1, 1); +static const Vec6 G_SHAKE_READY = gv(0.85, 0.85, 0.85, 0.85, 0.85, 0.6); +static const Vec6 G_SHAKE_GRIP = gv(0.2, 0.2, 0.2, 0.2, 0.25, 0.1); + +// Execute a built-in arm action; print a helpful message on failure. +inline bool arm_do(unitree::robot::g1::G1ArmActionClient &arm, int id, const char *name) +{ + using namespace unitree::robot::g1; + std::cout << "arm: " << name << " (" << id << ") ..." << std::endl; + int32_t ret = arm.ExecuteAction(id); + if (ret != 0) { + std::cerr << " arm action '" << name << "' failed, code " << ret; + if (ret == UT_ROBOT_ARM_ACTION_ERR_ARMSDK) std::cerr << " (rt/arm_sdk occupied)"; + else if (ret == UT_ROBOT_ARM_ACTION_ERR_INVALID_FSM_ID) std::cerr << " (need fsm {500,501,801})"; + else if (ret == UT_ROBOT_ARM_ACTION_ERR_HOLDING) std::cerr << " (arm holding; send release 99)"; + std::cerr << std::endl; + return false; + } + return true; +} + +// arm reaches out -> hand grips -> thumbs-up -> arm releases +inline void runHandshake(unitree::robot::g1::G1ArmActionClient &arm, Hand &hand) +{ + hand.set(G_SHAKE_READY, G_OPEN); // present an open hand + if (!arm_do(arm, 27, "shake hand")) { hand.set(G_OPEN, G_OPEN); return; } + ghold(300); + std::cout << "hand: grip" << std::endl; + hand.set(G_SHAKE_GRIP, G_OPEN); ghold(1500); + std::cout << "hand: thumbs-up" << std::endl; + hand.set(G_THUMBUP, G_OPEN); ghold(1200); + hand.set(G_OPEN, G_OPEN); ghold(400); + arm_do(arm, 99, "release arm"); +} + +// raise right hand -> hand thumbs-up -> arm releases +inline void runThumbup(unitree::robot::g1::G1ArmActionClient &arm, Hand &hand) +{ + if (!arm_do(arm, 23, "right hand up")) { hand.set(G_OPEN, G_OPEN); return; } + ghold(300); + std::cout << "hand: thumbs-up" << std::endl; + hand.set(G_THUMBUP, G_OPEN); ghold(2500); + hand.set(G_OPEN, G_OPEN); ghold(400); + arm_do(arm, 99, "release arm"); +} + +// Shared setup: init DDS + arm client + hand, then return the arm client. +inline std::shared_ptr greet_init(const std::string &iface) +{ + unitree::robot::ChannelFactory::Instance()->Init(0, iface); + auto arm = std::make_shared(); + arm->Init(); + arm->SetTimeout(10.f); + return arm; +} diff --git a/example/hand_bridge.cpp b/example/hand_bridge.cpp new file mode 100644 index 0000000..447b49a --- /dev/null +++ b/example/hand_bridge.cpp @@ -0,0 +1,208 @@ +/** + * @file hand_bridge.cpp + * @brief Local TCP <-> DDS bridge for the web dashboard. + * - "v0 v1 ... v11\n" -> publish MotorCmds_ to rt/inspire/cmd (q in [0,1], or <0 = release) + * - "R\n" -> reply with the 12 latest rt/inspire/state q values (read-back / capture) + * - "S\n" -> reply with 24 values: 12 angles then 12 forces (joint tracker) + * Persistent publisher + state subscriber. Needs ./inspire_g1 running. + * + * ./hand_bridge [iface] [port] (default eth0 7799) + */ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + std::string iface = argc > 1 ? argv[1] : "eth0"; + int port = argc > 2 ? std::atoi(argv[2]) : 7799; + + unitree::robot::ChannelFactory::Instance()->Init(0, iface); + auto pub = std::make_shared>("rt/inspire/cmd"); + pub->InitChannel(); + unitree_go::msg::dds_::MotorCmds_ cmd; + cmd.cmds().resize(12); + + // Subscribe to hand state for read-back / capture. + std::mutex mtx; + double state[12] = {0}; + double force[12] = {0}; + double cur[12] = {0}, fset[12] = {0}; // dq = current mA, ddq = force limit + double temp[12] = {0}, err[12] = {0}, sta[12] = {0}, lost[12] = {0}; + double rate = 0; int rn = 0; auto rt0 = std::chrono::steady_clock::now(); + + // ---- force-follow (admittance) -------------------------------------------------- + // The RH56 cannot be back-driven, so pushing a finger moves it 0.000. This closes the + // loop in software: read the press, drive the finger that way, hold when you stop. It + // lives here rather than in the web layer because the loop needs the 30Hz state stream + // and the command publisher in the same place -- polling over HTTP is far too slow. + std::atomic follow{false}; + std::atomic fgain{0.0006}, fdead{60.0}, fmaxrate{0.35}, fforce{400.0}; + double ftarget[12] = {0}, fbase[12] = {0}; + std::atomic fcal{false}; // tau_est, in grams — the joint tracker needs BOTH angle and + // force, because 'force moved but angle did not' is the whole + // signature of a finger that cannot be back-driven. + auto sub = std::make_shared>("rt/inspire/state"); + sub->InitChannel([&](const void *m) { + auto s = (const unitree_go::msg::dds_::MotorStates_ *)m; + std::lock_guard lk(mtx); + for (int i = 0; i < 12 && i < (int)s->states().size(); i++) { + state[i] = s->states()[i].q(); + force[i] = s->states()[i].tau_est(); + cur[i] = s->states()[i].dq(); // mA + fset[i] = s->states()[i].ddq(); // grip force limit + temp[i] = s->states()[i].temperature(); + err[i] = s->states()[i].reserve()[0]; + sta[i] = s->states()[i].reserve()[1]; + lost[i] = s->states()[i].lost(); + } + // Measure the ACTUAL publish rate here — a hand whose bus has died keeps serving + // cached values, so a healthy-looking readout with a collapsed rate is the tell. + if (++rn >= 20) { + auto now = std::chrono::steady_clock::now(); + double dt = std::chrono::duration(now - rt0).count(); + if (dt > 0) rate = rn / dt; + rn = 0; rt0 = now; + } + }); + + // Admittance thread: only publishes while follow is on, so it never fights the + // dashboard, the recorder, or anything else driving the hand. + std::thread ftick([&]{ + auto prev = std::chrono::steady_clock::now(); + while (true) { + std::this_thread::sleep_for(std::chrono::milliseconds(33)); + auto now = std::chrono::steady_clock::now(); + double dt = std::chrono::duration(now - prev).count(); prev = now; + if (!follow.load()) continue; + if (dt > 0.2) dt = 0.2; + { + std::lock_guard lk(mtx); + if (fcal.exchange(false)) { // arm: zero on the current force + for (int i = 0; i < 12; i++) { fbase[i] = force[i]; ftarget[i] = state[i]; } + } + for (int i = 0; i < 12; i++) { + double dev = force[i] - fbase[i]; + if (std::abs(dev) <= fdead.load()) continue; + dev -= (dev > 0 ? fdead.load() : -fdead.load()); + double step = -fgain.load() * dev * dt; // press pad (+force) -> close + double cap = fmaxrate.load() * dt; + if (step > cap) step = cap; + if (step < -cap) step = -cap; + ftarget[i] += step; + if (ftarget[i] < 0.0) ftarget[i] = 0.0; + if (ftarget[i] > 1.0) ftarget[i] = 1.0; + } + for (int i = 0; i < 12; i++) { + cmd.cmds()[i].q() = ftarget[i]; + cmd.cmds()[i].kp() = fforce.load(); + } + } + pub->Write(cmd); + } + }); + ftick.detach(); + + int srv = socket(AF_INET, SOCK_STREAM, 0); + int opt = 1; + setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + sockaddr_in addr{}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = htons(port); + if (bind(srv, (sockaddr *)&addr, sizeof(addr)) < 0) { perror("bind"); return 1; } + listen(srv, 8); + std::cout << "hand_bridge listening on 127.0.0.1:" << port << " (iface " << iface << ")" << std::endl; + + char buf[1024]; + while (true) + { + int c = accept(srv, nullptr, nullptr); + if (c < 0) continue; + int n = read(c, buf, sizeof(buf) - 1); + if (n > 0) + { + buf[n] = 0; + if (buf[0] == 'F' || buf[0] == 'f') // follow: "F 1 gain dead maxrate force" | "F 0" + { + std::istringstream iss(buf + 1); + int on = 0; double g = 0, d = 0, mr = 0, fo = 0; + iss >> on; + if (iss >> g && g > 0) fgain.store(g); + if (iss >> d && d > 0) fdead.store(d); + if (iss >> mr && mr > 0) fmaxrate.store(mr); + if (iss >> fo && fo > 0) fforce.store(fo); + if (on) fcal.store(true); // re-zero the baseline each time it arms + follow.store(on != 0); + std::string o = std::string("follow ") + (on ? "on" : "off") + "\n"; + (void)!write(c, o.c_str(), o.size()); + } + else if (buf[0] == 'D' || buf[0] == 'd') // full diagnostics: 8x12 + rate + { + std::string out; + { std::lock_guard lk(mtx); + char t[32]; + const double *blocks[8] = {state, force, cur, fset, temp, err, sta, lost}; + for (int b = 0; b < 8; b++) + for (int i = 0; i < 12; i++) { snprintf(t, sizeof(t), "%.3f ", blocks[b][i]); out += t; } + snprintf(t, sizeof(t), "%.2f ", rate); out += t; } + out += "\n"; + (void)!write(c, out.c_str(), out.size()); + } + else if (buf[0] == 'S' || buf[0] == 's') // angle + force: 24 values + { + std::string out; + { std::lock_guard lk(mtx); + char t[32]; + for (int i = 0; i < 12; i++) { snprintf(t, sizeof(t), "%.3f ", state[i]); out += t; } + for (int i = 0; i < 12; i++) { snprintf(t, sizeof(t), "%.1f ", force[i]); out += t; } } + out += "\n"; + (void)!write(c, out.c_str(), out.size()); + } + else if (buf[0] == 'R' || buf[0] == 'r') // read-back request (12 angles, legacy) + { + std::string out; + { std::lock_guard lk(mtx); + char t[32]; + for (int i = 0; i < 12; i++) { snprintf(t, sizeof(t), "%.3f ", state[i]); out += t; } } + out += "\n"; + (void)!write(c, out.c_str(), out.size()); + } + else + { + std::istringstream iss(buf); + std::vector v; float x; + while (iss >> x) v.push_back(x); + if (v.size() == 12) + { + for (int i = 0; i < 12; i++) cmd.cmds()[i].q() = v[i]; + for (int k = 0; k < 3; k++) { pub->Write(cmd); usleep(8000); } + (void)!write(c, "ok\n", 3); + } + else (void)!write(c, "err\n", 4); + } + } + close(c); + } + return 0; +} diff --git a/example/hand_diag.cpp b/example/hand_diag.cpp new file mode 100644 index 0000000..fd3c8a4 --- /dev/null +++ b/example/hand_diag.cpp @@ -0,0 +1,290 @@ +/** + * @file hand_diag.cpp + * @brief Read the Inspire RH56 fault registers per finger: ERROR, STATUS, TEMP, + * CURRENT. Tells you exactly WHY a finger won't move (locked rotor, + * over-temperature, over-current, ...) instead of guessing. + * + * Register map (from the Inspire Modbus map; this serial protocol encodes the + * address as low,high in bytes 5,6 — verified: CLEAR_ERROR 1004 = 0x03EC = {0xEC,0x03}): + * CURRENT 1594 (0x063A, 12 bytes) ERROR 1606 (0x0646, 6) + * STATUS 1612 (0x064C, 6) TEMP 1618 (0x0652, 6) + * CLEAR_ERROR 1004 (0x03EC) RESET_PARA 1006 (0x03EE) + * + * g++ -std=c++17 -Iinclude example/hand_diag.cpp -o /tmp/hand_diag + * /tmp/hand_diag /dev/ttyUSB1 [clear] # 'clear' also sends CLEAR_ERROR + * + * Needs the port free (stop the container first). + */ +#include "SerialPort.h" +#include +#include +#include +#include +#include +#include + +static const char *FING[6] = {"pinky", "ring", "mid", "index", "thumbB", "thumbR"}; + +static uint8_t cksum(const uint8_t *d, int len) +{ + uint8_t s = 0; + for (int i = 2; i < len - 1; i++) s += d[i]; + return s; +} + +// Read `n` bytes from register `addr`, robustly (flush + re-query). +static bool readReg(SerialPort &sp, uint16_t addr, uint8_t n, uint8_t *out, uint8_t id = 1) +{ + uint8_t q[9] = {0xEB, 0x90, id, 0x04, 0x11, + (uint8_t)(addr & 0xFF), (uint8_t)(addr >> 8), n, 0x00}; + q[8] = cksum(q, 9); + uint8_t buf[128]; + for (int att = 0; att < 8; att++) + { + sp.flush(); + sp.send(q, 9); + usleep(8000); + size_t got = sp.recv(buf, sizeof(buf)); + for (size_t off = 0; off + 7 + n <= got; off++) + { + if (!((buf[off] == 0x90 && buf[off + 1] == 0xEB) || + (buf[off] == 0xEB && buf[off + 1] == 0x90))) continue; + if (buf[off + 4] != 0x11) continue; + uint16_t got_addr = buf[off + 5] | (buf[off + 6] << 8); + if (got_addr != addr) continue; + int frame = 7 + n + 1; // header+addr+data+checksum + if (off + frame > got) continue; + if (buf[off + frame - 1] != cksum(buf + off, frame)) continue; + memcpy(out, buf + off + 7, n); + return true; + } + } + return false; +} + +static void writeReg16(SerialPort &sp, uint16_t addr, uint16_t val, uint8_t id = 1) +{ + uint8_t c[11] = {0xEB, 0x90, id, 0x06, 0x12, + (uint8_t)(addr & 0xFF), (uint8_t)(addr >> 8), + (uint8_t)(val & 0xFF), (uint8_t)(val >> 8), 0x00, 0x00}; + c[9] = cksum(c, 10); + sp.send(c, 10); + usleep(5000); + uint8_t b[32]; + sp.recv(b, sizeof(b)); +} + +static std::string errStr(uint8_t e) +{ + if (!e) return "OK"; + std::string s; + if (e & 0x01) s += "LOCKED_ROTOR "; + if (e & 0x02) s += "OVER_TEMPERATURE "; + if (e & 0x04) s += "OVER_CURRENT "; + if (e & 0x08) s += "ABNORMAL_OPERATION "; + if (e & 0x10) s += "COMMUNICATION_ERROR "; + return s; +} + +// Command all 6 fingers to a position (ANGLE_SET 1486 = 0x05CE, 12 bytes). +static void setAll(SerialPort &sp, int16_t val, uint8_t id = 1) +{ + uint8_t c[20]; + c[0] = 0xEB; c[1] = 0x90; c[2] = id; c[3] = 0x0F; c[4] = 0x12; + c[5] = 0xCE; c[6] = 0x05; + for (int i = 0; i < 6; i++) + { + c[7 + 2 * i] = val & 0xFF; + c[8 + 2 * i] = (val >> 8) & 0xFF; + } + c[19] = cksum(c, 20); + sp.send(c, 20); + usleep(5000); + uint8_t b[32]; + sp.recv(b, sizeof(b)); +} + +// Write all 6 force limits (FORCE_SET 1498 = 0x05DA, 12 bytes). +static void setForceAll(SerialPort &sp, uint16_t val, uint8_t id = 1) +{ + uint8_t c[20]; + c[0] = 0xEB; c[1] = 0x90; c[2] = id; c[3] = 0x0F; c[4] = 0x12; + c[5] = 0xDA; c[6] = 0x05; + for (int i = 0; i < 6; i++) + { + c[7 + 2 * i] = val & 0xFF; + c[8 + 2 * i] = (val >> 8) & 0xFF; + } + c[19] = cksum(c, 20); + sp.send(c, 20); + usleep(5000); + uint8_t b[32]; + sp.recv(b, sizeof(b)); +} + +// Does the motor actually energize when told to move? current>0 => it's trying +// (mechanically blocked); current==0 => it never energizes. +// +// IMPORTANT: current==0 alone does NOT mean the actuator is dead. The RH56 stops driving a +// finger the instant its MEASURED force (FORCE_ACT 1582) reaches its force LIMIT +// (FORCE_SET 1498), so a finger whose force-sensor zero has drifted above the limit draws +// 0 mA and is indistinguishable from a dead motor. This test used to inherit whatever limit +// the service last wrote (500g) and so reproduced exactly that failure while looking like an +// independent raw-serial check — it is how the G1's right mid+index were wrongly called dead +// when they in fact run their full range at a 1000g limit. It now sweeps the limit itself. +static void moveTest(SerialPort &sp, uint16_t force) +{ + printf("\n=== MOVE TEST: force limit %ug, command CLOSE, watch current + force + angle ===\n", + force); + setForceAll(sp, force); + setAll(sp, 1000); // open first + usleep(800000); + + uint8_t f0[12] = {0}; + if (readReg(sp, 1582, 12, f0)) + { + printf("resting force vs the %ug limit (>= limit => the driver refuses to move it):\n ", + force); + for (int i = 0; i < 6; i++) + { + int16_t F = (int16_t)(f0[2 * i] | (f0[2 * i + 1] << 8)); + printf("%s=%dg%s ", FING[i], F, (F >= (int)force || -F >= (int)force) ? "<", "hand_diag "); +} + +// Are the dead channels merely CONFIGURED not to drive? A speed / force / current +// limit of 0 would give exactly the observed signature: no motion, no current, no +// error, status "reached target". That would be software-fixable. +static void paramDump(SerialPort &sp) +{ + struct { const char *name; uint16_t addr; uint8_t bytes; } regs[] = { + {"ANGLE_SET ", 1486, 12}, + {"FORCE_SET ", 1498, 12}, + {"SPEED_SET ", 1522, 12}, + {"CURRENT_LIM ", 1020, 12}, + {"ANGLE_ACT ", 1546, 12}, + }; + printf("%-13s", "register"); + for (int i = 0; i < 6; i++) printf("%9s", FING[i]); + printf("\n"); + for (auto &r : regs) + { + uint8_t b[16] = {0}; + bool ok = readReg(sp, r.addr, r.bytes, b); + printf("%-13s", r.name); + for (int i = 0; i < 6; i++) + { + if (!ok) { printf("%9s", "FAIL"); continue; } + printf("%9d", (int16_t)(b[2 * i] | (b[2 * i + 1] << 8))); + } + printf("\n"); + } + printf("\n(a 0 in SPEED_SET / FORCE_SET / CURRENT_LIM for a finger would explain\n" + " no motion + no current + no error, and would be software-fixable.)\n"); +} + +int main(int argc, char **argv) +{ + const char *dev = argc > 1 ? argv[1] : "/dev/ttyUSB1"; + std::string opt = argc > 2 ? argv[2] : ""; + bool doClear = (opt == "clear"); + bool doMove = (opt == "move"); + SerialPort sp(dev, B115200, 8); + if (opt == "params") { paramDump(sp); return 0; } + // Command a CLOSE, then read ANGLE_SET back: does the setpoint actually LAND in + // the dead channels? ANGLE_SET=0 but no motion => setpoint accepted, motor dead. + // ANGLE_SET stays 1000 => the channel rejects the write. + if (opt == "setclose") + { + printf("commanding CLOSE (ANGLE_SET=0) ...\n\n"); + setAll(sp, 0); + usleep(1500000); + paramDump(sp); + setAll(sp, 1000); + return 0; + } + // 'move [force]' — force defaults to the RH56 max so a drifted sensor zero can't + // silently gate a healthy finger off and make it look dead. + if (doMove) + { + uint16_t f = argc > 3 ? (uint16_t)atoi(argv[3]) : 1000; + moveTest(sp, f); + return 0; + } + + if (doClear) + { + printf("sending CLEAR_ERROR (1004) ...\n"); + writeReg16(sp, 1004, 1); + usleep(300000); + } + + uint8_t err[6] = {0}, sta[6] = {0}, tmp[6] = {0}, cur[12] = {0}; + uint8_t fact[12] = {0}, fset[12] = {0}; + bool eok = readReg(sp, 1606, 6, err); + bool sok = readReg(sp, 1612, 6, sta); + bool tok = readReg(sp, 1618, 6, tmp); + bool cok = readReg(sp, 1594, 12, cur); + bool faok = readReg(sp, 1582, 12, fact); // FORCE_ACT — measured + bool fsok = readReg(sp, 1498, 12, fset); // FORCE_SET — the limit + + printf("port %s (error=%s status=%s temp=%s current=%s force=%s limit=%s)\n\n", dev, + eok ? "ok" : "FAIL", sok ? "ok" : "FAIL", tok ? "ok" : "FAIL", cok ? "ok" : "FAIL", + faok ? "ok" : "FAIL", fsok ? "ok" : "FAIL"); + printf("%-8s %6s %8s %6s %9s %8s %7s %s\n", + "finger", "err", "status", "temp", "current", "force", "limit", "meaning"); + bool anyGated = false; + for (int i = 0; i < 6; i++) + { + int16_t c = cok ? (int16_t)(cur[2 * i] | (cur[2 * i + 1] << 8)) : 0; + int16_t fa = faok ? (int16_t)(fact[2 * i] | (fact[2 * i + 1] << 8)) : 0; + int16_t fs = fsok ? (int16_t)(fset[2 * i] | (fset[2 * i + 1] << 8)) : 0; + // THE check that was missing: a finger already at/over its force limit is gated off + // by the driver and will read 0 current with no error — identical to a dead motor. + bool gated = faok && fsok && fs > 0 && (fa >= fs || -fa >= fs); + if (gated) anyGated = true; + std::string mean = eok ? errStr(err[i]) : "?"; + if (gated) mean = "FORCE-GATED (not dead: force >= limit)"; + printf("%-8s 0x%02X %8d %5d\xC2\xB0 %8d %7dg %6dg %s\n", + FING[i], eok ? err[i] : 0, sok ? sta[i] : -1, tok ? tmp[i] : -1, c, + fa, fs, mean.c_str()); + } + if (anyGated) + printf("\n>> One or more fingers is FORCE-GATED: its measured force already meets its\n" + ">> limit, so the driver refuses to energise it (0 current, no error). This is a\n" + ">> drifted force-sensor zero, NOT a dead actuator. Confirm with:\n" + ">> %s %s move 1000\n" + ">> Fix by re-zeroing the sensor with the hand unloaded, or raise the limit.\n", + argv[0], dev); + return 0; +} diff --git a/example/hand_forcetest.cpp b/example/hand_forcetest.cpp new file mode 100644 index 0000000..048e664 --- /dev/null +++ b/example/hand_forcetest.cpp @@ -0,0 +1,135 @@ +/** + * @file hand_forcetest.cpp + * @brief Read BOTH the actual angle (0x0A) and the force sensor (0x2E) of the + * right hand while it holds open, to see whether pushing a finger by hand + * registers as a FORCE change even though the position can't move + * (non-backdrivable). Robust re-query read (flush + resend + scan). + * + * g++ -std=c++17 -Iinclude example/hand_forcetest.cpp -o /tmp/hft + * /tmp/hft /dev/ttyUSB1 (right hand; needs the port free) + */ +#include "inspire.h" +#include +#include +#include +#include +#include + +// Robust read of a 6-value register (angleAct 0x0A or forceAct 0x2E). +static int robustRead(SerialPort &sp, uint8_t addr, uint8_t id, + Eigen::Matrix &out, double scale, bool sign) +{ + std::vector q = {0xEB, 0x90, id, 0x04, 0x11, addr, 0x06, 0x0C, 0x00}; + uint8_t s = 0; + for (size_t i = 2; i < q.size() - 1; i++) s += q[i]; + q.back() = s; + + uint8_t buf[64]; + for (int att = 0; att < 8; att++) + { + sp.flush(); + sp.send(q.data(), q.size()); + usleep(8000); + size_t n = sp.recv(buf, sizeof(buf)); + for (size_t off = 0; off + 20 <= n; off++) + { + if (!((buf[off] == 0x90 && buf[off + 1] == 0xEB) || + (buf[off] == 0xEB && buf[off + 1] == 0x90))) continue; + if (buf[off + 4] != 0x11 || buf[off + 5] != addr) continue; + uint8_t cs = 0; + for (int i = 2; i < 19; i++) cs += buf[off + i]; + if (buf[off + 19] != cs) continue; + const uint8_t *b = buf + off; + for (int i = 0; i < 6; i++) + { + uint16_t raw = b[7 + 2 * i] | (b[8 + 2 * i] << 8); + out(i) = (sign ? (double)(int16_t)raw : (double)raw) * scale; + } + return 0; + } + } + return 1; +} + +int main(int argc, char **argv) +{ + const char *pr = argc > 1 ? argv[1] : "/dev/ttyUSB1"; + auto sp = std::make_shared(pr, B115200, 6); + inspire::InspireHand right(sp, 1); + right.ClearError(); + right.SetVelocity(1000, 1000, 1000, 1000, 1000, 1000); + right.SetForce(500, 500, 500, 500, 500, 500); + + Eigen::Matrix qopen, pos, force; + for (int i = 0; i < 6; i++) qopen(i) = 1.0; + + // DIAGNOSTIC: mimic inspire_g1's tight loop (SetPos, settle, GetPos, GetForce, + // no inter-cycle sleep) and count how often each read fails + whether force + // actually changes. This reproduces the "force frozen" symptom. + if (argc > 2 && std::string(argv[2]) == "diag") + { + int posFail = 0, forceFail = 0, N = 200; + double fmin[6], fmax[6]; + for (int i = 0; i < 6; i++) { fmin[i] = 1e9; fmax[i] = -1e9; } + for (int k = 0; k < N; k++) + { + right.SetPosition(qopen); + usleep(3000); + if (robustRead(*sp, 0x0A, 1, pos, 1.0 / 1000.0, false) != 0) posFail++; + if (robustRead(*sp, 0x2E, 1, force, 1.0, true) != 0) forceFail++; + else for (int i = 0; i < 6; i++) { if (force(i) < fmin[i]) fmin[i] = force(i); if (force(i) > fmax[i]) fmax[i] = force(i); } + } + printf("tight loop N=%d: GetPosition fails=%d GetForce fails=%d\n", N, posFail, forceFail); + printf("force range over run (g): "); + for (int i = 0; i < 6; i++) printf("[%.0f..%.0f] ", fmin[i], fmax[i]); + printf("\n"); + return 0; + } + + const char *NM[6] = {"pinky", "ring", "mid", "index", "thumbB", "thumbR"}; + const double THRESH = 100.0; // grams of deviation from baseline = a deliberate push + + // Drive open + hold, and calibrate each finger's resting-force baseline (~2s). + printf("Calibrating resting baseline — DON'T touch the fingers for 2s...\n"); + double base[6] = {0, 0, 0, 0, 0, 0}; + int nb = 0; + for (int k = 0; k < 10; k++) + { + right.SetPosition(qopen); + usleep(3000); + if (robustRead(*sp, 0x2E, 1, force, 1.0, true) == 0) + { + for (int i = 0; i < 6; i++) base[i] += force(i); + nb++; + } + usleep(150000); + } + for (int i = 0; i < 6; i++) base[i] = nb ? base[i] / nb : 0; + printf("Baseline(g): "); + for (int i = 0; i < 6; i++) printf("%s=%.0f ", NM[i], base[i]); + printf("\n\nNow PUSH one right finger at a time — it flags which finger and how hard:\n\n"); + + for (int k = 0; k < 120; k++) + { + right.SetPosition(qopen); + usleep(3000); + int rf = robustRead(*sp, 0x2E, 1, force, 1.0, true); + if (rf == 0) + { + char line[256]; int p = 0; bool pushed = false; + for (int i = 0; i < 6; i++) + { + double dev = force(i) - base[i]; + if (dev > THRESH || dev < -THRESH) + { + pushed = true; + p += sprintf(line + p, "%s:%+.0fg ", NM[i], dev); + } + } + if (pushed) printf("k%3d PUSH -> %s\n", k, line); + } + usleep(120000); + } + printf("(done)\n"); + return 0; +} diff --git a/example/hand_gestures.cpp b/example/hand_gestures.cpp new file mode 100644 index 0000000..3004845 --- /dev/null +++ b/example/hand_gestures.cpp @@ -0,0 +1,188 @@ +/** + * @file hand_gestures.cpp + * @brief Named gestures for the Inspire RH56 hands on G1, over DDS (rt/inspire/cmd). + * + * Requires the service running in another terminal: ./inspire_g1 + * + * Usage: + * ./hand_gestures # interactive menu (pick gestures repeatedly) + * ./hand_gestures [iface] # run one gesture and exit + * gesture = open | close | box | thumbup | handshake | combo | demo + * + * Per-hand DOF order: [pinky, ring, middle, index, thumb_bend, thumb_rotation] + * q in [0,1]: 0 = closed, 1 = open. Indices 0-5 = right hand, 6-11 = left hand. + * + * NOTE: this controls the HAND (fingers) only. Raising the arm is a separate + * subsystem (G1 arm joints) and is not driven here. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using Vec6 = Eigen::Matrix; + +class HandController +{ +public: + HandController() { Init(); } + + void set(const Vec6 &right, const Vec6 &left) + { + for (size_t i = 0; i < 6; i++) + { + cmd.cmds()[i].q() = right(i); + cmd.cmds()[i + 6].q() = left(i); + } + handcmd->Write(cmd); + } + void setBoth(const Vec6 &q) { set(q, q); } + + unitree_go::msg::dds_::MotorCmds_ cmd; + unitree_go::msg::dds_::MotorStates_ state; + +private: + void Init() + { + handcmd = std::make_shared>("rt/inspire/cmd"); + handcmd->InitChannel(); + cmd.cmds().resize(12); + handstate = std::make_shared>("rt/inspire/state"); + handstate->InitChannel([this](const void *message) { + std::lock_guard lock(mtx); + state = *(unitree_go::msg::dds_::MotorStates_ *)message; + }); + state.states().resize(12); + } + + std::mutex mtx; + unitree::robot::ChannelPublisherPtr handcmd; + unitree::robot::ChannelSubscriberPtr handstate; +}; + +static Vec6 V(float pinky, float ring, float middle, float index, float thumb_bend, float thumb_rot) +{ + Vec6 v; v << pinky, ring, middle, index, thumb_bend, thumb_rot; return v; +} +static void hold(int ms) { usleep(ms * 1000); } + +// --- Pose library (tune freely; values are [0,1], 0=closed 1=open) --- +static const Vec6 OPEN = V(1, 1, 1, 1, 1, 1); +static const Vec6 CLOSE = V(0, 0, 0, 0, 0, 0); +static const Vec6 BOX = V(0.5, 0.5, 0.5, 0.5, 0.4, 0.1); +static const Vec6 THUMBUP = V(0, 0, 0, 0, 1, 1); +static const Vec6 SHAKE_READY = V(0.85, 0.85, 0.85, 0.85, 0.85, 0.6); +static const Vec6 SHAKE_GRIP = V(0.2, 0.2, 0.2, 0.2, 0.25, 0.1); + +// --- Gestures (social ones use the RIGHT hand, left held open) --- +static void g_pose(HandController &h, const Vec6 &q, const char *name, int ms = 1500) +{ + std::cout << " [" << name << "]" << std::endl; + h.setBoth(q); + hold(ms); +} +static void g_thumbup(HandController &h, int ms = 2000) +{ + std::cout << " [thumbup] (right hand)" << std::endl; + h.set(THUMBUP, OPEN); + hold(ms); +} +static void g_handshake(HandController &h) +{ + std::cout << " [handshake] (right hand)" << std::endl; + h.set(SHAKE_READY, OPEN); hold(1200); // reach out, open + h.set(SHAKE_GRIP, OPEN); hold(800); // grip + for (int k = 0; k < 3; k++) // a few firm "shakes" + { + h.set(SHAKE_GRIP * 0.85f, OPEN); hold(180); + h.set(SHAKE_GRIP, OPEN); hold(180); + } + h.set(THUMBUP, OPEN); hold(1500); // finish with a thumbs-up + h.set(OPEN, OPEN); hold(600); // relax +} +static void g_demo(HandController &h) +{ + g_pose(h, OPEN, "open"); + g_pose(h, CLOSE, "close"); + g_pose(h, BOX, "box"); + g_pose(h, OPEN, "open"); + g_thumbup(h); + g_handshake(h); + g_pose(h, OPEN, "open"); +} + +// Dispatch by name or menu number. Returns false only on quit. +static bool runGesture(HandController &h, const std::string &g) +{ + if (g == "open" || g == "1") g_pose(h, OPEN, "open"); + else if (g == "close" || g == "2") g_pose(h, CLOSE, "close"); + else if (g == "box" || g == "3") g_pose(h, BOX, "box"); + else if (g == "thumbup" || g == "4") g_thumbup(h); + else if (g == "handshake" || g == "5") g_handshake(h); + else if (g == "combo" || g == "6") { g_thumbup(h); g_handshake(h); } + else if (g == "demo" || g == "7") g_demo(h); + else if (g == "q" || g == "quit" || g == "0") return false; + else std::cout << " unknown option: '" << g << "'" << std::endl; + return true; +} + +static void printMenu() +{ + std::cout << "\n=== Inspire Hand Gestures ===\n" + << " 1) open\n" + << " 2) close (fist)\n" + << " 3) box (power grasp)\n" + << " 4) thumbup (right hand)\n" + << " 5) handshake (right hand, ends thumbs-up)\n" + << " 6) combo (thumbup + handshake)\n" + << " 7) demo (run all)\n" + << " q) quit\n" + << "Select: " << std::flush; +} + +static std::string trim(std::string s) +{ + const char *ws = " \t\r\n"; + size_t a = s.find_first_not_of(ws); + size_t b = s.find_last_not_of(ws); + return a == std::string::npos ? "" : s.substr(a, b - a + 1); +} + +int main(int argc, char **argv) +{ + std::string arg1 = argc > 1 ? argv[1] : ""; + std::string iface = argc > 2 ? argv[2] : ""; + + unitree::robot::ChannelFactory::Instance()->Init(0, iface); + auto hand = std::make_shared(); + usleep(300000); // let DDS match the service + + // Direct mode: run one gesture and exit (backward compatible). + if (!arg1.empty()) + { + runGesture(*hand, arg1); + std::cout << "done." << std::endl; + return 0; + } + + // Interactive menu: fire gestures repeatedly over one DDS connection. + std::string line; + while (true) + { + printMenu(); + if (!std::getline(std::cin, line)) break; // EOF / Ctrl-D + line = trim(line); + if (line.empty()) continue; + if (!runGesture(*hand, line)) break; + } + std::cout << "bye." << std::endl; + return 0; +} diff --git a/example/hand_pose.cpp b/example/hand_pose.cpp new file mode 100644 index 0000000..64c9981 --- /dev/null +++ b/example/hand_pose.cpp @@ -0,0 +1,61 @@ +/** + * @file hand_pose.cpp + * @brief Live finger-pose tuner for the Inspire hand (right hand). Needs ./inspire_g1. + * + * ./hand_pose [networkInterface] (default iface: eth0) + * + * Then type 6 numbers (q in [0,1], 0=closed 1=open): + * pinky ring middle index thumb_bend thumb_rot + * e.g. 0 0 0 0 1 1 -> try a thumbs-up and tweak the last two values + * Shortcuts: open | close | q(uit) + * The pose is applied to the RIGHT hand (left held open) and holds until the next entry. + */ +#include +#include + +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + const std::string iface = argc > 1 ? argv[1] : "eth0"; + unitree::robot::ChannelFactory::Instance()->Init(0, iface); + + auto pub = std::make_shared>("rt/inspire/cmd"); + pub->InitChannel(); + unitree_go::msg::dds_::MotorCmds_ cmd; + cmd.cmds().resize(12); + usleep(300000); + + // right = indices 0-5, left = 6-11 (held open) + auto apply = [&](const std::vector &r) { + for (int i = 0; i < 6; i++) { cmd.cmds()[i].q() = r[i]; cmd.cmds()[i + 6].q() = 1.0f; } + for (int n = 0; n < 12; n++) { pub->Write(cmd); usleep(30000); } // ~0.36s of repeats so it lands + moves + }; + + std::cout << "Inspire RIGHT-hand pose tuner. Order: pinky ring middle index thumb_bend thumb_rot\n" + << "q in [0,1], 0=closed 1=open. Type 6 numbers, or 'open'/'close'/'q'.\n"; + + std::string line; + while (true) { + std::cout << "pose> " << std::flush; + if (!std::getline(std::cin, line)) break; + if (line == "q" || line == "quit") break; + if (line == "open") { apply({1, 1, 1, 1, 1, 1}); continue; } + if (line == "close") { apply({0, 0, 0, 0, 0, 0}); continue; } + + std::istringstream iss(line); + std::vector v; float x; + while (iss >> x) v.push_back(x); + if (v.size() != 6) { std::cout << " enter exactly 6 numbers (or open/close/q)\n"; continue; } + apply(v); + std::cout << " applied: pinky=" << v[0] << " ring=" << v[1] << " middle=" << v[2] + << " index=" << v[3] << " thumb_bend=" << v[4] << " thumb_rot=" << v[5] << "\n"; + } + std::cout << "bye.\n"; + return 0; +} diff --git a/example/hand_readtest.cpp b/example/hand_readtest.cpp new file mode 100644 index 0000000..adff6d2 --- /dev/null +++ b/example/hand_readtest.cpp @@ -0,0 +1,49 @@ +/** + * @file hand_readtest.cpp + * @brief Low-level read diagnostic: send the Inspire "read angleAct" query on a + * serial port and dump the RAW bytes that come back. Isolates whether the + * hand answers position reads (writes are confirmed working). + * + * g++ -std=c++17 -Iinclude example/hand_readtest.cpp -o /tmp/hand_readtest + * /tmp/hand_readtest /dev/ttyUSB0 [slaveid=1] [waitms=15] + * + * Needs the port free (stop inspire_g1 / the container first). + */ +#include "SerialPort.h" +#include +#include +#include +#include + +static uint8_t checksum(const uint8_t *d, int len) +{ + uint8_t s = 0; + for (int i = 2; i < len - 1; i++) s += d[i]; + return s; +} + +int main(int argc, char **argv) +{ + const char *dev = argc > 1 ? argv[1] : "/dev/ttyUSB0"; + int slave = argc > 2 ? atoi(argv[2]) : 1; + int waitms = argc > 3 ? atoi(argv[3]) : 15; + + SerialPort sp(dev, B115200, 40); // 40ms read timeout + uint8_t q[9] = {0xEB, 0x90, (uint8_t)slave, 0x04, 0x11, 0x0A, 0x06, 0x0C, 0x00}; + q[8] = checksum(q, 9); + + printf("port=%s slave=%d wait=%dms (query angleAct)\n", dev, slave, waitms); + for (int t = 0; t < 8; t++) + { + sp.flush(); + sp.send(q, 9); + usleep(waitms * 1000); + uint8_t buf[128]; + size_t n = sp.recv(buf, sizeof(buf)); + printf(" try %d: %2zu bytes:", t, n); + for (size_t i = 0; i < n; i++) printf(" %02X", buf[i]); + printf("\n"); + usleep(150000); + } + return 0; +} diff --git a/example/hand_record.cpp b/example/hand_record.cpp new file mode 100644 index 0000000..d426ace --- /dev/null +++ b/example/hand_record.cpp @@ -0,0 +1,185 @@ +/** + * @file hand_record.cpp + * @brief Record & replay named hand poses (12 joint values: right 0-5, left 6-11). + * + * You sculpt a shape live (close/open individual fingers), name it, and it's saved + * to a file. Records the COMMANDED values (the serial read-back is unreliable on + * these CH340 adapters, and the hand holds whatever it's commanded anyway). + * Needs ./inspire_g1 running. + * + * ./hand_record [iface] interactive recorder (default iface eth0) + * ./hand_record play [iface] command a saved pose and hold + * ./hand_record list list saved poses + * + * Poses file: $HOME/DFX_inspire_service/hand_poses.txt (line: "name v0 .. v11") + * Per-hand joint order: pinky ring middle index thumb_bend thumb_rot. q in [0,1]. + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Pose = std::array; + +static std::string posesFile() +{ + const char *h = getenv("HOME"); + return std::string(h ? h : ".") + "/DFX_inspire_service/hand_poses.txt"; +} + +static std::map loadPoses() +{ + std::map m; + std::ifstream f(posesFile()); + std::string line; + while (std::getline(f, line)) + { + if (line.empty() || line[0] == '#') continue; + std::istringstream iss(line); + std::string name; iss >> name; + Pose v{}; int i = 0; float x; + while (i < 12 && (iss >> x)) v[i++] = x; + if (i == 12) m[name] = v; + } + return m; +} + +static void savePose(const std::string &name, const Pose &v) +{ + auto m = loadPoses(); + m[name] = v; // add or overwrite + std::ofstream f(posesFile(), std::ios::trunc); + f << "# name \n"; + for (const auto &kv : m) + { + f << kv.first; + for (float x : kv.second) f << " " << x; + f << "\n"; + } +} + +class Hand +{ +public: + Hand() + { + pub_ = std::make_shared>("rt/inspire/cmd"); + pub_->InitChannel(); + cmd_.cmds().resize(12); + } + void apply(const Pose &v) + { + for (int i = 0; i < 12; i++) cmd_.cmds()[i].q() = v[i]; + for (int n = 0; n < 8; n++) { pub_->Write(cmd_); usleep(20000); } // ~0.16s so it lands + } +private: + unitree_go::msg::dds_::MotorCmds_ cmd_; + unitree::robot::ChannelPublisherPtr pub_; +}; + +static void printPose(const Pose &v) +{ + std::cout << " R:"; + for (int i = 0; i < 6; i++) std::cout << " " << v[i]; + std::cout << " L:"; + for (int i = 6; i < 12; i++) std::cout << " " << v[i]; + std::cout << std::endl; +} + +static void printMenu() +{ + std::cout << + "\n--- hand_record --- q in [0,1] (tune to taste), order: pinky ring middle index thumb_bend thumb_rot\n" + " r <6 nums> set RIGHT hand l <6 nums> set LEFT hand b <6 nums> set BOTH\n" + " f <0-11> set one joint (0-5 right, 6-11 left) open | close all\n" + " save save current shape load recall list | print | q\n"; +} + +int main(int argc, char **argv) +{ + std::string a1 = argc > 1 ? argv[1] : ""; + + if (a1 == "list") + { + auto m = loadPoses(); + std::cout << m.size() << " saved pose(s):\n"; + for (const auto &kv : m) { std::cout << " " << kv.first << ":"; printPose(kv.second); } + return 0; + } + if (a1 == "play") + { + if (argc < 3) { std::cerr << "usage: hand_record play [iface]\n"; return 1; } + std::string name = argv[2], iface = argc > 3 ? argv[3] : "eth0"; + auto m = loadPoses(); + if (!m.count(name)) { std::cerr << "no pose '" << name << "'. try: hand_record list\n"; return 1; } + unitree::robot::ChannelFactory::Instance()->Init(0, iface); + Hand hand; usleep(300000); + std::cout << "playing '" << name << "'\n"; printPose(m[name]); + hand.apply(m[name]); usleep(1500000); + return 0; + } + + // interactive + std::string iface = a1.empty() ? "eth0" : a1; + unitree::robot::ChannelFactory::Instance()->Init(0, iface); + Hand hand; usleep(300000); + + Pose cur; cur.fill(1.0f); // start open + hand.apply(cur); + printMenu(); + + std::string line; + while (true) + { + std::cout << "rec> " << std::flush; + if (!std::getline(std::cin, line)) break; + std::istringstream iss(line); + std::string op; iss >> op; + if (op.empty()) continue; + + if (op == "q" || op == "quit") break; + else if (op == "open") { cur.fill(1.0f); hand.apply(cur); printPose(cur); } + else if (op == "close") { cur.fill(0.0f); hand.apply(cur); printPose(cur); } + else if (op == "print") { printPose(cur); } + else if (op == "list") { for (const auto &kv : loadPoses()) { std::cout << " " << kv.first << ":"; printPose(kv.second); } } + else if (op == "r" || op == "l" || op == "b") + { + std::vector v; float x; while (iss >> x) v.push_back(x); + if (v.size() != 6) { std::cout << " need 6 numbers\n"; continue; } + if (op == "r" || op == "b") for (int i = 0; i < 6; i++) cur[i] = v[i]; + if (op == "l" || op == "b") for (int i = 0; i < 6; i++) cur[i + 6] = v[i]; + hand.apply(cur); printPose(cur); + } + else if (op == "f") + { + int idx; float val; + if (!(iss >> idx >> val) || idx < 0 || idx > 11) { std::cout << " usage: f <0-11> \n"; continue; } + cur[idx] = val; hand.apply(cur); printPose(cur); + } + else if (op == "save") + { + std::string name; iss >> name; + if (name.empty()) { std::cout << " usage: save \n"; continue; } + savePose(name, cur); + std::cout << " saved '" << name << "' -> " << posesFile() << "\n"; + } + else if (op == "load") + { + std::string name; iss >> name; + auto m = loadPoses(); + if (!m.count(name)) { std::cout << " no pose '" << name << "'\n"; continue; } + cur = m[name]; hand.apply(cur); printPose(cur); + } + else std::cout << " ? see menu\n"; + } + std::cout << "bye.\n"; + return 0; +} diff --git a/example/hand_setid.cpp b/example/hand_setid.cpp new file mode 100644 index 0000000..e19acaa --- /dev/null +++ b/example/hand_setid.cpp @@ -0,0 +1,119 @@ +/** + * @file hand_setid.cpp + * @brief Give a hand a distinct RS-485 id, permanently, so left/right can never swap again. + * + * WHY THIS EXISTS + * The RH56 has no handedness register — it cannot tell you whether it is a left or a right + * unit. Both USB-RS485 dongles are CH340 1a86:7523 reporting the SAME USB serial. So the + * only thing distinguishing the two hands was the ttyUSB number, which the kernel hands out + * in enumeration order — and that changes across replugs, reboots and hub hiccups. When it + * flips, left and right silently trade places: nothing errors, and you drive the wrong hand. + * (Observed on this robot 2026-07-30: software "L.*" was in fact the robot's RIGHT hand.) + * + * Giving the two hands DIFFERENT ids fixes that at the root. inspire_g1 scans ids 1..4 on + * each port and, when the ids differ, uses them as identity: id 1 = RIGHT, id 2 = LEFT. + * That is wiring-independent and survives any replug. + * + * USAGE — run ONCE per robot, with the OTHER hand's dongle unplugged or on its own port: + * g++ -std=c++17 -Iinclude example/hand_setid.cpp -o build/hand_setid + * ./build/hand_setid /dev/ttyUSBx # just report which id answers + * ./build/hand_setid /dev/ttyUSBx 1 2 # change id 1 -> 2 and SAVE it + * + * Convention: leave the RIGHT hand at id 1, give the LEFT hand id 2. + * Needs the port free — stop the container first. + * + * Registers: HAND_ID 1000 (0x03E8), SAVE 1005 (0x03ED). The id change only survives a + * power cycle if SAVE is written afterwards, which this does. + */ +#include "inspire.h" +#include +#include +#include +#include +#include + +static uint8_t cks(const uint8_t *d, int len) +{ + uint8_t s = 0; + for (int i = 2; i < len - 1; i++) s += d[i]; + return s; +} + +// Write the SAVE register (1005) so the new id survives a power cycle. +static void saveParams(SerialPort &sp, uint8_t id) +{ + uint8_t c[9] = {0xEB, 0x90, id, 0x04, 0x12, 0xED, 0x03, 0x01, 0x00}; + c[8] = cks(c, 9); + sp.send(c, 9); + usleep(300000); + uint8_t b[32]; + sp.recv(b, sizeof(b)); +} + +// Which id answers on this port? -1 if none. +static int findId(SerialPort::SharedPtr sp) +{ + for (int id = 1; id <= 4; id++) + { + inspire::InspireHand h(sp, (uint8_t)id); + Eigen::Matrix q; + for (int t = 0; t < 3; t++) + if (h.GetPosition(q) == 0) return id; + } + return -1; +} + +int main(int argc, char **argv) +{ + if (argc < 2) + { + printf("usage: %s [from_id to_id]\n" + " %s /dev/ttyUSB0 report the id that answers\n" + " %s /dev/ttyUSB0 1 2 change id 1 -> 2 and save\n", + argv[0], argv[0], argv[0]); + return 1; + } + const char *dev = argv[1]; + auto sp = std::make_shared(dev, B115200, 8); + + int cur = findId(sp); + if (cur < 0) + { + printf("%s: no hand answered on ids 1-4.\n" + " Is the container still holding the port? Is the hand powered?\n", dev); + return 2; + } + printf("%s: hand answers at rs485 id %d\n", dev, cur); + + if (argc < 4) return 0; + + int from = atoi(argv[2]), to = atoi(argv[3]); + if (from != cur) + { + printf("refusing: you said from=%d but this hand is at id %d.\n", from, cur); + return 3; + } + if (to < 1 || to > 4) + { + printf("refusing: to_id must be 1-4.\n"); + return 3; + } + + printf("changing id %d -> %d ...\n", from, to); + inspire::InspireHand h(sp, (uint8_t)from); + h.ChangeID((uint8_t)from, (uint8_t)to); + usleep(300000); + saveParams(*sp, (uint8_t)to); + usleep(300000); + + int now = findId(sp); + if (now == to) + printf("OK — this hand is now id %d and it is saved.\n" + " Convention: RIGHT hand stays id 1, LEFT hand becomes id 2.\n" + " inspire_g1 will now identify the hands by id, and left/right can no longer\n" + " swap however the USB ports enumerate.\n", now); + else + printf("FAILED — hand now answers at id %d (wanted %d). Nothing saved reliably;\n" + " power-cycle the hand and re-check with: %s %s\n", now, to, argv[0], dev); + return now == to ? 0 : 4; +} diff --git a/example/hand_wrtest.cpp b/example/hand_wrtest.cpp new file mode 100644 index 0000000..96ec472 --- /dev/null +++ b/example/hand_wrtest.cpp @@ -0,0 +1,57 @@ +/** + * @file hand_wrtest.cpp + * @brief Reproduce inspire_g1's write-then-read cycle standalone (no DDS) to + * debug why GetPosition reads 0 inside the container but works natively. + * + * g++ -std=c++17 -Iinclude example/hand_wrtest.cpp -o /tmp/hand_wrtest + * /tmp/hand_wrtest [rightPort=/dev/ttyUSB1] [leftPort=/dev/ttyUSB0] + * + * Needs the ports free (stop inspire_g1 / the container first). + */ +#include "inspire.h" +#include +#include +#include + +using namespace inspire; + +int main(int argc, char **argv) +{ + const char *pr = argc > 1 ? argv[1] : "/dev/ttyUSB1"; // right + const char *pl = argc > 2 ? argv[2] : "/dev/ttyUSB0"; // left + + auto s1 = std::make_shared(pr, B115200, 6); + auto s2 = std::make_shared(pl, B115200, 6); + InspireHand right(s1, 1), left(s2, 1); + + for (auto *h : {&right, &left}) + { + h->ClearError(); + h->SetVelocity(1000, 1000, 1000, 1000, 1000, 1000); + h->SetForce(500, 500, 500, 500, 500, 500); + } + + // TIGHT loop, no inter-cycle delay, alternating open/close — the worst case + // that failed 300/300 with the old one-shot GetPosition. Verify the re-query + // GetPosition holds up. + Eigen::Matrix qcmd, qr, ql; + int rfail = 0, lfail = 0, N = 300; + for (int cyc = 0; cyc < N; cyc++) + { + double v = ((cyc / 30) % 2 == 0) ? 1.0 : 0.0; // flip every 30 cycles + for (int i = 0; i < 6; i++) qcmd(i) = v; + right.SetPosition(qcmd); + left.SetPosition(qcmd); + usleep(3000); + int rr = right.GetPosition(qr); + usleep(2000); + int rl = left.GetPosition(ql); + if (rr != 0) rfail++; + if (rl != 0) lfail++; + if (cyc % 30 == 0) + printf("cyc%3d v=%.0f R(ret%d): %.2f %.2f %.2f L(ret%d): %.2f %.2f %.2f\n", + cyc, v, rr, qr(0), qr(1), qr(2), rl, ql(0), ql(1), ql(2)); + } + printf("=== %d tight cycles: RIGHT read-fails=%d LEFT read-fails=%d ===\n", N, rfail, lfail); + return 0; +} diff --git a/example/handshake.cpp b/example/handshake.cpp new file mode 100644 index 0000000..b4f79f6 --- /dev/null +++ b/example/handshake.cpp @@ -0,0 +1,23 @@ +/** + * @file handshake.cpp + * @brief G1 handshake: arm "shake hand"(27) -> Inspire hand grips -> thumbs-up -> release(99). + * + * ./handshake [networkInterface] (default iface: eth0) + * + * Needs ./inspire_g1 running for the fingers. The ARM physically moves — + * clear space + E-stop ready. + */ +#include "greet_common.hpp" + +int main(int argc, char **argv) +{ + const std::string iface = argc > 1 ? argv[1] : "eth0"; + auto arm = greet_init(iface); + Hand hand; + usleep(300000); // let DDS match the arm service + inspire_g1 + + std::cout << "*** HANDSHAKE (arm + hand). Clear space, E-stop ready. ***" << std::endl; + runHandshake(*arm, hand); + std::cout << "done." << std::endl; + return 0; +} diff --git a/example/like.cpp b/example/like.cpp new file mode 100644 index 0000000..ba2f1f0 --- /dev/null +++ b/example/like.cpp @@ -0,0 +1,56 @@ +/** + * @file like.cpp + * @brief Robot holds a thumbs-up "like" WHILE the built-in handshake arm action plays. + * + * A background thread republishes the 👍 continuously (so inspire_g1 always has a + * fresh command and the fingers stay curled), while the main thread triggers the + * handshake and holds it for a fixed window (ExecuteAction can return before the + * arm actually finishes moving, so we don't gate on its return). + * + * ./like [thumb_bend] [thumb_rot] [networkInterface] + * thumb_bend, thumb_rot in [0,1] (default 1 1). Tweak if 👍 isn't clean: + * e.g. ./like 1 0 ./like 0.7 0.3 + * default iface: eth0 + * + * Needs exactly one ./inspire_g1 running for the fingers. The ARM moves — + * clear space, E-stop ready. + */ +#include "greet_common.hpp" +#include +#include +#include + +int main(int argc, char **argv) +{ + const float tb = argc > 1 ? std::atof(argv[1]) : 1.0f; // thumb_bend (1 = extended) + const float tr = argc > 2 ? std::atof(argv[2]) : 1.0f; // thumb_rot + const std::string iface = argc > 3 ? argv[3] : "eth0"; + + auto arm = greet_init(iface); + Hand hand; + usleep(1500000); // 1.5s: let DDS match inspire_g1 + the arm service before commanding + + // On these hands the 4 fingers close at q=1 (q=0 = extended, inverted vs SDK doc). + const Vec6 like = gv(1, 1, 1, 1, tb, tr); // 4 fingers curled into a fist, thumb up + + std::cout << "*** LIKE while handshake (arm + hand). Clear space, E-stop ready. ***\n" + << "hand: thumbs-up (thumb_bend=" << tb << " thumb_rot=" << tr << ")" << std::endl; + + // Background thread: keep the 👍 published the whole time. + std::atomic running{true}; + std::thread holder([&] { while (running) { hand.set(like, G_OPEN); ghold(40); } }); + + ghold(1200); // let the 👍 form before the arm starts + + if (arm_do(*arm, 27, "shake hand")) { + ghold(4500); // hold the 👍 while the handshake plays + arm_do(*arm, 99, "release arm"); + ghold(500); + } + + running = false; + holder.join(); + hand.set(G_OPEN, G_OPEN); + std::cout << "done." << std::endl; + return 0; +} diff --git a/example/thumbup.cpp b/example/thumbup.cpp new file mode 100644 index 0000000..bc0dda0 --- /dev/null +++ b/example/thumbup.cpp @@ -0,0 +1,23 @@ +/** + * @file thumbup.cpp + * @brief G1 thumbs-up: arm "right hand up"(23) -> Inspire hand thumbs-up -> release(99). + * + * ./thumbup [networkInterface] (default iface: eth0) + * + * Needs ./inspire_g1 running for the fingers. The ARM physically moves — + * clear space + E-stop ready. + */ +#include "greet_common.hpp" + +int main(int argc, char **argv) +{ + const std::string iface = argc > 1 ? argv[1] : "eth0"; + auto arm = greet_init(iface); + Hand hand; + usleep(300000); // let DDS match the arm service + inspire_g1 + + std::cout << "*** THUMBS-UP (arm + hand). Clear space, E-stop ready. ***" << std::endl; + runThumbup(*arm, hand); + std::cout << "done." << std::endl; + return 0; +} diff --git a/include/SerialPort.h b/include/SerialPort.h index cf7704f..2a41f7e 100644 --- a/include/SerialPort.h +++ b/include/SerialPort.h @@ -13,6 +13,8 @@ #include #include #include +#include // recv_buf below is a std::array; without this, any TU that doesn't + // pull in transitively (e.g. via Eigen) fails to compile. class SerialPort { @@ -41,8 +43,9 @@ public: FD_ZERO(&rSet_); FD_SET(fd_, &rSet_); ssize_t recv_len = 0; + timeval tv = timeout_; // local copy: select() mutates the timeval, keep timeout_ intact - switch (select(fd_ + 1, &rSet_, NULL, NULL, &timeout_)) + switch (select(fd_ + 1, &rSet_, NULL, NULL, &tv)) { case -1: // error // std::cout << "communication error" << std::endl; @@ -64,6 +67,8 @@ public: timeout_.tv_usec = (timeout_ms % 1000) * 1000; } + void flush() { tcflush(fd_, TCIFLUSH); } // discard buffered/echoed input + private: void Init(std::string port, speed_t baudrate) { diff --git a/include/inspire.h b/include/inspire.h index bc40f12..1ce04c9 100644 --- a/include/inspire.h +++ b/include/inspire.h @@ -39,7 +39,17 @@ public: */ int16_t SetPosition(const Eigen::Matrix & q) { - Eigen::Matrix q_int16 = (q * 1000).cast().cwiseMax(0).cwiseMin(1000); + Eigen::Matrix q_int16; + for (int i = 0; i < 6; i++) + { + // 0xFFFF = "no change" (per the docstring above): the hand IGNORES this joint and keeps + // driving it to its previous target. It does NOT release the motor — an earlier comment + // here claimed it did, and a hand-pose recording mode was built on that before the + // operator found the finger still locked. There is no release command on the RH56, and + // the drive is a non-backdrivable worm gear, so no force setting frees it either. + if (q(i) < 0.0) q_int16(i) = -1; + else { double v = q(i) * 1000.0; q_int16(i) = (int16_t)(v < 0 ? 0 : (v > 1000 ? 1000 : v)); } + } uint8_t cmd[20]; cmd[0] = 0xEB; // head @@ -66,7 +76,7 @@ public: cmd[19] = CheckSum(cmd, 20); serial_->send(cmd, 20); - usleep(5000); + usleep(1500); // was 5000 - recv() polls via select(), and reads flush() first serial_->recv(recvBuff, 9); return 0; } @@ -78,23 +88,10 @@ public: */ int16_t GetPosition(Eigen::Matrix & q) { - std::vector cmd = {0xEB, 0x90, id, 0x04, 0x11, 0x0A, 0x06, 0x0C, 0x00}; - cmd.back() = CheckSum(cmd.data(), cmd.size()); - serial_->send(cmd.data(), cmd.size()); - - usleep(5000); - size_t len = serial_->recv(recvBuff, 20); - - if(len != 20) return 1; - if(recvBuff[19] != CheckSum(recvBuff, 20)) return 2; - - q(0) = (recvBuff[7] | (recvBuff[8] << 8)) / 1000.; - q(1) = (recvBuff[9] | (recvBuff[10] << 8)) / 1000.; - q(2) = (recvBuff[11] | (recvBuff[12] << 8)) / 1000.; - q(3) = (recvBuff[13] | (recvBuff[14] << 8)) / 1000.; - q(4) = (recvBuff[15] | (recvBuff[16] << 8)) / 1000.; - q(5) = (recvBuff[17] | (recvBuff[18] << 8)) / 1000.; - + const uint8_t *b = Query12(0x0A); // angleAct + if (!b) return 1; + for (int i = 0; i < 6; i++) + q(i) = (b[7 + 2 * i] | (b[8 + 2 * i] << 8)) / 1000.; return 0; } @@ -128,7 +125,7 @@ public: cmd[19] = CheckSum(cmd, 20); serial_->send(cmd, 20); - usleep(5000); + usleep(1500); // was 5000 - recv() polls via select(), and reads flush() first serial_->recv(recvBuff, 9); } @@ -164,7 +161,7 @@ public: cmd[19] = CheckSum(cmd, 20); serial_->send(cmd, 20); - usleep(5000); + usleep(1500); // was 5000 - recv() polls via select(), and reads flush() first serial_->recv(recvBuff, 9); } @@ -175,30 +172,87 @@ public: */ int16_t GetForce(Eigen::Matrix & f) { - std::vector cmd = {0xEB, 0x90, id, 0x04, 0x11, 0x2E, 0x06, 0x0C, 0x00}; - cmd.back() = CheckSum(cmd.data(), cmd.size()); - serial_->send(cmd.data(), cmd.size()); + // Force sensor register 0x2E. Output = grams, SIGNED. Used for push-to-teach: a hand + // push shows up as a force deviation even though the finger can't back-drive. + const uint8_t *b = Query12(0x2E); + if (!b) return 1; + for (int i = 0; i < 6; i++) + f(i) = (double)(int16_t)(b[7 + 2 * i] | (b[8 + 2 * i] << 8)); + return 0; + } - usleep(5000); - size_t len = serial_->recv(recvBuff, 20); + /** + * @brief Read the per-finger ERROR register (1606). + * + * Bits: 0x01 locked rotor, 0x02 over-temperature, 0x04 over-current, + * 0x08 abnormal operation, 0x10 communication error. + * + * The RH56 LATCHES these: once a finger stalls it refuses every subsequent motion + * command — any target, any force limit, and a release (q<0) does not reset it — until + * CLEAR_ERROR is sent. It keeps reporting a low force and a valid angle throughout, so + * from the outside it just looks like a finger that stopped listening. + */ + int16_t GetError(uint8_t out[6]) + { + const uint8_t *b = QueryReg(1606, 6); + if (!b) return 1; + for (int i = 0; i < 6; i++) out[i] = b[7 + i]; + return 0; + } - if(len != 20) return 1; - if(recvBuff[19] != CheckSum(recvBuff, 20)) return 2; + /** + * @brief Per-finger STATUS (1612). 0 unclenching, 1 grasping, 2 reached position, + * 3 reached force, 5 current protection, 6 locked rotor, 7 fault. + */ + int16_t GetStatus(uint8_t out[6]) + { + const uint8_t *b = QueryReg(1612, 6); + if (!b) return 1; + for (int i = 0; i < 6; i++) out[i] = b[7 + i]; + return 0; + } - f(0) = int16_t(recvBuff[7] | (recvBuff[8] << 8)) / 1000. * 9.8; - f(1) = int16_t(recvBuff[9] | (recvBuff[10] << 8)) / 1000. * 9.8; - f(2) = int16_t(recvBuff[11] | (recvBuff[12] << 8)) / 1000. * 9.8; - f(3) = int16_t(recvBuff[13] | (recvBuff[14] << 8)) / 1000. * 9.8; - f(4) = int16_t(recvBuff[15] | (recvBuff[16] << 8)) / 1000. * 9.8; - f(5) = int16_t(recvBuff[17] | (recvBuff[18] << 8)) / 1000. * 9.8; + /// Per-finger motor temperature in degrees C (1618). + int16_t GetTemp(uint8_t out[6]) + { + const uint8_t *b = QueryReg(1618, 6); + if (!b) return 1; + for (int i = 0; i < 6; i++) out[i] = b[7 + i]; + return 0; + } + /** + * @brief Per-finger motor CURRENT in mA (1594, 12 bytes / 6 int16). + * + * The single number that separates "dead" from "force-gated": a finger commanded to move + * that draws 0 mA is not being driven at all, whereas one drawing current against a + * mechanical block is trying and failing. Reading only angle and force cannot tell those + * apart, which is how healthy fingers were once written off as dead actuators. + */ + int16_t GetCurrent(Eigen::Matrix &c) + { + const uint8_t *b = QueryReg(1594, 12); + if (!b) return 1; + for (int i = 0; i < 6; i++) + c(i) = (double)(int16_t)(b[7 + 2 * i] | (b[8 + 2 * i] << 8)); + return 0; + } + + /// Per-finger grip force LIMIT currently in the hand (FORCE_SET 1498). Shown next to the + /// measured force so "force >= limit -> driver refuses to move" is visible, not deduced. + int16_t GetForceSet(Eigen::Matrix &f) + { + const uint8_t *b = QueryReg(1498, 12); + if (!b) return 1; + for (int i = 0; i < 6; i++) + f(i) = (double)(int16_t)(b[7 + 2 * i] | (b[8 + 2 * i] << 8)); return 0; } /** * @brief Clear error - * - * When the Inspire Hand has a fault such as a stall, overcurrent, or abnormality, + * + * When the Inspire Hand has a fault such as a stall, overcurrent, or abnormality, * the fault can be cleared by the clear fault command. */ void ClearError() @@ -238,6 +292,67 @@ public: uint8_t id = 1; private: + /** + * @brief Send a 12-byte read query for `addr` and wait for its reply, POLLING the port + * rather than sleeping a fixed interval. + * + * The previous version slept a flat 8ms per attempt whether or not the reply had already + * landed. With four of these per control cycle (2 hands x angle+force) that alone cost + * ~32ms, which is why the 100Hz RecurrentThread actually published at a measured 28Hz. + * select() inside recv() already returns the instant bytes arrive, so we accumulate until + * a complete, checksum-valid frame is present or the budget expires — typically ~3ms. + * + * Still re-queries on failure: if the hand is briefly busy after a write it ignores a + * single query and a one-shot read returns all-zeros. We only accept a reply whose cmd + * (0x11) and address match, so a stray write-ack or echo can never be mis-parsed as 0. + * + * @return pointer to the 20-byte frame inside recvBuff, or nullptr if it never arrived. + */ + const uint8_t* QueryReg(uint16_t addr, uint8_t nbytes, int budget_us = 12000) + { + uint8_t query[9] = {0xEB, 0x90, id, 0x04, 0x11, + (uint8_t)(addr & 0xFF), (uint8_t)(addr >> 8), nbytes, 0x00}; + query[8] = CheckSum(query, 9); + const size_t frame = 7u + nbytes + 1u; // header+addr + data + checksum + + for (int attempt = 0; attempt < 4; attempt++) + { + serial_->flush(); + serial_->send(query, 9); + + size_t n = 0; + auto t0 = std::chrono::steady_clock::now(); + while (n + 1 < sizeof(recvBuff)) + { + ssize_t got = serial_->recv(recvBuff + n, sizeof(recvBuff) - n); + if (got > 0) + { + n += (size_t)got; + for (size_t off = 0; off + frame <= n; off++) + { + if (!((recvBuff[off] == 0x90 && recvBuff[off + 1] == 0xEB) || + (recvBuff[off] == 0xEB && recvBuff[off + 1] == 0x90))) continue; + if (recvBuff[off + 4] != 0x11) continue; + uint16_t got_addr = recvBuff[off + 5] | (recvBuff[off + 6] << 8); + if (got_addr != addr) continue; + if (recvBuff[off + frame - 1] != CheckSum(recvBuff + off, frame)) continue; + return recvBuff + off; + } + } + auto el = std::chrono::duration_cast( + std::chrono::steady_clock::now() - t0).count(); + if (el >= budget_us) break; + } + } + return nullptr; + } + + /// 12-byte reads (angleAct 0x060A, forceAct 0x062E) keep their short spelling. + const uint8_t* Query12(uint8_t addrLo, int budget_us = 12000) + { + return QueryReg((uint16_t)(0x0600 | addrLo), 12, budget_us); + } + uint8_t CheckSum(const uint8_t* data, uint8_t len) { uint8_t sum = 0; diff --git a/inspire_g1.cpp b/inspire_g1.cpp index 44454c5..8aa0f3d 100644 --- a/inspire_g1.cpp +++ b/inspire_g1.cpp @@ -7,17 +7,172 @@ #include #include +#include +#include +#include +#include +#include +#include +#include + +// One detected CH340 (1a86:7523) USB-RS485 hand adapter. +struct HandPort +{ + std::string dev; // /dev/ttyUSBn — NOT stable across replug/reboot + std::string path; // USB topology path, e.g. "1-2.2.3" — stable per physical socket +}; + +// Find the two CH340 hand adapters and order them by PHYSICAL USB PATH. +// +// This used to sort by ttyUSB name and assign right = ports[1]. Both dongles are +// 1a86:7523 and report the SAME USB serial, so the tty number is the only thing telling +// them apart — and the kernel hands those out in enumeration order, which changes across +// replugs, reboots and hub hiccups. When it flips, left and right silently trade places: +// nothing errors, the dashboard's port check still says "ok", and you drive the wrong hand. +// Observed live on 2026-07-30 — software "L.*" was in fact the robot's RIGHT hand. +// +// The USB topology path IS stable as long as a dongle stays in the same socket, so we +// order by that instead. Override explicitly with INSPIRE_RIGHT_PATH / INSPIRE_LEFT_PATH +// (e.g. INSPIRE_RIGHT_PATH=1-2.2.3) when the sockets are known. +static std::vector findCH340Ports() +{ + std::vector ttys; + if (DIR *d = opendir("/sys/class/tty")) + { + for (struct dirent *e; (e = readdir(d));) + { + std::string n = e->d_name; + if (n.rfind("ttyUSB", 0) == 0) ttys.push_back(n); + } + closedir(d); + } + std::vector ports; + for (const auto &t : ttys) + { + // Resolve the device symlink, then walk UP to the USB device dir (first + // ancestor that has an idVendor file) and check for the CH340 id. + char real[4096]; + std::string link = "/sys/class/tty/" + t + "/device"; + if (!realpath(link.c_str(), real)) continue; + std::string dir = real; + while (dir.size() > 1) + { + std::ifstream vf(dir + "/idVendor"); + if (vf.good()) + { + std::string vid, pid; vf >> vid; + std::ifstream pf(dir + "/idProduct"); pf >> pid; + if (vid == "1a86" && pid == "7523") + { + // The USB device dir's basename is the topology path ("1-2.2.3"). + auto s = dir.find_last_of('/'); + ports.push_back({"/dev/" + t, s == std::string::npos ? dir : dir.substr(s + 1)}); + } + break; + } + auto slash = dir.find_last_of('/'); + if (slash == std::string::npos || slash == 0) break; + dir.resize(slash); + } + } + std::sort(ports.begin(), ports.end(), + [](const HandPort &a, const HandPort &b) { return a.path < b.path; }); + return ports; +} + class InspireRunner { public: + /// One opened port plus the RS-485 id that actually answered on it (-1 = nothing answered). + struct Opened + { + std::shared_ptr hand; + SerialPort::SharedPtr port; + int id = -1; + }; + InspireRunner() { - serial1 = std::make_shared("/dev/ttyUSB1", B115200); - serial2 = std::make_shared("/dev/ttyUSB2", B115200); + // Dynamically detect the two CH340 hand adapters (port-independent — survives + // being moved to a hub / different USB ports). A generic udev rule grants 0666. + auto ports = findCH340Ports(); + if (ports.size() < 2) + { + std::cerr << "ERROR: found " << ports.size() + << " CH340 hand adapter(s), need 2. Plug in both USB-RS485 dongles." + << std::endl; + exit(1); + } + // --- Work out which physical hand is on which port ----------------------------------- + // + // The RH56 has NO handedness register — it cannot tell you it is a left or a right unit + // (checked: only HAND_ID 1000 exists, which is just the RS-485 slave address). Both + // dongles are CH340 1a86:7523 with the same USB serial too, so there is nothing + // intrinsic to key on. We therefore try three things, best first: + // + // 1. RS-485 ID. If the hands have been given DIFFERENT ids (see example/hand_setid), + // that is a permanent, wiring-independent identity: id 1 = RIGHT, id 2 = LEFT. + // This is the real fix — immune to enumeration order, replugs and hub changes. + // 2. INSPIRE_RIGHT_PATH / INSPIRE_LEFT_PATH env — pin by physical USB socket. + // 3. USB topology path order — stable per socket, unlike the ttyUSB number that this + // used to sort by (which silently swapped left and right; observed 2026-07-30). + Opened A = openAndIdentify(ports[0].dev, ports[0].path.c_str()); + Opened B = openAndIdentify(ports[1].dev, ports[1].path.c_str()); - // If your left and right hand controls are reversed, you can swap the positions of `serial1` and `serial2` below. - righthand = std::make_shared(serial1, 1); - lefthand = std::make_shared(serial2, 1); + int ri = 1, li = 0; // default: USB-path order (ports[1] = right) + const char *how = "usb path order"; + + if (A.id > 0 && B.id > 0 && A.id != B.id) + { + // Distinct ids => unambiguous identity, whatever the ports did. + ri = (A.id == 1) ? 0 : 1; + li = 1 - ri; + how = "RS-485 id (id1=right, id2=left)"; + } + else if (std::getenv("INSPIRE_RIGHT_PATH") || std::getenv("INSPIRE_LEFT_PATH")) + { + if (const char *rp = std::getenv("INSPIRE_RIGHT_PATH")) + for (size_t i = 0; i < ports.size(); i++) + if (ports[i].path == rp) { ri = (int)i; li = (int)(1 - i); } + if (const char *lp = std::getenv("INSPIRE_LEFT_PATH")) + for (size_t i = 0; i < ports.size(); i++) + if (ports[i].path == lp) { li = (int)i; ri = (int)(1 - i); } + how = "INSPIRE_*_PATH env"; + } + else if (std::getenv("INSPIRE_SWAP")) + { + ri = 0; li = 1; + how = "usb path order + INSPIRE_SWAP"; + } + + Opened &R = (ri == 0) ? A : B; + Opened &L = (li == 0) ? A : B; + serial1 = R.port; righthand = R.hand; + serial2 = L.port; lefthand = L.hand; + + std::cout << "Inspire hands identified by " << how << ":\n" + << " RIGHT = " << ports[ri].dev << " (usb " << ports[ri].path + << ", rs485 id " << R.id << ")\n" + << " LEFT = " << ports[li].dev << " (usb " << ports[li].path + << ", rs485 id " << L.id << ")" << std::endl; + if (!(A.id > 0 && B.id > 0 && A.id != B.id)) + std::cout << " NOTE: both hands answer on the same RS-485 id, so identity is inferred\n" + " from wiring. To make it self-identifying and permanent, run once:\n" + " ./build/hand_setid 1 2\n" + " (gives the LEFT hand id 2; then left/right can never swap again)" + << std::endl; + + // Recover the actuators on startup: clear any latched fault (stall/overcurrent) and + // ensure a non-zero speed/force (e.g. after the bad serial state from two services + // fighting over the bus, which can leave fingers unresponsive). + for (auto h : {righthand, lefthand}) + { + h->ClearError(); + h->SetVelocity(1000, 1000, 1000, 1000, 1000, 1000); + h->SetForce(500, 500, 500, 500, 500, 500); + } + + calibrateForceOffset(); // dds handcmd = std::make_shared>( @@ -27,59 +182,427 @@ public: "rt/" + param::ns + "/state"); handstate->msg_.states().resize(12); - // Start running + // Start running. + // 20ms, not 10ms: a cycle is 2 position writes + 4 register reads over a 115200 half- + // duplex bus, which cannot finish in 10ms. Asking for 100Hz just made the thread run + // back-to-back with no idle, and the measured publish rate was 28Hz. 50Hz is honest and + // achievable, and leaves headroom so a retry doesn't push the whole loop late. thread = std::make_shared( - 10000, std::bind(&InspireRunner::run, this) + 20000, std::bind(&InspireRunner::run, this) ); } + /** + * @brief Learn each finger's resting force, and raise that finger's force limit past it. + * + * The RH56 stops driving a finger the instant its MEASURED force reaches the force limit. + * So a finger whose force-sensor zero has drifted above the limit can never be driven: it + * draws 0 current and reads exactly like a dead motor. On this G1's right hand, R.mid rests + * at ~856g and R.index at ~570g against a 500g limit — both were mis-diagnosed as dead + * actuators until they ran their full range once the limit was raised to 1000g. + * + * We measure the offset with the hand open and unloaded, then (a) subtract it from the + * published force so touch-detection works from a true zero, and (b) add it to that + * finger's limit so the gate still trips at the same REAL force. + */ + void calibrateForceOffset() + { + std::cout << "Calibrating finger force baseline (hand open, unloaded) ..." << std::endl; + Eigen::Matrix open6; + open6.setOnes(); + for (int k = 0; k < 10; k++) + { + righthand->SetPosition(open6); + lefthand->SetPosition(open6); + usleep(30000); + } + + Eigen::Matrix acc; + acc.setZero(); + int n = 0; + Eigen::Matrix ftmp; + for (int k = 0; k < 12; k++) + { + bool ok = true; + Eigen::Matrix one; + if (righthand->GetForce(ftmp) == 0) one.block<6, 1>(0, 0) = ftmp; else ok = false; + if (lefthand->GetForce(ftmp) == 0) one.block<6, 1>(6, 0) = ftmp; else ok = false; + if (ok) { acc += one; n++; } + usleep(20000); + } + if (!n) + { + std::cerr << " WARNING: no force readings - skipping offset calibration. " + "Fingers with a drifted sensor zero will not move." << std::endl; + foff.setZero(); + applyForce(reqForceR, 0); + applyForce(reqForceL, 6); + return; + } + foff = acc / n; + + static const char *FN[6] = {"pinky", "ring", "mid", "index", "thumbB", "thumbR"}; + for (int lo = 0; lo <= 6; lo += 6) + { + double req = (lo == 0) ? reqForceR : reqForceL; + bool flagged = false; + for (int i = lo; i < lo + 6; i++) + { + if (std::abs(foff(i)) > req - kForceMargin) + { + if (!flagged) + { + std::cout << " " << (lo == 0 ? "RIGHT" : "LEFT") + << " hand force-sensor ZERO HAS DRIFTED:" << std::endl; + flagged = true; + } + std::cout << " " << FN[i - lo] << " rests at " << (int)foff(i) + << "g vs a " << (int)req << "g limit -> would read as DEAD (0 current). " + << "Raising its limit." << std::endl; + } + } + if (flagged) + std::cout << " (proper fix: re-zero the force sensor with the hand unloaded)" + << std::endl; + applyForce(req, lo); + } + } + + /** + * @brief Open a hand's serial port and confirm it actually ANSWERS, reopening if it doesn't. + * + * Measured repeatedly on this robot: after a (re)start one of the two RS-485 buses comes up + * mute — every read for that hand fails for the whole session while the other is perfect, + * and WHICH one is random (5 restarts: R dead, L dead, both ok, R dead, L dead). The port + * always enumerates and open() always succeeds; the adapter just never replies, so nothing + * upstream notices. Closing and reopening clears it. + * + * Probing here costs a few hundred ms at startup and turns "half the robot is silently dead + * until someone notices" into "it retried and told you". + */ + /// SerialPort::Init calls exit(-1) if open() fails, so never hand it a path that is not + /// currently openable — that turns a transient into a service-wide crash. + static bool canOpen(const std::string &dev) + { + int fd = ::open(dev.c_str(), O_RDWR | O_NOCTTY); + if (fd < 0) return false; + ::close(fd); + return true; + } + + /// Re-resolve a hand's tty from its (stable) USB path. Reopening a CH340 repeatedly can + /// make the adapters re-enumerate, so a device name captured at startup can go stale + /// mid-probe — which is exactly how the earlier probe crash-looped the service. + static std::string resolveByPath(const std::string &usbpath, const std::string &fallback) + { + for (const auto &p : findCH340Ports()) + if (p.path == usbpath) return p.dev; + return fallback; + } + + Opened openAndIdentify(const std::string &dev_in, const char *usbpath) + { + Opened o; + std::string dev = dev_in; + // Two attempts, not four: each reopen churns the USB bus and can renumber BOTH adapters. + // One retry recovers a genuinely stalled link; more just destabilises the other hand. + for (int attempt = 1; attempt <= 2; attempt++) + { + dev = resolveByPath(usbpath, dev); + if (!canOpen(dev)) + { + std::cerr << " " << dev << " (usb " << usbpath << ") cannot be opened right now" + << std::endl; + usleep(300000); + dev = resolveByPath(usbpath, dev); + if (!canOpen(dev)) + { + // Everything downstream dereferences righthand/lefthand unconditionally, so a + // null hand would segfault. Exit clearly instead and let the supervisor retry — + // this self-heals as soon as the adapter is back. + std::cerr << "FATAL: usb " << usbpath << " has no openable tty (" << dev << "). " + "Adapter unplugged or re-enumerating. Exiting; supervisor will retry." + << std::endl; + exit(1); + } + } + o.port = std::make_shared(dev, B115200, 6); + usleep(120000); // let the CH340 settle after open + + // Scan the RS-485 ids. Normally only id 1 is used, but giving the two hands distinct + // ids makes them self-identifying, so look for any of them. + for (int id = 1; id <= 4 && !o.hand; id++) + { + auto h = std::make_shared(o.port, (uint8_t)id); + Eigen::Matrix q; + for (int t = 0; t < 2; t++) + if (h->GetPosition(q) == 0) { o.hand = h; o.id = id; break; } + } + if (o.hand) + { + std::cout << " " << dev << " (usb " << usbpath << ") answered at rs485 id " << o.id + << (attempt > 1 ? " [after reopen #" + std::to_string(attempt) + "]" : "") + << std::endl; + return o; + } + std::cerr << " " << dev << " (usb " << usbpath << ") is SILENT - reopening (" + << attempt << "/2)" << std::endl; + o.port.reset(); // close before retrying + usleep(250000); // brief release; longer churns the bus + } + // Give the caller a usable object regardless, so one dead hand does not take the other + // down. The runtime watchdog keeps retrying and the warning fires once. + dev = resolveByPath(usbpath, dev); + if (!canOpen(dev)) + { + std::cerr << "FATAL: usb " << usbpath << " has no openable tty (" << dev << ") after " + "probing. Exiting; supervisor will retry." << std::endl; + exit(1); + } + o.port = std::make_shared(dev, B115200, 6); + o.hand = std::make_shared(o.port, 1); + o.id = -1; + std::cerr << " WARNING: " << dev << " (usb " << usbpath << ") never answered. Check its " + "RS-485 cable/power. Continuing so the other hand still works." << std::endl; + return o; + } + + /** + * @brief Refresh the diagnostic registers for both hands. + * + * These are what turn "the finger will not move" from a guess into an answer: + * ERROR - locked rotor / over-temp / over-current, LATCHED until CLEAR_ERROR + * STATUS - what the driver thinks it is doing + * CURRENT- 0mA means it never energised; >0 means it is trying and blocked + * FORCE_SET - the limit the measured force is being compared against + */ + void refreshDiag() + { + uint8_t e[6], st[6], tp[6]; + Eigen::Matrix cu, fs; + for (int lo = 0; lo <= 6; lo += 6) + { + auto h = (lo == 0) ? righthand : lefthand; + if (h->GetError(e) == 0) for (int i = 0; i < 6; i++) dErr[lo + i] = e[i]; + if (h->GetStatus(st)== 0) for (int i = 0; i < 6; i++) dSta[lo + i] = st[i]; + if (h->GetTemp(tp) == 0) for (int i = 0; i < 6; i++) dTmp[lo + i] = tp[i]; + if (h->GetCurrent(cu) == 0) dCur.block<6,1>(lo,0) = cu; + if (h->GetForceSet(fs) == 0) dFset.block<6,1>(lo,0) = fs; + } + } + + /// Poll both hands' ERROR registers and auto-clear any latched fault. + void checkFaults() + { + static const char *FN[6] = {"pinky", "ring", "mid", "index", "thumbB", "thumbR"}; + for (int lo = 0; lo <= 6; lo += 6) + { + auto h = (lo == 0) ? righthand : lefthand; + uint8_t err[6] = {0}; + if (h->GetError(err) != 0) continue; // read failed; the link watchdog covers that + + std::string hit; + for (int i = 0; i < 6; i++) + { + if (!err[i]) continue; + hit += std::string(FN[i]) + "(0x"; + const char *hex = "0123456789ABCDEF"; + hit += hex[(err[i] >> 4) & 0xF]; + hit += hex[err[i] & 0xF]; + if (err[i] & 0x01) hit += " locked-rotor"; + if (err[i] & 0x02) hit += " over-temp"; + if (err[i] & 0x04) hit += " over-current"; + if (err[i] & 0x08) hit += " abnormal"; + if (err[i] & 0x10) hit += " comms"; + hit += ") "; + } + if (hit.empty()) continue; + + faultClears[lo == 0 ? 0 : 1]++; + std::cerr << "FAULT on " << (lo == 0 ? "RIGHT" : "LEFT") << " hand: " << hit + << "-> sending CLEAR_ERROR (clear #" << faultClears[lo == 0 ? 0 : 1] << ")" + << std::endl; + h->ClearError(); + // A stall also drops the speed/force config on some units; restore both so the finger + // actually moves again instead of silently staying at zero speed. + h->SetVelocity(1000, 1000, 1000, 1000, 1000, 1000); + applyForce(lo == 0 ? reqForceR : reqForceL, lo); + } + } + + /// Per-finger force limit = requested + that finger's sensor offset, capped at the 1000g max. + void applyForce(double req, int lo) + { + uint16_t f[6]; + for (int i = 0; i < 6; i++) + { + double v = req + std::abs(foff(lo + i)); + f[i] = (uint16_t)(v > kForceMax ? kForceMax : (v < 0 ? 0 : v)); + } + auto h = (lo == 0) ? righthand : lefthand; + h->SetForce(f[0], f[1], f[2], f[3], f[4], f[5]); + } + void run() { - // Set command + // Set command (write BOTH hands first) if(!handcmd->isTimeout()) { for(int i(0); i<12; i++) { qcmd(i) = handcmd->msg_.cmds()[i].q(); } + // Optional grip-force override via the cmd's kp field (<=0 = keep default). + // Lets the teacher drop to a low, back-drivable force so the fingers can be + // moved by hand for recording, then restore the strong default afterwards. + // Only re-sent when the requested value changes (avoids serial spam). + // Applied through applyForce() so each finger's sensor offset is still added on top + // — otherwise an override would re-gate a drifted finger back to "dead". + double rf = handcmd->msg_.cmds()[0].kp(); + double lf = handcmd->msg_.cmds()[6].kp(); + if(rf > 0 && rf != lastForceR) + { reqForceR = rf; applyForce(rf, 0); lastForceR = rf; } + if(lf > 0 && lf != lastForceL) + { reqForceL = lf; applyForce(lf, 6); lastForceL = lf; } + righthand->SetPosition(qcmd.block<6, 1>(0, 0)); lefthand->SetPosition(qcmd.block<6, 1>(6, 0)); } - // Recv state + // Recv state. GetPosition() is self-retrying: it flushes stale RX and re-sends the + // angleAct query, recovering when the hand is briefly busy after a write (a single + // one-shot query gets ignored and reads back all-zeros — worst inside Docker / a tight + // loop). A short settle after the writes still helps the very first query land. Eigen::Matrix qtemp; + usleep(1500); // settle after the writes (was 3000) + if(righthand->GetPosition(qtemp) == 0) { qstate.block<6, 1>(0, 0) = qtemp; + posFailR = 0; + everOkR = true; } else { + posFailR++; for(int i(0); i<6; i++) { handstate->msg_.states()[i].lost()++; } - // spdlog::debug("Failed to get right hand state"); } + if(lefthand->GetPosition(qtemp) == 0) { qstate.block<6, 1>(6, 0) = qtemp; + posFailL = 0; + everOkL = true; } else { + posFailL++; for(int i(0); i<6; i++) { handstate->msg_.states()[i+6].lost()++; } - // spdlog::debug("Failed to get left hand state"); } + + // Force (grams, signed) — published as tau_est. A push on a finger registers + // here even though the non-backdrivable finger can't move; used for + // push-to-teach. Reuses the robust re-query reader. + // + // A FAILED read used to silently keep the previous value and republish it as if it were + // fresh, with no counter (positions had one, force did not). A frozen force channel is + // indistinguishable from a genuinely still finger, so push-to-teach would quietly stop + // detecting touches and nothing would say so. Now failures bump lost() and get logged. + Eigen::Matrix ftemp; + bool fr = (righthand->GetForce(ftemp) == 0); + if(fr) fstate.block<6, 1>(0, 0) = ftemp; + bool fl = (lefthand->GetForce(ftemp) == 0); + if(fl) fstate.block<6, 1>(6, 0) = ftemp; + + if(!fr) { forceFailR++; for(int i(0); i<6; i++) handstate->msg_.states()[i].lost()++; } + else { forceFailR = 0; everOkR = true; } + if(!fl) { forceFailL++; for(int i(6); i<12; i++) handstate->msg_.states()[i].lost()++; } + else { forceFailL = 0; everOkL = true; } + + if(forceFailR == kStaleWarn) + std::cerr << "WARNING: right hand force reads failing - published force is STALE " + "(push-to-teach will not detect touches)" << std::endl; + if(forceFailL == kStaleWarn) + std::cerr << "WARNING: left hand force reads failing - published force is STALE " + "(push-to-teach will not detect touches)" << std::endl; + + // A hand whose reads all fail is either (a) a stale link that a reopen would fix, or + // (b) physically gone — unplugged, unpowered, or not answering on RS-485. + // + // Only (a) is worth exiting for. Exiting on (b) would boot-loop forever: the supervisor + // restarts us every 3s, the hand is still absent, we exit again. So restart ONLY if this + // hand was working earlier in this process — that is the signature of a link that died + // and might come back on a reopen. If it has never answered since startup, stay up and + // keep serving the healthy hand, loudly and once. + bool deadR = (forceFailR > kDeadCycles) && (posFailR > kDeadCycles); + bool deadL = (forceFailL > kDeadCycles) && (posFailL > kDeadCycles); + if((deadR && everOkR) || (deadL && everOkL)) + { + std::cerr << "FATAL: " << ((deadR && everOkR) ? "right" : "left") + << " hand serial link DIED after working (" << kDeadCycles + << " consecutive failed reads). Exiting so the supervisor can restart and " + "reopen/re-detect the adapters." << std::endl; + exit(1); + } + if(deadR && !everOkR && !warnedNeverR) + { warnedNeverR = true; + std::cerr << "WARNING: RIGHT hand has NEVER answered since startup - check its RS-485 " + "cable/power. Continuing with the left hand only." << std::endl; } + if(deadL && !everOkL && !warnedNeverL) + { warnedNeverL = true; + std::cerr << "WARNING: LEFT hand has NEVER answered since startup - check its RS-485 " + "cable/power. Continuing with the right hand only." << std::endl; } + if(handstate->trylock()) { for(int i(0); i<12; i++) { handstate->msg_.states()[i].q() = qstate(i); + // Piggyback the diagnostics on fields this hand never populates, so no IDL + // change is needed: dq=current(mA), ddq=force limit, temperature=degC, + // reserve[0]=ERROR bits, reserve[1]=STATUS. + handstate->msg_.states()[i].dq() = dCur(i); + handstate->msg_.states()[i].ddq() = dFset(i); + handstate->msg_.states()[i].temperature() = dTmp[i]; + handstate->msg_.states()[i].reserve()[0] = dErr[i]; + handstate->msg_.states()[i].reserve()[1] = dSta[i]; + // Subtract the calibrated resting offset so a drifted sensor zero doesn't look + // like a permanent 850g press, and touch deltas start from a true zero. + handstate->msg_.states()[i].tau_est() = fstate(i) - foff(i); } handstate->unlockAndPublish(); } + + // Fault watchdog. ClearError() used to run ONLY in the constructor, so a finger that + // stalled mid-session stayed latched and dead until the whole service was restarted — + // it ignored every target at every force limit, and a release did not reset it, while + // still reporting a plausible angle and a low force. Poll the ERROR register ~1x/s and + // clear it automatically, naming the finger so a repeat offender is visible. + if(cycles % 50 == 0) + { + refreshDiag(); + checkFaults(); + } + + // Report the rate we ACTUALLY achieve, so the gap between the requested period and the + // serial bus's real throughput is visible instead of assumed. + if(++cycles % 250 == 0) + { + auto now = std::chrono::steady_clock::now(); + double dt = std::chrono::duration(now - lastReport).count(); + if(dt > 0) + { + pubHz = 250.0 / dt; + std::cout << "[inspire_g1] " << pubHz << " Hz" << std::endl; + } + lastReport = now; + } } unitree::common::ThreadPtr thread; @@ -89,7 +612,39 @@ public: SerialPort::SharedPtr serial2; std::shared_ptr lefthand; std::shared_ptr righthand; - Eigen::Matrix qcmd, qstate; + Eigen::Matrix qcmd, qstate, fstate; + double lastForceR = -1, lastForceL = -1; // last grip force applied per hand (via cmd kp) + + // Force-sensor zero offset per finger, measured at startup with the hand open+unloaded. + // Subtracted from published force, and added to each finger's force limit. + Eigen::Matrix foff = Eigen::Matrix::Zero(); + double reqForceR = 500.0, reqForceL = 500.0; // grip force asked for, before offset + static constexpr double kForceMax = 1000.0; // RH56 hardware max + static constexpr double kForceMargin = 250.0; // headroom kept above a drifted zero + + // Read-failure tracking. Consecutive failures per hand; force used to have no counter at + // all, so a dead force channel republished stale values indefinitely and looked healthy. + int forceFailR = 0, forceFailL = 0, posFailR = 0, posFailL = 0; + static constexpr int kStaleWarn = 25; // ~0.5s at 50Hz -> warn once + static constexpr int kDeadCycles = 150; // ~3s of total silence -> link is gone, restart + + // Has this hand EVER answered since this process started? Distinguishes a link that died + // (worth exiting for, a reopen may fix it) from a hand that was never there (exiting would + // just boot-loop every 3s). Set on any successful read. + bool everOkR = false, everOkL = false; + bool warnedNeverR = false, warnedNeverL = false; // warn once, not every cycle + + // Slow-poll diagnostics: ERROR/STATUS/TEMP/CURRENT/FORCE_SET, refreshed ~1x/s and + // published in MotorState_ fields this hand does not otherwise use. Costs one extra + // register read per second per hand, not per cycle. + uint8_t dErr[12] = {0}, dSta[12] = {0}, dTmp[12] = {0}; + Eigen::Matrix dCur = Eigen::Matrix::Zero(); + Eigen::Matrix dFset = Eigen::Matrix::Zero(); + double pubHz = 0.0; + + long cycles = 0; + long faultClears[2] = {0, 0}; // per hand; a climbing count = something keeps stalling + std::chrono::steady_clock::time_point lastReport = std::chrono::steady_clock::now(); // dds std::unique_ptr> handstate; diff --git a/manage.sh b/manage.sh new file mode 100755 index 0000000..2827500 --- /dev/null +++ b/manage.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env bash +# ============================================================================= +# manage.sh — test & manage the Inspire RH56 hands + G1 arm gestures (on PC2) +# +# ./manage.sh [args] +# +# Service / status: +# status show service, devices, adapters (default if no command) +# devices list /dev/inspire_* + ttyUSB + CH340 adapters +# build (re)build all binaries via build.sh +# start start ONE inspire_g1 (detached); refuses if already running +# stop stop inspire_g1 +# restart stop + start +# logs tail the inspire_g1 log +# +# Hand only (needs the service; auto-starts it): +# example Unitree open/close test (hand_example) +# menu interactive finger menu (hand_gestures) +# open|close|box a finger pose (hand_gestures ) +# pose <6 nums> one-shot right-hand pose (hand_pose), e.g. pose 1 1 1 1 1 1 +# record sculpt & SAVE named hand shapes (hand_record; save/load live) +# poses list saved shapes +# play command a saved shape onto the hands +# dashboard start the web slider dashboard (http://:8088) +# dashboard-stop stop the dashboard +# +# Arm + hand fused (ARM MOVES — clear space, E-stop ready; needs the service): +# like thumbs-up "like" held during the built-in handshake +# handshake reach -> grip -> thumbs-up -> release +# thumbup raise right hand -> thumbs-up -> release +# +# Arm only (built-in actions): +# arm run a built-in action (e.g. arm "shake hand", arm 99) +# armlist list built-in actions +# +# Env: IFACE (default eth0) +# ============================================================================= +set -uo pipefail + +HERE="$(cd "$(dirname "$0")" && pwd)" +BUILD="$HERE/build" +IFACE="${IFACE:-eth0}" +LOG="/tmp/inspire_g1.log" +SVC="$BUILD/inspire_g1" + +c_red=$'\e[31m'; c_grn=$'\e[32m'; c_yel=$'\e[33m'; c_rst=$'\e[0m' +ok(){ echo "${c_grn}$*${c_rst}"; } +warn(){ echo "${c_yel}$*${c_rst}"; } +err(){ echo "${c_red}$*${c_rst}" >&2; } + +svc_count(){ local n; n="$(pgrep -xc inspire_g1 2>/dev/null)"; echo "${n:-0}"; } +svc_running(){ [ "$(svc_count)" -ge 1 ]; } +have_devices(){ [ -e /dev/ttyUSB0 ] && [ -e /dev/ttyUSB1 ]; } +writable(){ [ -w /dev/ttyUSB0 ] && [ -w /dev/ttyUSB1 ]; } +need_build(){ [ -x "$SVC" ] || { err "binaries missing — run: ./manage.sh build"; exit 1; }; } + +show_devices(){ + echo "Hand serial (ttyUSB0=right, ttyUSB1=left):" + if have_devices; then + ls -l /dev/ttyUSB0 /dev/ttyUSB1 2>&1 | sed 's/^/ /' + writable || warn " NOT writable — run the udev rule (see SETUP_G1.md §2) or sudo chmod 666." + else warn " /dev/ttyUSB0+1 MISSING — plug in the two CH340 hand adapters."; fi + echo "CH340 adapters (lsusb):" + lsusb | grep -i "1a86" | sed 's/^/ /' || warn " none detected" +} + +cmd_status(){ + local n; n="$(svc_count)" + if [ "$n" -eq 0 ]; then warn "inspire_g1: NOT running" + elif [ "$n" -eq 1 ]; then ok "inspire_g1: running ($(pgrep -x inspire_g1))" + else err "inspire_g1: $n INSTANCES running — duplicates corrupt the serial bus! run: ./manage.sh restart"; fi + show_devices +} + +cmd_wait(){ + echo "Waiting for hand adapters — plug in the two USB-RS485 dongles now (Ctrl+C to stop)..." + while ! have_devices; do + printf "\r CH340 on bus: %s ttyUSB0:%s ttyUSB1:%s " \ + "$(lsusb | grep -ci 1a86)" \ + "$([ -e /dev/ttyUSB0 ] && echo ✓ || echo ✗)" \ + "$([ -e /dev/ttyUSB1 ] && echo ✓ || echo ✗)" + sleep 1 + done + echo; ok "Adapters detected!"; show_devices +} + +cmd_stop(){ pkill -x inspire_g1 2>/dev/null; sleep 1; pkill -9 -x inspire_g1 2>/dev/null; ok "stopped"; } + +cmd_start(){ + need_build + if svc_running; then warn "already running ($(svc_count) instance(s)); use restart to reset"; return 0; fi + have_devices || warn "WARNING: /dev/inspire_* missing — fingers won't move until adapters are plugged in." + ( cd "$BUILD" && setsid nohup "$SVC" >"$LOG" 2>&1 /dev/null; pkill -f "hand_web.py" 2>/dev/null; sleep 1 + setsid "$BUILD/hand_bridge" "$IFACE" 7799 >/tmp/hand_bridge.log 2>&1 /tmp/hand_web.log 2>&1 /dev/null && echo " bridge: up" || { echo " bridge FAILED:"; sed "s/^/ /" /tmp/hand_bridge.log; } + grep -q "Running on" /tmp/hand_web.log && ok ">> Dashboard: http://$ip:8088 (also the wifi IP:8088)" || { echo " web FAILED:"; sed "s/^/ /" /tmp/hand_web.log | tail -4; } + exit 0 ;; + dashboard-stop) pkill -x hand_bridge 2>/dev/null; pkill -f "hand_web.py" 2>/dev/null; ok "dashboard stopped" ;; + like) fused like "$@" ;; + handshake) fused handshake ;; + thumbup) fused thumbup ;; + arm) need_build; "$BUILD/arm_action" "$@" "$IFACE" ;; + armlist) need_build; "$BUILD/arm_action" list "$IFACE" ;; + help|-h|--help) sed -n '2,40p' "$0" | sed 's/^# \{0,1\}//' ;; + *) err "unknown command: $cmd"; echo "run: ./manage.sh help"; exit 1 ;; +esac diff --git a/recorder/__pycache__/g1_record_replay.cpython-313.pyc b/recorder/__pycache__/g1_record_replay.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..02444206d8ba292460191435227eca37c1e5131c GIT binary patch literal 66749 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 beq5u{C5#M(wV5C4On=mfg+Q0ZhSUE8%tc1J literal 0 HcmV?d00001 diff --git a/recorder/arm_home.jsonl b/recorder/arm_home.jsonl new file mode 100644 index 0000000..a7b1b62 --- /dev/null +++ b/recorder/arm_home.jsonl @@ -0,0 +1,482 @@ +{"meta": {"hz": 60.0, "motors": 29}} +{"t": 0.0, "q": [-0.3130820691585541, -0.01388593576848507, 0.015708694234490395, 0.6304575800895691, -0.326513409614563, 0.021098745986819267, -0.3218769133090973, 0.030117155984044075, -0.014771261252462864, 0.6223444938659668, -0.3365834653377533, -0.021014997735619545, 0.002691771136596799, 0.006411683280020952, -0.0298378374427557, 0.29042571783065796, 0.2159797102212906, -0.005213138181716204, 0.9791951179504395, 0.15760454535484314, 0.0616828091442585, -0.040890175849199295, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.1835743635892868, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.0167, "q": [-0.3130394518375397, -0.01388593576848507, 0.015722084790468216, 0.6305854320526123, -0.3265092968940735, 0.02109152264893055, -0.3218769133090973, 0.030117155984044075, -0.01478465273976326, 0.6224552989006042, -0.3365916609764099, -0.021000416949391365, 0.002691771136596799, 0.006411683280020952, -0.0298378374427557, 0.2904137372970581, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.061706773936748505, -0.040890175849199295, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05240701511502266, 0.008844357915222645]} +{"t": 0.0336, "q": [-0.31300538778305054, -0.01388593576848507, 0.015708694234490395, 0.6306195259094238, -0.3265010416507721, 0.021077075973153114, -0.32190245389938354, 0.030100110918283463, -0.01478465273976326, 0.6225661039352417, -0.3365834653377533, -0.02100047469139099, 0.002691771136596799, 0.006464851088821888, -0.029813440516591072, 0.2904376983642578, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.29502764344215393, -0.2237934172153473, 0.02310558594763279, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.0503, "q": [-0.3129883408546448, -0.013868890702724457, 0.015695301815867424, 0.6306791305541992, -0.3265010416507721, 0.021077075973153114, -0.32190245389938354, 0.03009158931672573, -0.014771261252462864, 0.6225916743278503, -0.3365834653377533, -0.02100047469139099, 0.002691771136596799, 0.006480046082288027, -0.029823286458849907, 0.2904616594314575, 0.2159797102212906, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.1835743635892868, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.067, "q": [-0.3129883408546448, -0.013868890702724457, 0.015681909397244453, 0.6306791305541992, -0.3265092968940735, 0.02109152264893055, -0.32190245389938354, 0.03009158931672573, -0.014771261252462864, 0.622617244720459, -0.33660396933555603, -0.020978543907403946, 0.002731946762651205, 0.006480061449110508, -0.029862530529499054, 0.2904616594314575, 0.2159557342529297, -0.005201153922826052, 0.9791951179504395, 0.15758058428764343, 0.06167082488536835, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.0838, "q": [-0.3129883408546448, -0.013868890702724457, 0.015681909397244453, 0.6307047009468079, -0.3265092968940735, 0.02109152264893055, -0.3219280242919922, 0.030066022649407387, -0.014731084927916527, 0.6226428151130676, -0.33661219477653503, -0.020963944494724274, 0.0026783791836351156, 0.00641929917037487, -0.029891815036535263, 0.2904856503009796, 0.21596772968769073, -0.005201153922826052, 0.9791951179504395, 0.15758058428764343, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.02310558594763279, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.1005, "q": [-0.31299686431884766, -0.013868890702724457, 0.01565512642264366, 0.6307217478752136, -0.3265175223350525, 0.021076949313282967, -0.32195359468460083, 0.030066022649407387, -0.014757868833839893, 0.6226428151130676, -0.33660808205604553, -0.02095671370625496, 0.002718554809689522, 0.00648766802623868, -0.02988707646727562, 0.2905096113681793, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.29507559537887573, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 0.1172, "q": [-0.3130139112472534, -0.013843324035406113, 0.015601558610796928, 0.6307047009468079, -0.326513409614563, 0.021098745986819267, -0.32194507122039795, 0.030057501047849655, -0.014757868833839893, 0.6226342916488647, -0.33662450313568115, -0.02097111940383911, 0.0026783791836351156, 0.006510468199849129, -0.0299018993973732, 0.2905455529689789, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.040890175849199295, 0.29507559537887573, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008856342174112797]} +{"t": 0.134, "q": [-0.31304797530174255, -0.013851847499608994, 0.015588166192173958, 0.6307047009468079, -0.326521635055542, 0.021084172651171684, -0.32203882932662964, 0.030048979446291924, -0.014757868833839893, 0.6226513385772705, -0.33662450313568115, -0.02097111940383911, 0.0027051628567278385, 0.00654088007286191, -0.02994132786989212, 0.2905455529689789, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.2950875759124756, -0.2237934172153473, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008832373656332493]} +{"t": 0.1507, "q": [-0.31309911608695984, -0.013843324035406113, 0.015588166192173958, 0.6307047009468079, -0.326521635055542, 0.021084172651171684, -0.3220984637737274, 0.030040455982089043, -0.014731084927916527, 0.6226428151130676, -0.3366450071334839, -0.020963728427886963, 0.0026382035575807095, 0.006464953999966383, -0.03002951480448246, 0.2905455529689789, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29509955644607544, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18353840708732605, 0.05241899937391281, 0.008868326433002949]} +{"t": 0.1676, "q": [-0.3131246864795685, -0.013851847499608994, 0.015574774704873562, 0.6307132244110107, -0.326521635055542, 0.021084172651171684, -0.32217517495155334, 0.03003193438053131, -0.01470430102199316, 0.6226428151130676, -0.336649090051651, -0.020941896364092827, 0.0027989062946289778, 0.006419417914003134, -0.030117813497781754, 0.2905455529689789, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.04090216010808945, 0.2951115369796753, -0.22380541265010834, 0.023093601688742638, 0.9781045317649841, -0.1835503876209259, 0.05241899937391281, 0.008832373656332493]} +{"t": 0.1843, "q": [-0.3132099211215973, -0.013851847499608994, 0.015574774704873562, 0.6307132244110107, -0.326521635055542, 0.021098682656884193, -0.32222631573677063, 0.03003193438053131, -0.01471769344061613, 0.6226428151130676, -0.3366532027721405, -0.020934605970978737, 0.0027721223887056112, 0.006389044225215912, -0.030147191137075424, 0.2905455529689789, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.0409141443669796, 0.2950875759124756, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 0.201, "q": [-0.3132610321044922, -0.013851847499608994, 0.015574774704873562, 0.6307047009468079, -0.326521635055542, 0.021098682656884193, -0.32230299711227417, 0.030014891177415848, -0.014677518047392368, 0.6226428151130676, -0.33665731549263, -0.020956380292773247, 0.002731946762651205, 0.006381465587764978, -0.03018156625330448, 0.29053357243537903, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15754462778568268, 0.06167082488536835, -0.04090216010808945, 0.29507559537887573, -0.22378143668174744, 0.023081617429852486, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008844357915222645]} +{"t": 0.2177, "q": [-0.3133121728897095, -0.013851847499608994, 0.015601558610796928, 0.6307047009468079, -0.3265340030193329, 0.021091332659125328, -0.32239675521850586, 0.03002341277897358, -0.014637341722846031, 0.6226257681846619, -0.3366696238517761, -0.02094901166856289, 0.002691771136596799, 0.00633589131757617, -0.030201058834791183, 0.2905096113681793, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15755660831928253, 0.0616828091442585, -0.04090216010808945, 0.29507559537887573, -0.22380541265010834, 0.02310558594763279, 0.9780685901641846, -0.18358634412288666, 0.05243098363280296, 0.008856342174112797]} +{"t": 0.2345, "q": [-0.3133974075317383, -0.013851847499608994, 0.015614950098097324, 0.6307047009468079, -0.3265298902988434, 0.02109861932694912, -0.3224649131298065, 0.029997846111655235, -0.014610557816922665, 0.6226257681846619, -0.33667370676994324, -0.020985309034585953, 0.002718554809689522, 0.006320723332464695, -0.030250148847699165, 0.2904736399650574, 0.21594375371932983, -0.005201153922826052, 0.9791951179504395, 0.15762852132320404, 0.06169478967785835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.2512, "q": [-0.3135252296924591, -0.013860369101166725, 0.015588166192173958, 0.6306791305541992, -0.3265340030193329, 0.021105842664837837, -0.3226012885570526, 0.030006367713212967, -0.014637341722846031, 0.6226257681846619, -0.33666959404945374, -0.02097807638347149, 0.0026649872306734324, 0.0062523758970201015, -0.030303962528705597, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.0409141443669796, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.2679, "q": [-0.3136274814605713, -0.013877412304282188, 0.015614950098097324, 0.6307047009468079, -0.3265257775783539, 0.02110590599477291, -0.32262685894966125, 0.029997846111655235, -0.014637341722846031, 0.6226342916488647, -0.33666959404945374, -0.02097807638347149, 0.002731946762651205, 0.006108071189373732, -0.03038698434829712, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.0409141443669796, 0.295051634311676, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 0.2846, "q": [-0.3136615753173828, -0.013894457370042801, 0.01562834158539772, 0.6307047009468079, -0.3265257775783539, 0.021091395989060402, -0.3226609230041504, 0.029997846111655235, -0.014677518047392368, 0.6226342916488647, -0.3366449773311615, -0.021021820604801178, 0.0026382035575807095, 0.006092881318181753, -0.030396757647395134, 0.29042571783065796, 0.21596772968769073, -0.005201153922826052, 0.9791830778121948, 0.15760454535484314, 0.06169478967785835, -0.04087819159030914, 0.295051634311676, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18358634412288666, 0.05243098363280296, 0.008844357915222645]} +{"t": 0.3014, "q": [-0.3137127161026001, -0.01388593576848507, 0.01565512642264366, 0.630696177482605, -0.3265133798122406, 0.02111327461898327, -0.32266944646835327, 0.030014891177415848, -0.014677518047392368, 0.6226257681846619, -0.3366490602493286, -0.021029053255915642, 0.0026783791836351156, 0.006054885685443878, -0.03038187511265278, 0.2904137372970581, 0.2159557342529297, -0.0051891696639359, 0.9792070984840393, 0.1575925648212433, 0.06169478967785835, -0.04092612862586975, 0.295051634311676, -0.2237934172153473, 0.023081617429852486, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008844357915222645]} +{"t": 0.3181, "q": [-0.31372976303100586, -0.013894457370042801, 0.01564173400402069, 0.6307047009468079, -0.326521635055542, 0.021113192662596703, -0.32267796993255615, 0.030014891177415848, -0.014677518047392368, 0.6226342916488647, -0.3366572856903076, -0.021014470607042313, 0.002731946762651205, 0.005994115956127644, -0.030401311814785004, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18358634412288666, 0.05243098363280296, 0.008856342174112797]} +{"t": 0.3348, "q": [-0.3137127161026001, -0.01395411230623722, 0.01565512642264366, 0.6307047009468079, -0.326521635055542, 0.021113192662596703, -0.32268649339675903, 0.030014891177415848, -0.014664125628769398, 0.6226342916488647, -0.3366572856903076, -0.021014470607042313, 0.0026783791836351156, 0.0061156307347118855, -0.030323121696710587, 0.29044967889785767, 0.21594375371932983, -0.0051891696639359, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04086620733141899, 0.295051634311676, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.3515, "q": [-0.31373828649520874, -0.01395411230623722, 0.015668518841266632, 0.6307047009468079, -0.326521635055542, 0.021113192662596703, -0.32267796993255615, 0.030014891177415848, -0.014677518047392368, 0.6226428151130676, -0.3366367518901825, -0.021050943061709404, 0.0027051628567278385, 0.006085248664021492, -0.030332839116454124, 0.29044967889785767, 0.21594375371932983, -0.005225121974945068, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.04090216010808945, 0.29502764344215393, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008832373656332493]} +{"t": 0.3683, "q": [-0.3137127161026001, -0.01394559070467949, 0.015668518841266632, 0.6307132244110107, -0.3265175223350525, 0.021105969324707985, -0.32266944646835327, 0.03002341277897358, -0.014690909534692764, 0.6226769089698792, -0.3366285562515259, -0.021036479622125626, 0.002731946762651205, 0.006153599359095097, -0.030288858339190483, 0.29040175676345825, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06169478967785835, -0.040890175849199295, 0.2950036823749542, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 0.3851, "q": [-0.3137127161026001, -0.01395411230623722, 0.015668518841266632, 0.6307047009468079, -0.3265133798122406, 0.02111327461898327, -0.3226609230041504, 0.03003193438053131, -0.014731084927916527, 0.6227110028266907, -0.3366244435310364, -0.02105829305946827, 0.002731946762651205, 0.006100435741245747, -0.03032306581735611, 0.29042571783065796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04090216010808945, 0.29499170184135437, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.0088803106918931]} +{"t": 0.4018, "q": [-0.3136786222457886, -0.013962633907794952, 0.015695301815867424, 0.6307302713394165, -0.326521635055542, 0.021113192662596703, -0.3226523995399475, 0.03003193438053131, -0.01471769344061613, 0.6227365136146545, -0.33662036061286926, -0.02106558345258236, 0.0027453387156128883, 0.006123214494436979, -0.03029857575893402, 0.2904137372970581, 0.21594375371932983, -0.005213138181716204, 0.9791951179504395, 0.15760454535484314, 0.06169478967785835, -0.04090216010808945, 0.29503965377807617, -0.22378143668174744, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.4185, "q": [-0.3136615753173828, -0.013962633907794952, 0.015681909397244453, 0.6307473182678223, -0.3265051543712616, 0.021113337948918343, -0.3226523995399475, 0.03003193438053131, -0.014731084927916527, 0.6228047013282776, -0.3366244435310364, -0.021072816103696823, 0.0026649872306734324, 0.006108025088906288, -0.030308350920677185, 0.29040175676345825, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.4352, "q": [-0.31363600492477417, -0.013971157371997833, 0.015735477209091187, 0.6308155059814453, -0.3265092670917511, 0.02110605128109455, -0.32263535261154175, 0.03003193438053131, -0.014690909534692764, 0.6228643655776978, -0.33662036061286926, -0.02106558345258236, 0.002691771136596799, 0.006100419443100691, -0.030293578281998634, 0.29040175676345825, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 0.452, "q": [-0.3136189579963684, -0.013971157371997833, 0.015722084790468216, 0.6308666467666626, -0.3265092670917511, 0.02110605128109455, -0.32262685894966125, 0.030040455982089043, -0.014690909534692764, 0.6229410767555237, -0.3366244435310364, -0.02105829305946827, 0.002691771136596799, 0.006138376891613007, -0.030239654704928398, 0.2904137372970581, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15765248239040375, 0.06167082488536835, -0.040890175849199295, 0.2950156629085541, -0.2237934172153473, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 0.4687, "q": [-0.31359341740608215, -0.013979678973555565, 0.015748869627714157, 0.6308922171592712, -0.326513409614563, 0.021098745986819267, -0.32262685894966125, 0.030014891177415848, -0.014677518047392368, 0.6229836940765381, -0.336632639169693, -0.021058233454823494, 0.002691771136596799, 0.006183940451592207, -0.030200503766536713, 0.29042571783065796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04092612862586975, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008868326433002949]} +{"t": 0.4854, "q": [-0.3135848939418793, -0.013979678973555565, 0.015748869627714157, 0.6309177875518799, -0.3265092670917511, 0.02110605128109455, -0.3226098120212555, 0.030014891177415848, -0.014677518047392368, 0.6230263113975525, -0.336640864610672, -0.021043652668595314, 0.0027051628567278385, 0.00623712595552206, -0.030205613002181053, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.04090216010808945, 0.29502764344215393, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 0.5022, "q": [-0.3135763704776764, -0.013971157371997833, 0.015735477209091187, 0.6310114860534668, -0.3265092968940735, 0.02109152264893055, -0.3226012885570526, 0.030014891177415848, -0.014677518047392368, 0.6230774521827698, -0.3366326689720154, -0.02104371041059494, 0.002691771136596799, 0.006206746678799391, -0.030225161463022232, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.15760454535484314, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.0088803106918931]} +{"t": 0.519, "q": [-0.3135763704776764, -0.01395411230623722, 0.015708694234490395, 0.631071150302887, -0.326513409614563, 0.021098745986819267, -0.3226012885570526, 0.030014891177415848, -0.014690909534692764, 0.6231200098991394, -0.3366285562515259, -0.021065523847937584, 0.002718554809689522, 0.006183956749737263, -0.030229991301894188, 0.2904856503009796, 0.2159557342529297, -0.005201153922826052, 0.9791951179504395, 0.1575925648212433, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008844357915222645]} +{"t": 0.5357, "q": [-0.31354227662086487, -0.013962633907794952, 0.015708694234490395, 0.6310626268386841, -0.3265092968940735, 0.02107701264321804, -0.32259276509284973, 0.029997846111655235, -0.014677518047392368, 0.6231285333633423, -0.3366367518901825, -0.02103642001748085, 0.002718554809689522, 0.006176367402076721, -0.03024470806121826, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.04090216010808945, 0.29507559537887573, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008844357915222645]} +{"t": 0.5524, "q": [-0.31355932354927063, -0.01394559070467949, 0.015681909397244453, 0.6311052441596985, -0.3265092968940735, 0.02109152264893055, -0.3226012885570526, 0.030006367713212967, -0.014690909534692764, 0.6231541037559509, -0.336649090051651, -0.021014530211687088, 0.0027721223887056112, 0.006252326536923647, -0.030215498059988022, 0.2905096113681793, 0.21594375371932983, -0.005201153922826052, 0.9792070984840393, 0.15765248239040375, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 0.5692, "q": [-0.31355080008506775, -0.013928545638918877, 0.015681909397244453, 0.6311222910881042, -0.3265175223350525, 0.021091459318995476, -0.3226183354854584, 0.029997846111655235, -0.014677518047392368, 0.6231541037559509, -0.336649090051651, -0.021000007167458534, 0.002718554809689522, 0.006290311459451914, -0.030210722237825394, 0.2904856503009796, 0.21594375371932983, -0.005201153922826052, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 0.5859, "q": [-0.31354227662086487, -0.013920024037361145, 0.015681909397244453, 0.63113933801651, -0.3265175223350525, 0.021091459318995476, -0.3226012885570526, 0.029997846111655235, -0.014664125628769398, 0.6231626272201538, -0.3366408944129944, -0.021014587953686714, 0.002731946762651205, 0.0062143742106854916, -0.03027925081551075, 0.29053357243537903, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04090216010808945, 0.29507559537887573, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 0.6026, "q": [-0.31355080008506775, -0.01395411230623722, 0.015668518841266632, 0.6311734318733215, -0.3265257775783539, 0.021091395989060402, -0.32259276509284973, 0.029997846111655235, -0.014650734141469002, 0.623145580291748, -0.3366449773311615, -0.021007297560572624, 0.0027989062946289778, 0.006176416762173176, -0.030333172529935837, 0.2905096113681793, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29507559537887573, -0.22378143668174744, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 0.6193, "q": [-0.31355080008506775, -0.01394559070467949, 0.015668518841266632, 0.6311819553375244, -0.326521635055542, 0.021098682656884193, -0.32257571816444397, 0.029997846111655235, -0.014637341722846031, 0.6231541037559509, -0.3366572856903076, -0.020985424518585205, 0.0027051628567278385, 0.006206796038895845, -0.030313625931739807, 0.2905096113681793, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06169478967785835, -0.04090216010808945, 0.29507559537887573, -0.22378143668174744, 0.02310558594763279, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 0.6361, "q": [-0.3135848939418793, -0.013920024037361145, 0.01565512642264366, 0.6311904788017273, -0.326521635055542, 0.021084172651171684, -0.32264387607574463, 0.029997846111655235, -0.014637341722846031, 0.6231541037559509, -0.3366572856903076, -0.021014470607042313, 0.002691771136596799, 0.006199211813509464, -0.030338170006871223, 0.29053357243537903, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.040890175849199295, 0.29509955644607544, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.6529, "q": [-0.31359341740608215, -0.013928545638918877, 0.015668518841266632, 0.6311990022659302, -0.326521635055542, 0.021098682656884193, -0.32264387607574463, 0.029997846111655235, -0.014637341722846031, 0.6231541037559509, -0.3366531729698181, -0.021007239818572998, 0.0027051628567278385, 0.006077686324715614, -0.030396701768040657, 0.29053357243537903, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.6697, "q": [-0.3136104345321655, -0.01395411230623722, 0.015681909397244453, 0.6312075257301331, -0.3265175223350525, 0.021105969324707985, -0.32264387607574463, 0.029980802908539772, -0.014650734141469002, 0.623145580291748, -0.3366613984107971, -0.021007180213928223, 0.0027453387156128883, 0.006115671247243881, -0.03039192594587803, 0.29049763083457947, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05243098363280296, 0.008856342174112797]} +{"t": 0.6864, "q": [-0.3136104345321655, -0.01394559070467949, 0.015681909397244453, 0.6312160491943359, -0.326521635055542, 0.021113192662596703, -0.32263535261154175, 0.029989324510097504, -0.014637341722846031, 0.6231285333633423, -0.3366408944129944, -0.021014587953686714, 0.002718554809689522, 0.0060701025649905205, -0.030421247705817223, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.29507559537887573, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.1835503876209259, 0.05241899937391281, 0.008844357915222645]} +{"t": 0.7033, "q": [-0.3136274814605713, -0.01394559070467949, 0.015681909397244453, 0.6312330961227417, -0.3265175223350525, 0.021105969324707985, -0.3226523995399475, 0.029989324510097504, -0.014637341722846031, 0.6231370568275452, -0.3366449475288391, -0.021036362275481224, 0.0027051628567278385, 0.00604731822386384, -0.03043590858578682, 0.2904616594314575, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 0.7201, "q": [-0.3136786222457886, -0.013971157371997833, 0.015695301815867424, 0.6312245726585388, -0.3265257775783539, 0.02110590599477291, -0.3226523995399475, 0.029980802908539772, -0.014637341722846031, 0.6231370568275452, -0.336640864610672, -0.02102912962436676, 0.002731946762651205, 0.006115671247243881, -0.03039192594587803, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.040890175849199295, 0.29502764344215393, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.052442967891693115, 0.008856342174112797]} +{"t": 0.7368, "q": [-0.3136786222457886, -0.013962633907794952, 0.015681909397244453, 0.6312330961227417, -0.326521635055542, 0.021098682656884193, -0.3226523995399475, 0.029989324510097504, -0.014637341722846031, 0.623145580291748, -0.3366490602493286, -0.021029053255915642, 0.002718554809689522, 0.006115660537034273, -0.030372267588973045, 0.29042571783065796, 0.21594375371932983, -0.005213138181716204, 0.9791830778121948, 0.15758058428764343, 0.06167082488536835, -0.040890175849199295, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.052442967891693115, 0.008856342174112797]} +{"t": 0.7535, "q": [-0.3136700987815857, -0.013962633907794952, 0.015695301815867424, 0.6312501430511475, -0.326521635055542, 0.021084172651171684, -0.3226609230041504, 0.029997846111655235, -0.014637341722846031, 0.6231370568275452, -0.3366449773311615, -0.021021820604801178, 0.002758730435743928, 0.006100476253777742, -0.030391870066523552, 0.29040175676345825, 0.21596772968769073, -0.0051891696639359, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.040890175849199295, 0.2950156629085541, -0.2237934172153473, 0.02310558594763279, 0.9780805706977844, -0.1835503876209259, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.7702, "q": [-0.31369566917419434, -0.013979678973555565, 0.015708694234490395, 0.6312330961227417, -0.3265175223350525, 0.021105969324707985, -0.3226609230041504, 0.029989324510097504, -0.014677518047392368, 0.6231370568275452, -0.336640864610672, -0.021043652668595314, 0.002718554809689522, 0.006070069968700409, -0.030362272635102272, 0.29040175676345825, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04090216010808945, 0.2950156629085541, -0.22378143668174744, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.787, "q": [-0.3136786222457886, -0.013979678973555565, 0.015695301815867424, 0.6312330961227417, -0.326521635055542, 0.021113192662596703, -0.32267796993255615, 0.029997846111655235, -0.01470430102199316, 0.6231541037559509, -0.3366285562515259, -0.021036479622125626, 0.002718554809689522, 0.006100454367697239, -0.030352553352713585, 0.2904376983642578, 0.21594375371932983, -0.0051891696639359, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.04087819159030914, 0.2950156629085541, -0.2238173931837082, 0.02310558594763279, 0.9780925512313843, -0.1835743635892868, 0.052442967891693115, 0.008844357915222645]} +{"t": 0.8037, "q": [-0.31363600492477417, -0.013979678973555565, 0.015708694234490395, 0.6312330961227417, -0.3265175223350525, 0.021105969324707985, -0.3226609230041504, 0.030006367713212967, -0.014664125628769398, 0.6231541037559509, -0.3366367518901825, -0.021050943061709404, 0.002691771136596799, 0.006115636322647333, -0.03033295087516308, 0.2904137372970581, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.04090216010808945, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780685901641846, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 0.8204, "q": [-0.3136700987815857, -0.013979678973555565, 0.015708694234490395, 0.6312330961227417, -0.326513409614563, 0.021098745986819267, -0.32264387607574463, 0.030006367713212967, -0.01470430102199316, 0.6231541037559509, -0.3366285562515259, -0.02105100080370903, 0.0027453387156128883, 0.006100441329181194, -0.03033289685845375, 0.2904376983642578, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835503876209259, 0.05243098363280296, 0.008844357915222645]} +{"t": 0.8372, "q": [-0.31364452838897705, -0.013971157371997833, 0.015708694234490395, 0.6312245726585388, -0.3265175223350525, 0.021105969324707985, -0.32264387607574463, 0.030014891177415848, -0.01471769344061613, 0.6231626272201538, -0.336640864610672, -0.021043652668595314, 0.002691771136596799, 0.006123231258243322, -0.030328065156936646, 0.2904376983642578, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.157616525888443, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.8539, "q": [-0.31364452838897705, -0.013971157371997833, 0.015708694234490395, 0.6312330961227417, -0.326521635055542, 0.021098682656884193, -0.3226609230041504, 0.030014891177415848, -0.014731084927916527, 0.6231711506843567, -0.3366326689720154, -0.021029187366366386, 0.002718554809689522, 0.00625234842300415, -0.03025481477379799, 0.2904616594314575, 0.2159797102212906, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.8706, "q": [-0.31365305185317993, -0.013962633907794952, 0.015695301815867424, 0.6312330961227417, -0.3265092670917511, 0.02110605128109455, -0.3226523995399475, 0.03002341277897358, -0.014677518047392368, 0.6231881976127625, -0.3366326689720154, -0.021029187366366386, 0.002731946762651205, 0.006168789230287075, -0.03027908317744732, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.0409141443669796, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008868326433002949]} +{"t": 0.8873, "q": [-0.31365305185317993, -0.01394559070467949, 0.015722084790468216, 0.6312501430511475, -0.326513409614563, 0.021098745986819267, -0.3226523995399475, 0.03002341277897358, -0.014690909534692764, 0.623222291469574, -0.3366326689720154, -0.021029187366366386, 0.0026783791836351156, 0.0061611998826265335, -0.030293799936771393, 0.2904616594314575, 0.21594375371932983, -0.005213138181716204, 0.9791951179504395, 0.15760454535484314, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.9041, "q": [-0.3136189579963684, -0.013962633907794952, 0.015708694234490395, 0.6312586665153503, -0.326521635055542, 0.021113192662596703, -0.3226609230041504, 0.030014891177415848, -0.014677518047392368, 0.6232308149337769, -0.336640864610672, -0.021043652668595314, 0.002718554809689522, 0.006199179217219353, -0.030279194936156273, 0.29044967889785767, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.061658840626478195, -0.040890175849199295, 0.29502764344215393, -0.22378143668174744, 0.02310558594763279, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 0.9209, "q": [-0.31363600492477417, -0.013962633907794952, 0.015695301815867424, 0.6312671899795532, -0.326513409614563, 0.021098745986819267, -0.32264387607574463, 0.030014891177415848, -0.014677518047392368, 0.6232649087905884, -0.3366367518901825, -0.02103642001748085, 0.002718554809689522, 0.006191589869558811, -0.030293911695480347, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15755660831928253, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008856342174112797]} +{"t": 0.9376, "q": [-0.3136189579963684, -0.01395411230623722, 0.015681909397244453, 0.6313012838363647, -0.326513409614563, 0.021098745986819267, -0.32263535261154175, 0.030014891177415848, -0.014690909534692764, 0.6232819557189941, -0.3366285562515259, -0.02105100080370903, 0.0026783791836351156, 0.006168805528432131, -0.030308572575449944, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 0.9543, "q": [-0.3136189579963684, -0.013971157371997833, 0.015708694234490395, 0.6313268542289734, -0.3265175223350525, 0.021091459318995476, -0.3226523995399475, 0.029997846111655235, -0.014690909534692764, 0.6233245730400085, -0.3366285562515259, -0.021036479622125626, 0.0026783791836351156, 0.006244780961424112, -0.03030884824693203, 0.2904736399650574, 0.2159797102212906, -0.005213138181716204, 0.9791830778121948, 0.15762852132320404, 0.06167082488536835, -0.04087819159030914, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.0088803106918931]} +{"t": 0.9711, "q": [-0.31360191106796265, -0.013971157371997833, 0.015681909397244453, 0.6313268542289734, -0.326513409614563, 0.021098745986819267, -0.3226523995399475, 0.030014891177415848, -0.014677518047392368, 0.6233330965042114, -0.3366572856903076, -0.020985424518585205, 0.002718554809689522, 0.006161210592836142, -0.030313458293676376, 0.2905096113681793, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.06169478967785835, -0.0409141443669796, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9781045317649841, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 0.9878, "q": [-0.31359341740608215, -0.01395411230623722, 0.015681909397244453, 0.6313353776931763, -0.3265175223350525, 0.021091459318995476, -0.32264387607574463, 0.029997846111655235, -0.014677518047392368, 0.6233160495758057, -0.3366613984107971, -0.02099265716969967, 0.002718554809689522, 0.006199206691235304, -0.03032834082841873, 0.2904736399650574, 0.21594375371932983, -0.005201153922826052, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.04087819159030914, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008856342174112797]} +{"t": 1.005, "q": [-0.31360191106796265, -0.013962633907794952, 0.015681909397244453, 0.631352424621582, -0.3265175223350525, 0.021091459318995476, -0.32264387607574463, 0.029997846111655235, -0.014677518047392368, 0.6233416199684143, -0.33666548132896423, -0.020999889820814133, 0.0027051628567278385, 0.00619922298938036, -0.030357830226421356, 0.2904736399650574, 0.21593177318572998, -0.005201153922826052, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.0409141443669796, 0.29507559537887573, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008868326433002949]} +{"t": 1.0217, "q": [-0.3136104345321655, -0.013971157371997833, 0.01564173400402069, 0.631352424621582, -0.326513409614563, 0.021098745986819267, -0.3226523995399475, 0.029989324510097504, -0.014664125628769398, 0.6233245730400085, -0.3366613984107971, -0.02099265716969967, 0.002691771136596799, 0.006191622465848923, -0.030352886766195297, 0.2905096113681793, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.2950875759124756, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05240701511502266, 0.008868326433002949]} +{"t": 1.0384, "q": [-0.3136274814605713, -0.01394559070467949, 0.015668518841266632, 0.6313439011573792, -0.3265257775783539, 0.02110590599477291, -0.32267796993255615, 0.029989324510097504, -0.014690909534692764, 0.6233330965042114, -0.3366613984107971, -0.02099265716969967, 0.0026649872306734324, 0.006191633641719818, -0.03037254512310028, 0.2905096113681793, 0.21593177318572998, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 1.0552, "q": [-0.31360191106796265, -0.013962633907794952, 0.015668518841266632, 0.631352424621582, -0.326513409614563, 0.021098745986819267, -0.32268649339675903, 0.029980802908539772, -0.014664125628769398, 0.6233330965042114, -0.33666548132896423, -0.020999889820814133, 0.0027453387156128883, 0.006222012918442488, -0.03035299852490425, 0.29049763083457947, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.157616525888443, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.1835743635892868, 0.05241899937391281, 0.008868326433002949]} +{"t": 1.0719, "q": [-0.31363600492477417, -0.01395411230623722, 0.01565512642264366, 0.6313609480857849, -0.3265257775783539, 0.021091395989060402, -0.32268649339675903, 0.029980802908539772, -0.014637341722846031, 0.6233330965042114, -0.3366572856903076, -0.020985424518585205, 0.0026649872306734324, 0.006206828635185957, -0.030372601002454758, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.29507559537887573, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 1.0887, "q": [-0.31363600492477417, -0.013971157371997833, 0.015681909397244453, 0.631352424621582, -0.3265133798122406, 0.02111327461898327, -0.32268649339675903, 0.02996375784277916, -0.014677518047392368, 0.6233330965042114, -0.3366572856903076, -0.021014470607042313, 0.002691771136596799, 0.006222023628652096, -0.030372656881809235, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 1.1054, "q": [-0.31365305185317993, -0.013971157371997833, 0.015668518841266632, 0.631352424621582, -0.326521635055542, 0.021098682656884193, -0.3226950168609619, 0.029980802908539772, -0.014650734141469002, 0.6233416199684143, -0.33666548132896423, -0.020999889820814133, 0.0026783791836351156, 0.006153656169772148, -0.0303871501237154, 0.2904616594314575, 0.2159557342529297, -0.0051891696639359, 0.9792070984840393, 0.157616525888443, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.1835743635892868, 0.05240701511502266, 0.008856342174112797]} +{"t": 1.1222, "q": [-0.3136615753173828, -0.013962633907794952, 0.015668518841266632, 0.6313439011573792, -0.3265175223350525, 0.021120497956871986, -0.3226950168609619, 0.029980802908539772, -0.014637341722846031, 0.6233330965042114, -0.33666548132896423, -0.020999889820814133, 0.0026783791836351156, 0.006070108152925968, -0.030431076884269714, 0.2904616594314575, 0.2159557342529297, -0.0051891696639359, 0.9791830778121948, 0.15762852132320404, 0.06167082488536835, -0.04090216010808945, 0.29507559537887573, -0.2237934172153473, 0.02310558594763279, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 1.1389, "q": [-0.3136700987815857, -0.013962633907794952, 0.015695301815867424, 0.631352424621582, -0.326521635055542, 0.021098682656884193, -0.32272058725357056, 0.029980802908539772, -0.014650734141469002, 0.6233330965042114, -0.3366572856903076, -0.020985424518585205, 0.0026649872306734324, 0.006130866706371307, -0.030391981825232506, 0.2904856503009796, 0.2159557342529297, -0.0051891696639359, 0.9792070984840393, 0.15762852132320404, 0.061706773936748505, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 1.1556, "q": [-0.3136615753173828, -0.013971157371997833, 0.015695301815867424, 0.6313694715499878, -0.326521635055542, 0.021098682656884193, -0.32272058725357056, 0.029980802908539772, -0.014637341722846031, 0.6233330965042114, -0.3366490602493286, -0.021029053255915642, 0.0027051628567278385, 0.00610808189958334, -0.030406642705202103, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04092612862586975, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 1.1723, "q": [-0.3137041926383972, -0.013971157371997833, 0.015695301815867424, 0.631352424621582, -0.3265257775783539, 0.02110590599477291, -0.3227376341819763, 0.029980802908539772, -0.014637341722846031, 0.6233330965042114, -0.3366572856903076, -0.021014470607042313, 0.002691771136596799, 0.00610808189958334, -0.030406642705202103, 0.2904616594314575, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.1575925648212433, 0.061706773936748505, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 1.1891, "q": [-0.3137041926383972, -0.014005245640873909, 0.015681909397244453, 0.631352424621582, -0.326521635055542, 0.021113192662596703, -0.32276320457458496, 0.02996375784277916, -0.014677518047392368, 0.6233245730400085, -0.3366613984107971, -0.02099265716969967, 0.002691771136596799, 0.006130866706371307, -0.030391981825232506, 0.29044967889785767, 0.21596772968769073, -0.005225121974945068, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 1.2058, "q": [-0.3137041926383972, -0.013979678973555565, 0.015681909397244453, 0.631352424621582, -0.3265257775783539, 0.02110590599477291, -0.3227546811103821, 0.029980802908539772, -0.014637341722846031, 0.6233330965042114, -0.33666548132896423, -0.020999889820814133, 0.002651595277711749, 0.006146050523966551, -0.03037237748503685, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 1.2225, "q": [-0.3137041926383972, -0.013971157371997833, 0.015681909397244453, 0.631352424621582, -0.3265175223350525, 0.021105969324707985, -0.32272058725357056, 0.02997227944433689, -0.014664125628769398, 0.6233245730400085, -0.33666548132896423, -0.020999889820814133, 0.0026649872306734324, 0.006153656169772148, -0.0303871501237154, 0.2904616594314575, 0.21596772968769073, -0.005225121974945068, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 1.2392, "q": [-0.31369566917419434, -0.013988200575113297, 0.015681909397244453, 0.6313609480857849, -0.326521635055542, 0.021113192662596703, -0.3227376341819763, 0.02997227944433689, -0.014637341722846031, 0.6233330965042114, -0.3366613984107971, -0.02099265716969967, 0.0027051628567278385, 0.0061232661828398705, -0.030387038365006447, 0.2904616594314575, 0.21596772968769073, -0.005225121974945068, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 1.2561, "q": [-0.3137041926383972, -0.013988200575113297, 0.015681909397244453, 0.6313694715499878, -0.3265298902988434, 0.02111312933266163, -0.3227376341819763, 0.029997846111655235, -0.014664125628769398, 0.6233330965042114, -0.3366613984107971, -0.021007180213928223, 0.002731946762651205, 0.006146066822111607, -0.030401866883039474, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9791830778121948, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.2237934172153473, 0.023081617429852486, 0.9781045317649841, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 1.2728, "q": [-0.3137041926383972, -0.013971157371997833, 0.015668518841266632, 0.6313694715499878, -0.3265257775783539, 0.02110590599477291, -0.32272911071777344, 0.029980802908539772, -0.014677518047392368, 0.6233416199684143, -0.33666548132896423, -0.020999889820814133, 0.0027051628567278385, 0.006123277358710766, -0.03040669858455658, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.1835503876209259, 0.05241899937391281, 0.008856342174112797]} +{"t": 1.2895, "q": [-0.3137127161026001, -0.013988200575113297, 0.015681909397244453, 0.6313694715499878, -0.3265175223350525, 0.021091459318995476, -0.32272911071777344, 0.029997846111655235, -0.014690909534692764, 0.623367190361023, -0.3366490602493286, -0.021029053255915642, 0.002691771136596799, 0.006138466764241457, -0.030396923422813416, 0.29049763083457947, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.04090216010808945, 0.29503965377807617, -0.22378143668174744, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 1.3062, "q": [-0.31369566917419434, -0.013971157371997833, 0.015681909397244453, 0.6313864588737488, -0.326513409614563, 0.021098745986819267, -0.3227546811103821, 0.029980802908539772, -0.014690909534692764, 0.6233927607536316, -0.3366449773311615, -0.021021820604801178, 0.002691771136596799, 0.006047328934073448, -0.030455566942691803, 0.2904856503009796, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.22378143668174744, 0.02310558594763279, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 1.323, "q": [-0.3136700987815857, -0.013996722176671028, 0.015681909397244453, 0.6314205527305603, -0.3265175223350525, 0.021091459318995476, -0.3227461576461792, 0.029980802908539772, -0.014664125628769398, 0.6234439015388489, -0.3366449773311615, -0.021021820604801178, 0.002731946762651205, 0.0061005037277936935, -0.03044101782143116, 0.2905096113681793, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 1.3397, "q": [-0.3136700987815857, -0.013971157371997833, 0.015681909397244453, 0.631446123123169, -0.3265175223350525, 0.021091459318995476, -0.3227376341819763, 0.029989324510097504, -0.014664125628769398, 0.6234779357910156, -0.3366449773311615, -0.021021820604801178, 0.0027051628567278385, 0.006100498139858246, -0.030431188642978668, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008832373656332493]} +{"t": 1.3564, "q": [-0.3136786222457886, -0.013988200575113297, 0.015681909397244453, 0.6314802169799805, -0.326513409614563, 0.021098745986819267, -0.32272911071777344, 0.029980802908539772, -0.014637341722846031, 0.6235035061836243, -0.3366408944129944, -0.021014587953686714, 0.0026649872306734324, 0.0060549345798790455, -0.030470339581370354, 0.2904856503009796, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.0409141443669796, 0.29507559537887573, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008856342174112797]} +{"t": 1.3731, "q": [-0.31365305185317993, -0.013971157371997833, 0.015695301815867424, 0.6315057873725891, -0.326513409614563, 0.021098745986819267, -0.3227461576461792, 0.02997227944433689, -0.014637341722846031, 0.6235376000404358, -0.3366449773311615, -0.021021820604801178, 0.0027453387156128883, 0.006062518805265427, -0.030445793643593788, 0.2904376983642578, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 1.3898, "q": [-0.3136615753173828, -0.013971157371997833, 0.015722084790468216, 0.6315398812294006, -0.326513409614563, 0.021098745986819267, -0.32272058725357056, 0.029980802908539772, -0.014664125628769398, 0.6235716938972473, -0.3366408944129944, -0.021014587953686714, 0.002691771136596799, 0.006024533417075872, -0.030450569465756416, 0.2904736399650574, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.2237934172153473, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 1.4065, "q": [-0.3136615753173828, -0.013979678973555565, 0.015708694234490395, 0.631582498550415, -0.3265175223350525, 0.021105969324707985, -0.32272911071777344, 0.029980802908539772, -0.014690909534692764, 0.6235802173614502, -0.3366490602493286, -0.021029053255915642, 0.0026783791836351156, 0.006032133940607309, -0.030455512925982475, 0.29044967889785767, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.04087819159030914, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 1.4234, "q": [-0.31364452838897705, -0.013979678973555565, 0.015708694234490395, 0.6315995454788208, -0.3265175223350525, 0.021105969324707985, -0.32272058725357056, 0.02997227944433689, -0.014664125628769398, 0.6236057877540588, -0.336640864610672, -0.021043652668595314, 0.002691771136596799, 0.006062518805265427, -0.030445793643593788, 0.2904616594314575, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.040890175849199295, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 1.4402, "q": [-0.31365305185317993, -0.013971157371997833, 0.015735477209091187, 0.6316165924072266, -0.3265092670917511, 0.02110605128109455, -0.3227546811103821, 0.02996375784277916, -0.014650734141469002, 0.623597264289856, -0.3366449773311615, -0.021021820604801178, 0.0026382035575807095, 0.006092898081988096, -0.03042624518275261, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18358634412288666, 0.05243098363280296, 0.008868326433002949]} +{"t": 1.4569, "q": [-0.3136615753173828, -0.013979678973555565, 0.015695301815867424, 0.6316251158714294, -0.326521635055542, 0.021098682656884193, -0.3227376341819763, 0.02997227944433689, -0.014664125628769398, 0.6236057877540588, -0.336649090051651, -0.021014530211687088, 0.002691771136596799, 0.006100492551922798, -0.030421359464526176, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15754462778568268, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.052395034581422806, 0.008856342174112797]} +{"t": 1.4736, "q": [-0.3136615753173828, -0.013971157371997833, 0.015695301815867424, 0.6316336393356323, -0.3265298902988434, 0.02109861932694912, -0.3227376341819763, 0.02997227944433689, -0.014650734141469002, 0.6236143112182617, -0.33666548132896423, -0.020999889820814133, 0.002731946762651205, 0.006138472352176905, -0.030406752601265907, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.2237934172153473, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 1.4903, "q": [-0.3136786222457886, -0.013971157371997833, 0.015695301815867424, 0.6316421627998352, -0.326521635055542, 0.021113192662596703, -0.3227376341819763, 0.02997227944433689, -0.014637341722846031, 0.6236313581466675, -0.3366531729698181, -0.021021762862801552, 0.002691771136596799, 0.006062529515475035, -0.03046545200049877, 0.2904856503009796, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 1.5071, "q": [-0.3136786222457886, -0.013979678973555565, 0.015695301815867424, 0.6316421627998352, -0.3265175223350525, 0.021105969324707985, -0.32276320457458496, 0.02996375784277916, -0.014637341722846031, 0.6236398816108704, -0.33666959404945374, -0.021007122471928596, 0.002731946762651205, 0.006092903204262257, -0.03043607622385025, 0.2904856503009796, 0.21596772968769073, -0.0051891696639359, 0.9791830778121948, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.2237934172153473, 0.023093601688742638, 0.9780685901641846, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 1.5238, "q": [-0.3136786222457886, -0.013979678973555565, 0.015681909397244453, 0.6316506862640381, -0.3265175223350525, 0.021091459318995476, -0.32276320457458496, 0.02996375784277916, -0.014664125628769398, 0.6236398816108704, -0.33666548132896423, -0.020999889820814133, 0.0026649872306734324, 0.0061005037277936935, -0.03044101782143116, 0.29049763083457947, 0.21596772968769073, -0.005213138181716204, 0.9791830778121948, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 1.5406, "q": [-0.31368714570999146, -0.013988200575113297, 0.015681909397244453, 0.631659209728241, -0.326521635055542, 0.021098682656884193, -0.3227546811103821, 0.02997227944433689, -0.014637341722846031, 0.6236398816108704, -0.3366572856903076, -0.021014470607042313, 0.002691771136596799, 0.006062529515475035, -0.03046545200049877, 0.2905096113681793, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15754462778568268, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9781045317649841, -0.18358634412288666, 0.05243098363280296, 0.008856342174112797]} +{"t": 1.5573, "q": [-0.3136786222457886, -0.013979678973555565, 0.015681909397244453, 0.6316933035850525, -0.3265175223350525, 0.021105969324707985, -0.3227546811103821, 0.02997227944433689, -0.014637341722846031, 0.6236398816108704, -0.3366572856903076, -0.021014470607042313, 0.0026649872306734324, 0.005994165316224098, -0.03048977628350258, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18358634412288666, 0.05243098363280296, 0.008856342174112797]} +{"t": 1.574, "q": [-0.3136700987815857, -0.013996722176671028, 0.015708694234490395, 0.6316847801208496, -0.3265175223350525, 0.021091459318995476, -0.3227546811103821, 0.02997227944433689, -0.014650734141469002, 0.6236569285392761, -0.3366449773311615, -0.021021820604801178, 0.0027051628567278385, 0.006009360309690237, -0.030489832162857056, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.1576405018568039, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05240701511502266, 0.008856342174112797]} +{"t": 1.5908, "q": [-0.31368714570999146, -0.013962633907794952, 0.015695301815867424, 0.6317188739776611, -0.326521635055542, 0.021098682656884193, -0.3227546811103821, 0.029980802908539772, -0.014650734141469002, 0.6236569285392761, -0.3366367816925049, -0.021021896973252296, 0.002731946762651205, 0.0059258174151182175, -0.03054358996450901, 0.2904616594314575, 0.21594375371932983, -0.005225121974945068, 0.9792070984840393, 0.15752065181732178, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22378143668174744, 0.023093601688742638, 0.9780925512313843, -0.1835743635892868, 0.05241899937391281, 0.008844357915222645]} +{"t": 1.6075, "q": [-0.31369566917419434, -0.013979678973555565, 0.015708694234490395, 0.631727397441864, -0.3265175223350525, 0.021105969324707985, -0.3227546811103821, 0.02996375784277916, -0.014650734141469002, 0.6236739754676819, -0.3366490602493286, -0.021029053255915642, 0.002691771136596799, 0.005941001698374748, -0.030523985624313354, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 1.6242, "q": [-0.3136786222457886, -0.013996722176671028, 0.015722084790468216, 0.6317444443702698, -0.3265175223350525, 0.021105969324707985, -0.3227546811103821, 0.02997227944433689, -0.014623950235545635, 0.6236995458602905, -0.3366367518901825, -0.02103642001748085, 0.0027051628567278385, 0.005978975910693407, -0.030499551445245743, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 1.6412, "q": [-0.3136700987815857, -0.013988200575113297, 0.015748869627714157, 0.6317529678344727, -0.3265133798122406, 0.02111327461898327, -0.32276320457458496, 0.02996375784277916, -0.014664125628769398, 0.6237251162528992, -0.3366449773311615, -0.021021820604801178, 0.0026783791836351156, 0.005986565258353949, -0.03048483468592167, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.0409141443669796, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05240701511502266, 0.008844357915222645]} +{"t": 1.658, "q": [-0.3136700987815857, -0.013979678973555565, 0.015735477209091187, 0.6317529678344727, -0.326513409614563, 0.021098745986819267, -0.32277172803878784, 0.029980802908539772, -0.014650734141469002, 0.6237677335739136, -0.336640864610672, -0.021043652668595314, 0.002731946762651205, 0.00597137538716197, -0.030494607985019684, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.2237934172153473, 0.023081617429852486, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008844357915222645]} +{"t": 1.6747, "q": [-0.3136786222457886, -0.013996722176671028, 0.015735477209091187, 0.6317699551582336, -0.3265175223350525, 0.021091459318995476, -0.32277172803878784, 0.02997227944433689, -0.014664125628769398, 0.6237677335739136, -0.3366408944129944, -0.021014587953686714, 0.0027453387156128883, 0.005925806704908609, -0.030523929744958878, 0.2904616594314575, 0.2159557342529297, -0.0051891696639359, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.0409141443669796, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008868326433002949]} +{"t": 1.6915, "q": [-0.3136786222457886, -0.013971157371997833, 0.015748869627714157, 0.6317699551582336, -0.326521635055542, 0.021098682656884193, -0.32276320457458496, 0.029980802908539772, -0.014623950235545635, 0.6237762570381165, -0.3366449773311615, -0.021021820604801178, 0.0026783791836351156, 0.00597137538716197, -0.030494607985019684, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.06169478967785835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780685901641846, -0.18353840708732605, 0.05240701511502266, 0.008844357915222645]} +{"t": 1.7082, "q": [-0.3136615753173828, -0.013996722176671028, 0.015722084790468216, 0.6318040490150452, -0.3265257775783539, 0.021091395989060402, -0.3227546811103821, 0.029989324510097504, -0.014650734141469002, 0.6238188743591309, -0.3366490602493286, -0.021029053255915642, 0.002731946762651205, 0.00600175466388464, -0.030475059524178505, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06169478967785835, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 1.7249, "q": [-0.3136786222457886, -0.013971157371997833, 0.015748869627714157, 0.6318210959434509, -0.3265092670917511, 0.02110605128109455, -0.3227546811103821, 0.029980802908539772, -0.014677518047392368, 0.6238359212875366, -0.3366449773311615, -0.021021820604801178, 0.002691771136596799, 0.005994165316224098, -0.03048977628350258, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.02310558594763279, 0.9781045317649841, -0.1835743635892868, 0.05243098363280296, 0.008856342174112797]} +{"t": 1.7417, "q": [-0.31365305185317993, -0.01401376724243164, 0.015762262046337128, 0.6318381428718567, -0.3265175223350525, 0.021091459318995476, -0.3227376341819763, 0.029980802908539772, -0.014637341722846031, 0.6239040493965149, -0.3366367816925049, -0.021021896973252296, 0.002731946762651205, 0.006024544592946768, -0.03047022968530655, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008856342174112797]} +{"t": 1.7584, "q": [-0.3136615753173828, -0.013996722176671028, 0.015748869627714157, 0.6318637132644653, -0.3265175223350525, 0.021091459318995476, -0.32276320457458496, 0.029980802908539772, -0.014677518047392368, 0.6239210963249207, -0.3366490602493286, -0.021029053255915642, 0.002718554809689522, 0.006009349599480629, -0.030470173805952072, 0.2904736399650574, 0.2159797102212906, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.040890175849199295, 0.295051634311676, -0.22378143668174744, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 1.7751, "q": [-0.31365305185317993, -0.014005245640873909, 0.015748869627714157, 0.631889283657074, -0.326513409614563, 0.021098745986819267, -0.3227546811103821, 0.029980802908539772, -0.014650734141469002, 0.6239551901817322, -0.3366408944129944, -0.021014587953686714, 0.002691771136596799, 0.00602453900501132, -0.030460398644208908, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008856342174112797]} +{"t": 1.792, "q": [-0.31365305185317993, -0.013988200575113297, 0.015748869627714157, 0.6319404244422913, -0.326521635055542, 0.021098682656884193, -0.32276320457458496, 0.029980802908539772, -0.014650734141469002, 0.6240063309669495, -0.3366449773311615, -0.021021820604801178, 0.0027051628567278385, 0.006009349599480629, -0.030470173805952072, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9791830778121948, 0.15760454535484314, 0.0616828091442585, -0.040890175849199295, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 1.8087, "q": [-0.3136274814605713, -0.013996722176671028, 0.015748869627714157, 0.6319489479064941, -0.3265175223350525, 0.021091459318995476, -0.3227546811103821, 0.02997227944433689, -0.014637341722846031, 0.6240319013595581, -0.3366449773311615, -0.021021820604801178, 0.0026649872306734324, 0.006032139528542757, -0.030465342104434967, 0.29049763083457947, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.0409141443669796, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.1835503876209259, 0.05241899937391281, 0.008844357915222645]} +{"t": 1.8254, "q": [-0.31363600492477417, -0.013996722176671028, 0.015748869627714157, 0.6319659948348999, -0.3265175223350525, 0.021091459318995476, -0.3227546811103821, 0.029980802908539772, -0.014664125628769398, 0.6240659952163696, -0.3366449773311615, -0.021021820604801178, 0.0027051628567278385, 0.005978970322757959, -0.03048972226679325, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 1.8422, "q": [-0.3136274814605713, -0.013988200575113297, 0.015735477209091187, 0.6319915652275085, -0.326513409614563, 0.021098745986819267, -0.32276320457458496, 0.029955236241221428, -0.014650734141469002, 0.6240830421447754, -0.3366490602493286, -0.021029053255915642, 0.002718554809689522, 0.006039739586412907, -0.030470283702015877, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.0409141443669796, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 1.8589, "q": [-0.3136274814605713, -0.013979678973555565, 0.015722084790468216, 0.6320000886917114, -0.3265175223350525, 0.021091459318995476, -0.32277172803878784, 0.02996375784277916, -0.014650734141469002, 0.624108612537384, -0.336649090051651, -0.021000007167458534, 0.002718554809689522, 0.006077708210796118, -0.030436020344495773, 0.2904856503009796, 0.21594375371932983, -0.005213138181716204, 0.9792190790176392, 0.15756858885288239, 0.06167082488536835, -0.040890175849199295, 0.29507559537887573, -0.2237934172153473, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 1.8756, "q": [-0.31363600492477417, -0.013988200575113297, 0.015722084790468216, 0.6320000886917114, -0.326521635055542, 0.021084172651171684, -0.3227887749671936, 0.029955236241221428, -0.014650734141469002, 0.6241256594657898, -0.3366449773311615, -0.021021820604801178, 0.0027051628567278385, 0.006070118863135576, -0.030450737103819847, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 1.8926, "q": [-0.31363600492477417, -0.013996722176671028, 0.015748869627714157, 0.6320171356201172, -0.326513409614563, 0.021098745986819267, -0.32281434535980225, 0.029955236241221428, -0.014637341722846031, 0.624108612537384, -0.33666136860847473, -0.021021703258156776, 0.002718554809689522, 0.006085303146392107, -0.03043113276362419, 0.2904856503009796, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15760454535484314, 0.06169478967785835, -0.04090216010808945, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9781045317649841, -0.18356238305568695, 0.052442967891693115, 0.008856342174112797]} +{"t": 1.9093, "q": [-0.31363600492477417, -0.013988200575113297, 0.015735477209091187, 0.6320086121559143, -0.3265175223350525, 0.021091459318995476, -0.3227972984313965, 0.029955236241221428, -0.014637341722846031, 0.6241256594657898, -0.33667367696762085, -0.02101435326039791, 0.0026783791836351156, 0.006070124451071024, -0.03046056628227234, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.1835743635892868, 0.05241899937391281, 0.008868326433002949]} +{"t": 1.9261, "q": [-0.31364452838897705, -0.013996722176671028, 0.015695301815867424, 0.6320171356201172, -0.3265092670917511, 0.02110605128109455, -0.32280582189559937, 0.029946712777018547, -0.014650734141469002, 0.6241256594657898, -0.33666548132896423, -0.020999889820814133, 0.0026783791836351156, 0.0060245501808822155, -0.03048005886375904, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.1575925648212433, 0.06169478967785835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.052442967891693115, 0.008844357915222645]} +{"t": 1.943, "q": [-0.31364452838897705, -0.013988200575113297, 0.015668518841266632, 0.6320171356201172, -0.326521635055542, 0.021098682656884193, -0.32282283902168274, 0.029955236241221428, -0.014623950235545635, 0.624108612537384, -0.33666548132896423, -0.020999889820814133, 0.0026382035575807095, 0.0060245501808822155, -0.03048005886375904, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 1.9597, "q": [-0.31363600492477417, -0.013996722176671028, 0.015708694234490395, 0.6320256590843201, -0.326513409614563, 0.021098745986819267, -0.32280582189559937, 0.029946712777018547, -0.014637341722846031, 0.6241171360015869, -0.33666548132896423, -0.020999889820814133, 0.002718554809689522, 0.005994176026433706, -0.030509434640407562, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.29507559537887573, -0.2237934172153473, 0.023093601688742638, 0.9781045317649841, -0.18356238305568695, 0.052442967891693115, 0.008856342174112797]} +{"t": 1.9765, "q": [-0.31363600492477417, -0.013988200575113297, 0.015695301815867424, 0.6320171356201172, -0.3265257775783539, 0.021091395989060402, -0.3228313624858856, 0.029946712777018547, -0.014650734141469002, 0.6241256594657898, -0.33667367696762085, -0.02101435326039791, 0.0027051628567278385, 0.006070130039006472, -0.03047039546072483, 0.2905096113681793, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.0409141443669796, 0.29507559537887573, -0.22380541265010834, 0.023069633170962334, 0.9781045317649841, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 1.9933, "q": [-0.3136615753173828, -0.013988200575113297, 0.015695301815867424, 0.6320171356201172, -0.3265298902988434, 0.02109861932694912, -0.3228825032711029, 0.029946712777018547, -0.014610557816922665, 0.6241000890731812, -0.33666548132896423, -0.020999889820814133, 0.002718554809689522, 0.006032150238752365, -0.03048500046133995, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04086620733141899, 0.295051634311676, -0.22378143668174744, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 2.0101, "q": [-0.31369566917419434, -0.013988200575113297, 0.015708694234490395, 0.6320171356201172, -0.326521635055542, 0.021113192662596703, -0.3228825032711029, 0.029938191175460815, -0.014623950235545635, 0.624108612537384, -0.33666959404945374, -0.021007122471928596, 0.002718554809689522, 0.006024555303156376, -0.030489888042211533, 0.2904616594314575, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.040890175849199295, 0.29503965377807617, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008856342174112797]} +{"t": 2.0268, "q": [-0.3137127161026001, -0.013996722176671028, 0.015695301815867424, 0.6320171356201172, -0.3265257775783539, 0.02110590599477291, -0.32290807366371155, 0.029946712777018547, -0.014623950235545635, 0.624108612537384, -0.33666959404945374, -0.021007122471928596, 0.002731946762651205, 0.005978992208838463, -0.030529038980603218, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 2.0435, "q": [-0.3137127161026001, -0.013988200575113297, 0.015695301815867424, 0.6320171356201172, -0.3265257775783539, 0.02110590599477291, -0.3229251205921173, 0.029946712777018547, -0.014597166329622269, 0.6241000890731812, -0.33667778968811035, -0.021021604537963867, 0.002651595277711749, 0.005956196691840887, -0.03052404150366783, 0.2905096113681793, 0.2159797102212906, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.04092612862586975, 0.2950636148452759, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05240701511502266, 0.008868326433002949]} +{"t": 2.0603, "q": [-0.3137553334236145, -0.013988200575113297, 0.015695301815867424, 0.6320171356201172, -0.3265257775783539, 0.02110590599477291, -0.32295069098472595, 0.029938191175460815, -0.014610557816922665, 0.6240830421447754, -0.33668190240859985, -0.02099977247416973, 0.0026783791836351156, 0.005887848790735006, -0.030577853322029114, 0.2904856503009796, 0.21594375371932983, -0.005213138181716204, 0.9791951179504395, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.2237934172153473, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.077, "q": [-0.3138149678707123, -0.01401376724243164, 0.015708694234490395, 0.6320171356201172, -0.3265257775783539, 0.02110590599477291, -0.3229847848415375, 0.029938191175460815, -0.014583774842321873, 0.6241000890731812, -0.33669009804725647, -0.021014254540205002, 0.0027453387156128883, 0.005933412350714207, -0.03053870238363743, 0.2904616594314575, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.06169478967785835, -0.0409141443669796, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9781045317649841, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 2.0937, "q": [-0.31383201479911804, -0.01401376724243164, 0.015695301815867424, 0.6320256590843201, -0.3265257775783539, 0.02110590599477291, -0.32299330830574036, 0.029929669573903084, -0.014583774842321873, 0.6240915656089783, -0.33668190240859985, -0.02099977247416973, 0.0027051628567278385, 0.005910633131861687, -0.030563192442059517, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06169478967785835, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.1105, "q": [-0.3138490617275238, -0.014005245640873909, 0.015708694234490395, 0.6320171356201172, -0.3265340030193329, 0.02112036943435669, -0.3230103552341461, 0.029929669573903084, -0.014570382423698902, 0.6240915656089783, -0.33667778968811035, -0.020992539823055267, 0.0026783791836351156, 0.005880243144929409, -0.030563080683350563, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.061658840626478195, -0.04090216010808945, 0.29502764344215393, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.1274, "q": [-0.31386610865592957, -0.014005245640873909, 0.015708694234490395, 0.6320171356201172, -0.3265381455421448, 0.021113082766532898, -0.3230358958244324, 0.029921147972345352, -0.014583774842321873, 0.6240830421447754, -0.33669009804725647, -0.021014254540205002, 0.002718554809689522, 0.005895438138395548, -0.03056313656270504, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.04087819159030914, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 2.1441, "q": [-0.3138916790485382, -0.014005245640873909, 0.015681909397244453, 0.6320256590843201, -0.3265257775783539, 0.021134961396455765, -0.3230188488960266, 0.029921147972345352, -0.014583774842321873, 0.6240915656089783, -0.33668190240859985, -0.02099977247416973, 0.0027453387156128883, 0.005880253855139017, -0.030582740902900696, 0.2904736399650574, 0.2159797102212906, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05243098363280296, 0.008856342174112797]} +{"t": 2.1608, "q": [-0.3138831555843353, -0.014022288843989372, 0.015708694234490395, 0.6320171356201172, -0.3265175223350525, 0.021120497956871986, -0.3230358958244324, 0.029921147972345352, -0.014610557816922665, 0.6240915656089783, -0.33668598532676697, -0.021007023751735687, 0.002718554809689522, 0.0059334286488592625, -0.030568189918994904, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06169478967785835, -0.0409141443669796, 0.29507559537887573, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008868326433002949]} +{"t": 2.1776, "q": [-0.3138916790485382, -0.013996722176671028, 0.015735477209091187, 0.6320171356201172, -0.326521635055542, 0.021098682656884193, -0.3230358958244324, 0.02991262450814247, -0.014610557816922665, 0.6240915656089783, -0.33669009804725647, -0.021014254540205002, 0.002731946762651205, 0.005918233655393124, -0.030568134039640427, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.2238173931837082, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.1943, "q": [-0.31387463212013245, -0.014005245640873909, 0.015695301815867424, 0.632034182548523, -0.3265175223350525, 0.021091459318995476, -0.3230103552341461, 0.02990410290658474, -0.014623950235545635, 0.624108612537384, -0.33668187260627747, -0.02102883718907833, 0.002718554809689522, 0.005903043784201145, -0.03057790920138359, 0.2904736399650574, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 2.2111, "q": [-0.31387463212013245, -0.01401376724243164, 0.015681909397244453, 0.6320256590843201, -0.326513409614563, 0.021098745986819267, -0.3230273723602295, 0.02991262450814247, -0.014583774842321873, 0.6241000890731812, -0.33667367696762085, -0.02101435326039791, 0.002691771136596799, 0.005925823003053665, -0.030553419142961502, 0.2904616594314575, 0.21593177318572998, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22378143668174744, 0.02310558594763279, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.2278, "q": [-0.31387463212013245, -0.01401376724243164, 0.015708694234490395, 0.6320171356201172, -0.3265175223350525, 0.021091459318995476, -0.32304441928863525, 0.02991262450814247, -0.014597166329622269, 0.624108612537384, -0.33666956424713135, -0.02102164551615715, 0.002731946762651205, 0.00592581182718277, -0.03053375892341137, 0.2904616594314575, 0.21596772968769073, -0.0051891696639359, 0.9792070984840393, 0.1575925648212433, 0.06169478967785835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05240701511502266, 0.008856342174112797]} +{"t": 2.2446, "q": [-0.3138575851917267, -0.013996722176671028, 0.015708694234490395, 0.632034182548523, -0.326521635055542, 0.021113192662596703, -0.3230103552341461, 0.029921147972345352, -0.014637341722846031, 0.624108612537384, -0.33666956424713135, -0.02102164551615715, 0.0027453387156128883, 0.005956196691840887, -0.03052404150366783, 0.2904616594314575, 0.2159557342529297, -0.005201153922826052, 0.9791830778121948, 0.15756858885288239, 0.06167082488536835, -0.04087819159030914, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.1835264265537262, 0.05241899937391281, 0.008844357915222645]} +{"t": 2.2614, "q": [-0.3138575851917267, -0.013996722176671028, 0.015722084790468216, 0.632034182548523, -0.3265092670917511, 0.02110605128109455, -0.32299330830574036, 0.029929669573903084, -0.014637341722846031, 0.6241171360015869, -0.3366531729698181, -0.021036284044384956, 0.002718554809689522, 0.0059486073441803455, -0.030538758262991905, 0.29044967889785767, 0.2159797102212906, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.2782, "q": [-0.3138490617275238, -0.01401376724243164, 0.015695301815867424, 0.632034182548523, -0.3265092670917511, 0.02110605128109455, -0.3230103552341461, 0.029921147972345352, -0.014623950235545635, 0.6241171360015869, -0.3366531431674957, -0.021050825715065002, 0.002691771136596799, 0.005963802337646484, -0.030538812279701233, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.2237934172153473, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 2.2949, "q": [-0.3138575851917267, -0.014022288843989372, 0.015708694234490395, 0.6320427060127258, -0.3265175223350525, 0.021091459318995476, -0.32300183176994324, 0.029929669573903084, -0.014650734141469002, 0.6241171360015869, -0.33665725588798523, -0.02104351669549942, 0.002691771136596799, 0.0059865922667086124, -0.030533980578184128, 0.2904856503009796, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023069633170962334, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 2.3118, "q": [-0.31383201479911804, -0.014005245640873909, 0.015722084790468216, 0.6320512294769287, -0.3265175223350525, 0.021091459318995476, -0.32299330830574036, 0.02991262450814247, -0.014623950235545635, 0.6241341829299927, -0.3366490602493286, -0.021058116108179092, 0.002758730435743928, 0.005956202279776335, -0.030533870682120323, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.0409141443669796, 0.2950636148452759, -0.2237934172153473, 0.023081617429852486, 0.9780805706977844, -0.18353840708732605, 0.05243098363280296, 0.008856342174112797]} +{"t": 2.3285, "q": [-0.31383201479911804, -0.014005245640873909, 0.015708694234490395, 0.6320427060127258, -0.3265175223350525, 0.021105969324707985, -0.32299330830574036, 0.029929669573903084, -0.014623950235545635, 0.6241597533226013, -0.33666136860847473, -0.02103622630238533, 0.002718554809689522, 0.006039750762283802, -0.03048994392156601, 0.2905096113681793, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.29507559537887573, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.3453, "q": [-0.3138405382633209, -0.01401376724243164, 0.015708694234490395, 0.6320512294769287, -0.3265092670917511, 0.02110605128109455, -0.32299330830574036, 0.029921147972345352, -0.014623950235545635, 0.6241597533226013, -0.3366490602493286, -0.02104359306395054, 0.0026783791836351156, 0.005978997331112623, -0.03053886815905571, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.04092612862586975, 0.29507559537887573, -0.2237934172153473, 0.023093601688742638, 0.9780925512313843, -0.1835503876209259, 0.05241899937391281, 0.008844357915222645]} +{"t": 2.3621, "q": [-0.31383201479911804, -0.014022288843989372, 0.015722084790468216, 0.6320597529411316, -0.326513409614563, 0.021098745986819267, -0.32299330830574036, 0.029921147972345352, -0.014650734141469002, 0.6241597533226013, -0.33666548132896423, -0.021014412865042686, 0.002718554809689522, 0.005986575968563557, -0.030504493042826653, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.0409141443669796, 0.2950636148452759, -0.22380541265010834, 0.02310558594763279, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 2.3788, "q": [-0.31383201479911804, -0.014005245640873909, 0.015695301815867424, 0.6320512294769287, -0.326513409614563, 0.021098745986819267, -0.3229847848415375, 0.029921147972345352, -0.014637341722846031, 0.6241512298583984, -0.33666959404945374, -0.021036185324192047, 0.0026783791836351156, 0.005994176026433706, -0.030509434640407562, 0.2904856503009796, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15758058428764343, 0.06169478967785835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.1835503876209259, 0.05243098363280296, 0.008856342174112797]} +{"t": 2.3956, "q": [-0.3138405382633209, -0.013996722176671028, 0.015708694234490395, 0.6320597529411316, -0.326513409614563, 0.021098745986819267, -0.32299330830574036, 0.029929669573903084, -0.014623950235545635, 0.6241512298583984, -0.33667367696762085, -0.02104341797530651, 0.002691771136596799, 0.005986581556499004, -0.030514322221279144, 0.2904856503009796, 0.2159797102212906, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835503876209259, 0.05243098363280296, 0.008868326433002949]} +{"t": 2.4123, "q": [-0.3138405382633209, -0.013996722176671028, 0.015722084790468216, 0.6320597529411316, -0.3265175223350525, 0.021091459318995476, -0.3229847848415375, 0.029929669573903084, -0.014610557816922665, 0.6241427063941956, -0.33666136860847473, -0.021021703258156776, 0.002758730435743928, 0.005956191103905439, -0.03051421232521534, 0.2905096113681793, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835503876209259, 0.05243098363280296, 0.008856342174112797]} +{"t": 2.429, "q": [-0.3138575851917267, -0.013996722176671028, 0.015708694234490395, 0.6320512294769287, -0.3265175223350525, 0.021105969324707985, -0.3229762613773346, 0.02991262450814247, -0.014597166329622269, 0.6241597533226013, -0.33666959404945374, -0.021007122471928596, 0.0027453387156128883, 0.006039745174348354, -0.03048011288046837, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.15758058428764343, 0.06169478967785835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835503876209259, 0.05240701511502266, 0.008868326433002949]} +{"t": 2.4458, "q": [-0.31387463212013245, -0.01401376724243164, 0.015695301815867424, 0.6320512294769287, -0.3265175223350525, 0.021105969324707985, -0.3230103552341461, 0.029921147972345352, -0.014623950235545635, 0.6241427063941956, -0.33669009804725647, -0.021014254540205002, 0.0027453387156128883, 0.006009371485561132, -0.03050949051976204, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15758058428764343, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 2.4627, "q": [-0.3138575851917267, -0.013996722176671028, 0.015708694234490395, 0.6320597529411316, -0.3265175223350525, 0.021091459318995476, -0.32299330830574036, 0.029929669573903084, -0.014623950235545635, 0.6241512298583984, -0.33666548132896423, -0.02102893590927124, 0.0026783791836351156, 0.005956196691840887, -0.03052404150366783, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.1835503876209259, 0.05243098363280296, 0.008844357915222645]} +{"t": 2.4794, "q": [-0.31387463212013245, -0.014005245640873909, 0.015748869627714157, 0.6320597529411316, -0.326513409614563, 0.021098745986819267, -0.32299330830574036, 0.029921147972345352, -0.014597166329622269, 0.6241512298583984, -0.33667367696762085, -0.02101435326039791, 0.002758730435743928, 0.005986581556499004, -0.030514322221279144, 0.2904856503009796, 0.21594375371932983, -0.005201153922826052, 0.9791951179504395, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.1835264265537262, 0.05241899937391281, 0.008844357915222645]} +{"t": 2.4962, "q": [-0.31387463212013245, -0.014005245640873909, 0.015722084790468216, 0.6320597529411316, -0.3265175223350525, 0.021091459318995476, -0.32299330830574036, 0.029921147972345352, -0.014623950235545635, 0.6241512298583984, -0.33667367696762085, -0.02101435326039791, 0.002731946762651205, 0.005986575968563557, -0.030504493042826653, 0.2904616594314575, 0.2159797102212906, -0.005213138181716204, 0.9791951179504395, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835503876209259, 0.052395034581422806, 0.008856342174112797]} +{"t": 2.5129, "q": [-0.31386610865592957, -0.014005245640873909, 0.015722084790468216, 0.6320597529411316, -0.326513409614563, 0.021098745986819267, -0.32300183176994324, 0.029921147972345352, -0.014610557816922665, 0.6241427063941956, -0.33667367696762085, -0.02101435326039791, 0.0026783791836351156, 0.005994176026433706, -0.030509434640407562, 0.2904616594314575, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.0409141443669796, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835503876209259, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.5296, "q": [-0.3138490617275238, -0.013996722176671028, 0.015722084790468216, 0.6320512294769287, -0.3265092670917511, 0.02110605128109455, -0.32299330830574036, 0.02990410290658474, -0.014597166329622269, 0.6241427063941956, -0.33667778968811035, -0.021021604537963867, 0.002718554809689522, 0.006032150238752365, -0.03048500046133995, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.04087819159030914, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.1835503876209259, 0.05243098363280296, 0.008856342174112797]} +{"t": 2.5464, "q": [-0.3138490617275238, -0.01401376724243164, 0.015722084790468216, 0.6320597529411316, -0.326513409614563, 0.021098745986819267, -0.3230103552341461, 0.029921147972345352, -0.014597166329622269, 0.6241512298583984, -0.33666959404945374, -0.021007122471928596, 0.0027051628567278385, 0.0060017709620296955, -0.03050454892218113, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9791830778121948, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.5631, "q": [-0.3138575851917267, -0.01401376724243164, 0.015722084790468216, 0.6320597529411316, -0.3265092968940735, 0.02109152264893055, -0.32300183176994324, 0.029921147972345352, -0.014610557816922665, 0.6241427063941956, -0.33666136860847473, -0.021050767973065376, 0.002718554809689522, 0.006009371485561132, -0.03050949051976204, 0.29044967889785767, 0.21596772968769073, -0.0051891696639359, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.052442967891693115, 0.008856342174112797]} +{"t": 2.5798, "q": [-0.3138490617275238, -0.01401376724243164, 0.015708694234490395, 0.6320597529411316, -0.3265092670917511, 0.02110605128109455, -0.32300183176994324, 0.02990410290658474, -0.014610557816922665, 0.6241427063941956, -0.3366572856903076, -0.021014470607042313, 0.002718554809689522, 0.005986575968563557, -0.030504493042826653, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.040890175849199295, 0.295051634311676, -0.2238173931837082, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.5966, "q": [-0.3138575851917267, -0.014005245640873909, 0.015735477209091187, 0.6320597529411316, -0.3265133798122406, 0.02111327461898327, -0.32300183176994324, 0.02991262450814247, -0.014623950235545635, 0.6241512298583984, -0.33666136860847473, -0.02103622630238533, 0.0027051628567278385, 0.0060017709620296955, -0.03050454892218113, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.157616525888443, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.6133, "q": [-0.31382349133491516, -0.014022288843989372, 0.015748869627714157, 0.6320512294769287, -0.326513409614563, 0.021098745986819267, -0.32300183176994324, 0.02991262450814247, -0.014623950235545635, 0.6241427063941956, -0.33665725588798523, -0.02104351669549942, 0.002731946762651205, 0.005978986620903015, -0.030519209802150726, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06169478967785835, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.1835503876209259, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.6301, "q": [-0.3138405382633209, -0.014005245640873909, 0.015735477209091187, 0.6320597529411316, -0.3265175223350525, 0.021091459318995476, -0.32299330830574036, 0.029929669573903084, -0.014610557816922665, 0.6241512298583984, -0.3366490602493286, -0.021058116108179092, 0.0026649872306734324, 0.005971380975097418, -0.030504437163472176, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.061658840626478195, -0.04090216010808945, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.647, "q": [-0.31382349133491516, -0.01401376724243164, 0.015695301815867424, 0.6320512294769287, -0.326521635055542, 0.021098682656884193, -0.3229762613773346, 0.029921147972345352, -0.014623950235545635, 0.6241427063941956, -0.33666956424713135, -0.02102164551615715, 0.002691771136596799, 0.0060245501808822155, -0.03048005886375904, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.0409141443669796, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.6637, "q": [-0.3138575851917267, -0.014005245640873909, 0.015708694234490395, 0.6320597529411316, -0.326513409614563, 0.021098745986819267, -0.3229847848415375, 0.029921147972345352, -0.014623950235545635, 0.6241512298583984, -0.33666136860847473, -0.021021703258156776, 0.0026783791836351156, 0.006009365897625685, -0.030499661341309547, 0.2904736399650574, 0.21596772968769073, -0.005225121974945068, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 2.6805, "q": [-0.31382349133491516, -0.014005245640873909, 0.015722084790468216, 0.6320597529411316, -0.326513409614563, 0.021098745986819267, -0.3229847848415375, 0.029921147972345352, -0.014623950235545635, 0.6241341829299927, -0.33666548132896423, -0.02102893590927124, 0.002731946762651205, 0.006009365897625685, -0.030499661341309547, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 2.6972, "q": [-0.31382349133491516, -0.013996722176671028, 0.015722084790468216, 0.6320512294769287, -0.326521635055542, 0.021084172651171684, -0.32299330830574036, 0.029938191175460815, -0.014623950235545635, 0.6241512298583984, -0.33666959404945374, -0.021036185324192047, 0.002731946762651205, 0.006039750762283802, -0.03048994392156601, 0.2905096113681793, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18353840708732605, 0.05243098363280296, 0.008844357915222645]} +{"t": 2.714, "q": [-0.31382349133491516, -0.014022288843989372, 0.015735477209091187, 0.6320597529411316, -0.3265133798122406, 0.02111327461898327, -0.32299330830574036, 0.029938191175460815, -0.014623950235545635, 0.6241512298583984, -0.33666548132896423, -0.02102893590927124, 0.002691771136596799, 0.006115698721259832, -0.030441073700785637, 0.2905096113681793, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 2.7308, "q": [-0.31383201479911804, -0.013979678973555565, 0.015695301815867424, 0.6320597529411316, -0.3265175223350525, 0.021091459318995476, -0.3229847848415375, 0.029946712777018547, -0.014637341722846031, 0.6241427063941956, -0.33668190240859985, -0.02099977247416973, 0.002758730435743928, 0.006100498139858246, -0.030431188642978668, 0.2905096113681793, 0.2159797102212906, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.29507559537887573, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008856342174112797]} +{"t": 2.7476, "q": [-0.31383201479911804, -0.013979678973555565, 0.015708694234490395, 0.6320597529411316, -0.3265092670917511, 0.02110605128109455, -0.32300183176994324, 0.029929669573903084, -0.014623950235545635, 0.6241341829299927, -0.33667778968811035, -0.021021604537963867, 0.0026783791836351156, 0.006054929457604885, -0.030460510402917862, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 2.7643, "q": [-0.3138405382633209, -0.013988200575113297, 0.015695301815867424, 0.6320682764053345, -0.326521635055542, 0.021098682656884193, -0.32300183176994324, 0.029921147972345352, -0.014637341722846031, 0.6241341829299927, -0.33667778968811035, -0.020992539823055267, 0.0027051628567278385, 0.006062529515475035, -0.03046545200049877, 0.2904616594314575, 0.21596772968769073, -0.005225121974945068, 0.9792070984840393, 0.1576405018568039, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.22378143668174744, 0.023081617429852486, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008832373656332493]} +{"t": 2.7812, "q": [-0.3138405382633209, -0.013962633907794952, 0.015708694234490395, 0.6320597529411316, -0.3265175223350525, 0.021091459318995476, -0.3230103552341461, 0.029921147972345352, -0.014623950235545635, 0.6241256594657898, -0.33667367696762085, -0.02101435326039791, 0.0027051628567278385, 0.006108098663389683, -0.030436130240559578, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9781045317649841, -0.18356238305568695, 0.052442967891693115, 0.008844357915222645]} +{"t": 2.7981, "q": [-0.31386610865592957, -0.013971157371997833, 0.015681909397244453, 0.6320597529411316, -0.3265298902988434, 0.02109861932694912, -0.3230273723602295, 0.02991262450814247, -0.014623950235545635, 0.6241256594657898, -0.33669009804725647, -0.021014254540205002, 0.0026649872306734324, 0.006062523927539587, -0.03045562282204628, 0.29044967889785767, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04087819159030914, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.8148, "q": [-0.31387463212013245, -0.013971157371997833, 0.015681909397244453, 0.6320512294769287, -0.3265257775783539, 0.02110590599477291, -0.3230273723602295, 0.029929669573903084, -0.014623950235545635, 0.6241256594657898, -0.33667367696762085, -0.02101435326039791, 0.0026649872306734324, 0.006077719386667013, -0.030455678701400757, 0.2904616594314575, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.8316, "q": [-0.31387463212013245, -0.013971157371997833, 0.015695301815867424, 0.6320512294769287, -0.326521635055542, 0.021113192662596703, -0.3230273723602295, 0.029921147972345352, -0.014610557816922665, 0.624108612537384, -0.33669009804725647, -0.021014254540205002, 0.0026649872306734324, 0.006077730096876621, -0.03047533705830574, 0.2904616594314575, 0.2159797102212906, -0.005213138181716204, 0.9791951179504395, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.052395034581422806, 0.008868326433002949]} +{"t": 2.8483, "q": [-0.31387463212013245, -0.013971157371997833, 0.015695301815867424, 0.6320597529411316, -0.326521635055542, 0.021098682656884193, -0.323061466217041, 0.029929669573903084, -0.014623950235545635, 0.6241256594657898, -0.3366941809654236, -0.021021487191319466, 0.002691771136596799, 0.006077730096876621, -0.03047533705830574, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 2.8651, "q": [-0.3139002025127411, -0.013971157371997833, 0.015668518841266632, 0.6320512294769287, -0.3265257775783539, 0.02110590599477291, -0.32305294275283813, 0.029921147972345352, -0.014597166329622269, 0.6241341829299927, -0.3367023766040802, -0.02102142944931984, 0.002691771136596799, 0.006039750762283802, -0.03048994392156601, 0.2904856503009796, 0.2159557342529297, -0.005225121974945068, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 2.8818, "q": [-0.3138916790485382, -0.013962633907794952, 0.015681909397244453, 0.6320597529411316, -0.3265257775783539, 0.02110590599477291, -0.323061466217041, 0.029929669573903084, -0.014597166329622269, 0.6240915656089783, -0.3367023766040802, -0.02102142944931984, 0.002758730435743928, 0.006032150238752365, -0.03048500046133995, 0.2905096113681793, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05243098363280296, 0.008856342174112797]} +{"t": 2.8985, "q": [-0.31390872597694397, -0.01395411230623722, 0.015681909397244453, 0.6320512294769287, -0.3265298902988434, 0.02109861932694912, -0.32305294275283813, 0.029921147972345352, -0.014610557816922665, 0.6241000890731812, -0.33669009804725647, -0.021014254540205002, 0.0026783791836351156, 0.006032155826687813, -0.030494829639792442, 0.2905096113681793, 0.2159557342529297, -0.005213138181716204, 0.9792190790176392, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 2.9152, "q": [-0.31390872597694397, -0.013971157371997833, 0.015681909397244453, 0.6320512294769287, -0.3265257775783539, 0.02112041600048542, -0.3230699896812439, 0.02991262450814247, -0.014583774842321873, 0.6241000890731812, -0.33669009804725647, -0.021014254540205002, 0.0026649872306734324, 0.006032150238752365, -0.03048500046133995, 0.2905096113681793, 0.21596772968769073, -0.005225121974945068, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 2.932, "q": [-0.3139342963695526, -0.013962633907794952, 0.015695301815867424, 0.6320512294769287, -0.3265257775783539, 0.02110590599477291, -0.3230699896812439, 0.029921147972345352, -0.014570382423698902, 0.6241000890731812, -0.3366941809654236, -0.021021487191319466, 0.0026649872306734324, 0.005941017996519804, -0.03055347315967083, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04092612862586975, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 2.9487, "q": [-0.31391724944114685, -0.013979678973555565, 0.015668518841266632, 0.6320597529411316, -0.3265298902988434, 0.02109861932694912, -0.3230699896812439, 0.02990410290658474, -0.014583774842321873, 0.6241000890731812, -0.3366983234882355, -0.02099965512752533, 0.0026783791836351156, 0.005948612932115793, -0.030548587441444397, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15758058428764343, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 2.9655, "q": [-0.3139002025127411, -0.013971157371997833, 0.015708694234490395, 0.6320597529411316, -0.3265092670917511, 0.02110605128109455, -0.32304441928863525, 0.02990410290658474, -0.014597166329622269, 0.624108612537384, -0.33669009804725647, -0.021014254540205002, 0.0027051628567278385, 0.005903043784201145, -0.03057790920138359, 0.2905096113681793, 0.21596772968769073, -0.005213138181716204, 0.9791830778121948, 0.15762852132320404, 0.06167082488536835, -0.040890175849199295, 0.295051634311676, -0.22378143668174744, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05240701511502266, 0.008844357915222645]} +{"t": 2.9824, "q": [-0.3138916790485382, -0.013988200575113297, 0.015708694234490395, 0.6320512294769287, -0.3265175223350525, 0.021105969324707985, -0.3230358958244324, 0.02990410290658474, -0.014583774842321873, 0.624108612537384, -0.33669009804725647, -0.021014254540205002, 0.0027453387156128883, 0.005903043784201145, -0.03057790920138359, 0.2904616594314575, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.157616525888443, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.052442967891693115, 0.008844357915222645]} +{"t": 2.9991, "q": [-0.31390872597694397, -0.013979678973555565, 0.015695301815867424, 0.6320597529411316, -0.326513409614563, 0.021098745986819267, -0.3230273723602295, 0.02991262450814247, -0.014583774842321873, 0.624108612537384, -0.33668598532676697, -0.021036067977547646, 0.002691771136596799, 0.0058954437263309956, -0.030572965741157532, 0.2904856503009796, 0.21594375371932983, -0.005201153922826052, 0.9791951179504395, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29507559537887573, -0.2238173931837082, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 3.0158, "q": [-0.31390872597694397, -0.013988200575113297, 0.015708694234490395, 0.6320597529411316, -0.326521635055542, 0.021084172651171684, -0.3230273723602295, 0.029921147972345352, -0.014597166329622269, 0.624108612537384, -0.33668187260627747, -0.02102883718907833, 0.0027855143416672945, 0.005925828590989113, -0.030563248321413994, 0.29049763083457947, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.040890175849199295, 0.29507559537887573, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05243098363280296, 0.008868326433002949]} +{"t": 3.0327, "q": [-0.3138831555843353, -0.014005245640873909, 0.015695301815867424, 0.6320597529411316, -0.326521635055542, 0.021098682656884193, -0.3230188488960266, 0.029921147972345352, -0.014637341722846031, 0.6241171360015869, -0.33666548132896423, -0.02105799876153469, 0.002691771136596799, 0.006032150238752365, -0.03048500046133995, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 3.0494, "q": [-0.31387463212013245, -0.014005245640873909, 0.015722084790468216, 0.6320597529411316, -0.3265175223350525, 0.021091459318995476, -0.3230103552341461, 0.029921147972345352, -0.014623950235545635, 0.6241341829299927, -0.33666548132896423, -0.02105799876153469, 0.002758730435743928, 0.005918222479522228, -0.030548475682735443, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9791830778121948, 0.15758058428764343, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.2237934172153473, 0.023093601688742638, 0.9780685901641846, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 3.0662, "q": [-0.31387463212013245, -0.013979678973555565, 0.015708694234490395, 0.6320597529411316, -0.3265175223350525, 0.021091459318995476, -0.32300183176994324, 0.02991262450814247, -0.014623950235545635, 0.624108612537384, -0.33666136860847473, -0.021065307781100273, 0.002691771136596799, 0.005956202279776335, -0.030533870682120323, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15756858885288239, 0.0616828091442585, -0.040890175849199295, 0.2950875759124756, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.0831, "q": [-0.31386610865592957, -0.013988200575113297, 0.015722084790468216, 0.6320597529411316, -0.326513409614563, 0.021098745986819267, -0.3230103552341461, 0.02991262450814247, -0.014623950235545635, 0.624108612537384, -0.33666956424713135, -0.021065231412649155, 0.002731946762651205, 0.006009365897625685, -0.030499661341309547, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.0409141443669796, 0.295051634311676, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.0998, "q": [-0.3138831555843353, -0.013996722176671028, 0.015708694234490395, 0.6320682764053345, -0.3265175223350525, 0.021105969324707985, -0.32300183176994324, 0.02991262450814247, -0.014623950235545635, 0.6241171360015869, -0.33667367696762085, -0.02104341797530651, 0.002731946762651205, 0.006009365897625685, -0.030499661341309547, 0.2904616594314575, 0.2159797102212906, -0.00523710623383522, 0.9791951179504395, 0.15762852132320404, 0.06167082488536835, -0.0409141443669796, 0.2950636148452759, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 3.1165, "q": [-0.31387463212013245, -0.013996722176671028, 0.015708694234490395, 0.6320512294769287, -0.326521635055542, 0.021098682656884193, -0.32299330830574036, 0.02991262450814247, -0.014637341722846031, 0.624108612537384, -0.33668187260627747, -0.02102883718907833, 0.002718554809689522, 0.006024555303156376, -0.030489888042211533, 0.2904616594314575, 0.2159557342529297, -0.00523710623383522, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.052395034581422806, 0.008856342174112797]} +{"t": 3.1335, "q": [-0.31387463212013245, -0.013996722176671028, 0.015708694234490395, 0.6320512294769287, -0.326513409614563, 0.021098745986819267, -0.3230103552341461, 0.029929669573903084, -0.014623950235545635, 0.624108612537384, -0.33668598532676697, -0.021036067977547646, 0.002718554809689522, 0.005971397273242474, -0.0305339265614748, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 3.1502, "q": [-0.3138831555843353, -0.014005245640873909, 0.015681909397244453, 0.6320512294769287, -0.3265257775783539, 0.02110590599477291, -0.32300183176994324, 0.029921147972345352, -0.014637341722846031, 0.6241171360015869, -0.33669009804725647, -0.021014254540205002, 0.002611419651657343, 0.006009371485561132, -0.03050949051976204, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06169478967785835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.1835743635892868, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.1669, "q": [-0.3138831555843353, -0.013971157371997833, 0.015722084790468216, 0.6320512294769287, -0.326521635055542, 0.021084172651171684, -0.3229847848415375, 0.029921147972345352, -0.014610557816922665, 0.6240915656089783, -0.33668598532676697, -0.021036067977547646, 0.002718554809689522, 0.005956196691840887, -0.03052404150366783, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05243098363280296, 0.008832373656332493]} +{"t": 3.1837, "q": [-0.31386610865592957, -0.013996722176671028, 0.015708694234490395, 0.6320512294769287, -0.3265257775783539, 0.021091395989060402, -0.3230103552341461, 0.029929669573903084, -0.014597166329622269, 0.6241000890731812, -0.33669009804725647, -0.021014254540205002, 0.002691771136596799, 0.0060017709620296955, -0.03050454892218113, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950875759124756, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 3.2004, "q": [-0.3138831555843353, -0.013988200575113297, 0.015681909397244453, 0.6320512294769287, -0.326521635055542, 0.021098682656884193, -0.32299330830574036, 0.02991262450814247, -0.014583774842321873, 0.6241000890731812, -0.33669009804725647, -0.021028777584433556, 0.0026649872306734324, 0.005978959146887064, -0.03047006204724312, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1576405018568039, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.2172, "q": [-0.3138831555843353, -0.013971157371997833, 0.015695301815867424, 0.6320512294769287, -0.3265257775783539, 0.02110590599477291, -0.32300183176994324, 0.029921147972345352, -0.014597166329622269, 0.6240915656089783, -0.33668598532676697, -0.021021544933319092, 0.002731946762651205, 0.005956180393695831, -0.030494552105665207, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008832373656332493]} +{"t": 3.2339, "q": [-0.31387463212013245, -0.013979678973555565, 0.015695301815867424, 0.6320597529411316, -0.3265257775783539, 0.02110590599477291, -0.32300183176994324, 0.02991262450814247, -0.014583774842321873, 0.6240915656089783, -0.33669009804725647, -0.021014254540205002, 0.002691771136596799, 0.0059637753292918205, -0.030489666387438774, 0.2904856503009796, 0.21599169075489044, -0.00523710623383522, 0.9791951179504395, 0.15758058428764343, 0.06167082488536835, -0.04090216010808945, 0.29507559537887573, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.052395034581422806, 0.008856342174112797]} +{"t": 3.2506, "q": [-0.3139002025127411, -0.013979678973555565, 0.015695301815867424, 0.6320597529411316, -0.326521635055542, 0.021098682656884193, -0.3230358958244324, 0.029921147972345352, -0.014597166329622269, 0.6240830421447754, -0.33669009804725647, -0.021014254540205002, 0.0027453387156128883, 0.006047328934073448, -0.030455566942691803, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05240701511502266, 0.008856342174112797]} +{"t": 3.2675, "q": [-0.31387463212013245, -0.013971157371997833, 0.015681909397244453, 0.6320512294769287, -0.3265257775783539, 0.02110590599477291, -0.32300183176994324, 0.02991262450814247, -0.014597166329622269, 0.6240830421447754, -0.33669009804725647, -0.021028777584433556, 0.0026783791836351156, 0.006001760251820087, -0.030484890565276146, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.157616525888443, 0.0616828091442585, -0.04090216010808945, 0.2950875759124756, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 3.2842, "q": [-0.3138831555843353, -0.013962633907794952, 0.015681909397244453, 0.6320512294769287, -0.326521635055542, 0.021113192662596703, -0.3230103552341461, 0.029921147972345352, -0.014610557816922665, 0.6240745186805725, -0.33669009804725647, -0.021014254540205002, 0.002691771136596799, 0.0060245501808822155, -0.03048005886375904, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.2950875759124756, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 3.301, "q": [-0.3138916790485382, -0.013962633907794952, 0.015695301815867424, 0.6320427060127258, -0.326521635055542, 0.021098682656884193, -0.3230103552341461, 0.02991262450814247, -0.014597166329622269, 0.6240745186805725, -0.33668598532676697, -0.021007023751735687, 0.002731946762651205, 0.006001760251820087, -0.030484890565276146, 0.2904736399650574, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18353840708732605, 0.05240701511502266, 0.008856342174112797]} +{"t": 3.3177, "q": [-0.31387463212013245, -0.013979678973555565, 0.015695301815867424, 0.6320597529411316, -0.3265175223350525, 0.021105969324707985, -0.32300183176994324, 0.02991262450814247, -0.014623950235545635, 0.6240745186805725, -0.33668598532676697, -0.021021544933319092, 0.002718554809689522, 0.006062523927539587, -0.03045562282204628, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.06169478967785835, -0.04090216010808945, 0.295051634311676, -0.2237934172153473, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.3344, "q": [-0.31387463212013245, -0.013962633907794952, 0.015695301815867424, 0.6320512294769287, -0.3265175223350525, 0.021105969324707985, -0.3230103552341461, 0.02991262450814247, -0.014610557816922665, 0.6240830421447754, -0.33669009804725647, -0.021014254540205002, 0.0027453387156128883, 0.006024544592946768, -0.03047022968530655, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 3.3512, "q": [-0.31387463212013245, -0.013971157371997833, 0.015695301815867424, 0.6320597529411316, -0.3265092670917511, 0.02110605128109455, -0.3230103552341461, 0.029921147972345352, -0.014610557816922665, 0.6240830421447754, -0.33668598532676697, -0.021021544933319092, 0.0027453387156128883, 0.006016949657350779, -0.030475115403532982, 0.2904856503009796, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.0409141443669796, 0.2950636148452759, -0.2237934172153473, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008856342174112797]} +{"t": 3.3679, "q": [-0.31386610865592957, -0.013979678973555565, 0.015708694234490395, 0.6320512294769287, -0.326521635055542, 0.021098682656884193, -0.32299330830574036, 0.029929669573903084, -0.014610557816922665, 0.6240915656089783, -0.33666956424713135, -0.0210507083684206, 0.002691771136596799, 0.0060245501808822155, -0.03048005886375904, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06169478967785835, -0.040890175849199295, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.3846, "q": [-0.31387463212013245, -0.013979678973555565, 0.015695301815867424, 0.6320512294769287, -0.3265175223350525, 0.021091459318995476, -0.32299330830574036, 0.029921147972345352, -0.014623950235545635, 0.6240745186805725, -0.33667367696762085, -0.02104341797530651, 0.0027051628567278385, 0.00605491828173399, -0.03044085204601288, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.15762852132320404, 0.06167082488536835, -0.0409141443669796, 0.295051634311676, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.052442967891693115, 0.008832373656332493]} +{"t": 3.4014, "q": [-0.31387463212013245, -0.013971157371997833, 0.015681909397244453, 0.6320682764053345, -0.3265175223350525, 0.021091459318995476, -0.32299330830574036, 0.029921147972345352, -0.014623950235545635, 0.6240830421447754, -0.33668187260627747, -0.02102883718907833, 0.002651595277711749, 0.0060473233461380005, -0.03044573776423931, 0.2904856503009796, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04092612862586975, 0.29507559537887573, -0.22378143668174744, 0.023093601688742638, 0.9780685901641846, -0.18356238305568695, 0.05240701511502266, 0.008856342174112797]} +{"t": 3.4181, "q": [-0.31387463212013245, -0.013979678973555565, 0.015708694234490395, 0.6320682764053345, -0.3265175223350525, 0.021105969324707985, -0.32300183176994324, 0.029921147972345352, -0.014623950235545635, 0.6240830421447754, -0.33668598532676697, -0.021036067977547646, 0.0027453387156128883, 0.006100492551922798, -0.030421359464526176, 0.2904616594314575, 0.2159797102212906, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.0409141443669796, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.4348, "q": [-0.31387463212013245, -0.013971157371997833, 0.015695301815867424, 0.6320512294769287, -0.3265175223350525, 0.021091459318995476, -0.32299330830574036, 0.029929669573903084, -0.014623950235545635, 0.6240830421447754, -0.33668187260627747, -0.02102883718907833, 0.0027051628567278385, 0.0060473233461380005, -0.03044573776423931, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.040890175849199295, 0.29507559537887573, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.4515, "q": [-0.31386610865592957, -0.013971157371997833, 0.015695301815867424, 0.6320597529411316, -0.3265175223350525, 0.021091459318995476, -0.3229847848415375, 0.029921147972345352, -0.014597166329622269, 0.6240745186805725, -0.33668598532676697, -0.021036067977547646, 0.002758730435743928, 0.006070124451071024, -0.03046056628227234, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.02310558594763279, 0.978116512298584, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 3.4683, "q": [-0.31387463212013245, -0.01395411230623722, 0.015708694234490395, 0.6320682764053345, -0.326513409614563, 0.021084235981106758, -0.3229847848415375, 0.029921147972345352, -0.014597166329622269, 0.6240915656089783, -0.33669009804725647, -0.021014254540205002, 0.0026783791836351156, 0.00605491828173399, -0.03044085204601288, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792190790176392, 0.1575925648212433, 0.06169478967785835, -0.04092612862586975, 0.295051634311676, -0.22380541265010834, 0.02310558594763279, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 3.485, "q": [-0.3138575851917267, -0.01395411230623722, 0.015681909397244453, 0.6320512294769287, -0.3265175223350525, 0.021105969324707985, -0.32299330830574036, 0.029921147972345352, -0.014623950235545635, 0.6240745186805725, -0.33668598532676697, -0.021036067977547646, 0.0027051628567278385, 0.006161262281239033, -0.030401920899748802, 0.2904856503009796, 0.2159557342529297, -0.005201153922826052, 0.9791830778121948, 0.15756858885288239, 0.06167082488536835, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05240701511502266, 0.008868326433002949]} +{"t": 3.5017, "q": [-0.3138490617275238, -0.01395411230623722, 0.015695301815867424, 0.6320597529411316, -0.326513409614563, 0.021098745986819267, -0.32299330830574036, 0.029929669573903084, -0.014637341722846031, 0.6240830421447754, -0.33668187260627747, -0.02102883718907833, 0.0026783791836351156, 0.006199228577315807, -0.030367659404873848, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950875759124756, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.1835983246564865, 0.05240701511502266, 0.008856342174112797]} +{"t": 3.5184, "q": [-0.3138490617275238, -0.013962633907794952, 0.015681909397244453, 0.6320512294769287, -0.326521635055542, 0.021098682656884193, -0.3229762613773346, 0.029929669573903084, -0.014623950235545635, 0.6240489482879639, -0.3366941809654236, -0.021021487191319466, 0.0026783791836351156, 0.006184038706123829, -0.030377432703971863, 0.2904616594314575, 0.2159557342529297, -0.005201153922826052, 0.9791951179504395, 0.15760454535484314, 0.06169478967785835, -0.04090216010808945, 0.29507559537887573, -0.2237934172153473, 0.023093601688742638, 0.9781045317649841, -0.1835503876209259, 0.05243098363280296, 0.008856342174112797]} +{"t": 3.5352, "q": [-0.31387463212013245, -0.013971157371997833, 0.015708694234490395, 0.6320512294769287, -0.326521635055542, 0.021098682656884193, -0.3229847848415375, 0.029921147972345352, -0.014597166329622269, 0.624040424823761, -0.33668598532676697, -0.021021544933319092, 0.002691771136596799, 0.006085303146392107, -0.03043113276362419, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9791830778121948, 0.15756858885288239, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008832373656332493]} +{"t": 3.5519, "q": [-0.31386610865592957, -0.013971157371997833, 0.015695301815867424, 0.6320512294769287, -0.3265257775783539, 0.02110590599477291, -0.32299330830574036, 0.029921147972345352, -0.014623950235545635, 0.6240148544311523, -0.33669009804725647, -0.021014254540205002, 0.0026783791836351156, 0.006146066822111607, -0.030401866883039474, 0.2904736399650574, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.29507559537887573, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 3.5686, "q": [-0.31387463212013245, -0.013962633907794952, 0.015668518841266632, 0.6320512294769287, -0.3265257775783539, 0.02110590599477291, -0.3229847848415375, 0.029921147972345352, -0.014597166329622269, 0.6240063309669495, -0.33669009804725647, -0.021014254540205002, 0.0026649872306734324, 0.00616884371265769, -0.030377376824617386, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.0409141443669796, 0.2950636148452759, -0.22378143668174744, 0.02310558594763279, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 3.5854, "q": [-0.31387463212013245, -0.01394559070467949, 0.01565512642264366, 0.6320512294769287, -0.3265298902988434, 0.02111312933266163, -0.3229847848415375, 0.029921147972345352, -0.014623950235545635, 0.6240063309669495, -0.3366941809654236, -0.021021487191319466, 0.002691771136596799, 0.006161251105368137, -0.03038226254284382, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.2950875759124756, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.6021, "q": [-0.3138916790485382, -0.01395411230623722, 0.015681909397244453, 0.6320512294769287, -0.3265257775783539, 0.02110590599477291, -0.3229762613773346, 0.029921147972345352, -0.014597166329622269, 0.6239892840385437, -0.33669009804725647, -0.021014254540205002, 0.0026783791836351156, 0.006100492551922798, -0.030421359464526176, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06169478967785835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 3.619, "q": [-0.3139002025127411, -0.01395411230623722, 0.015668518841266632, 0.6320512294769287, -0.3265298902988434, 0.02109861932694912, -0.32299330830574036, 0.029921147972345352, -0.014583774842321873, 0.6239892840385437, -0.3366941809654236, -0.021021487191319466, 0.0027051628567278385, 0.006108093075454235, -0.030426301062107086, 0.29049763083457947, 0.2159557342529297, -0.005201153922826052, 0.9791951179504395, 0.15758058428764343, 0.06167082488536835, -0.040890175849199295, 0.2950636148452759, -0.2237934172153473, 0.023081617429852486, 0.9780685901641846, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 3.6359, "q": [-0.3138916790485382, -0.01394559070467949, 0.015681909397244453, 0.6320427060127258, -0.3265257775783539, 0.02110590599477291, -0.3229847848415375, 0.029929669573903084, -0.014610557816922665, 0.6239892840385437, -0.33669009804725647, -0.021014254540205002, 0.0027051628567278385, 0.006100492551922798, -0.030421359464526176, 0.2904616594314575, 0.2159557342529297, -0.005225121974945068, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.040890175849199295, 0.2950875759124756, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 3.6527, "q": [-0.3139002025127411, -0.013962633907794952, 0.015668518841266632, 0.6320427060127258, -0.326521635055542, 0.021113192662596703, -0.32299330830574036, 0.029921147972345352, -0.014610557816922665, 0.6239551901817322, -0.33669009804725647, -0.021014254540205002, 0.002691771136596799, 0.0062144179828464985, -0.030357884243130684, 0.2904616594314575, 0.2159557342529297, -0.0051891696639359, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 3.6694, "q": [-0.3138916790485382, -0.01394559070467949, 0.015681909397244453, 0.632034182548523, -0.3265257775783539, 0.02110590599477291, -0.3229847848415375, 0.029929669573903084, -0.014623950235545635, 0.6239637136459351, -0.33668598532676697, -0.021036067977547646, 0.002758730435743928, 0.006138456054031849, -0.030377265065908432, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06169478967785835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.02310558594763279, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.6862, "q": [-0.31390872597694397, -0.013962633907794952, 0.015668518841266632, 0.6320427060127258, -0.3265175223350525, 0.021105969324707985, -0.3229847848415375, 0.029921147972345352, -0.014623950235545635, 0.6239722371101379, -0.33669009804725647, -0.021014254540205002, 0.002718554809689522, 0.006108087487518787, -0.030416471883654594, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.703, "q": [-0.3139002025127411, -0.01394559070467949, 0.015681909397244453, 0.6320427060127258, -0.3265257775783539, 0.02110590599477291, -0.3229762613773346, 0.02991262450814247, -0.014637341722846031, 0.6239637136459351, -0.33668598532676697, -0.021021544933319092, 0.002731946762651205, 0.006130883004516363, -0.03042146936058998, 0.2904736399650574, 0.21594375371932983, -0.005225121974945068, 0.9791830778121948, 0.1576405018568039, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.1835983246564865, 0.05241899937391281, 0.008868326433002949]} +{"t": 3.7197, "q": [-0.3138916790485382, -0.01394559070467949, 0.015681909397244453, 0.6320427060127258, -0.3265257775783539, 0.02110590599477291, -0.3229847848415375, 0.029921147972345352, -0.014623950235545635, 0.6239551901817322, -0.3366941809654236, -0.021021487191319466, 0.002718554809689522, 0.006115676835179329, -0.03040175512433052, 0.29044967889785767, 0.21596772968769073, -0.005201153922826052, 0.9791951179504395, 0.15765248239040375, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22378143668174744, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.7366, "q": [-0.3138916790485382, -0.01394559070467949, 0.015668518841266632, 0.632034182548523, -0.326521635055542, 0.021098682656884193, -0.32299330830574036, 0.029921147972345352, -0.014597166329622269, 0.6239722371101379, -0.33669009804725647, -0.021014254540205002, 0.0026783791836351156, 0.006168851628899574, -0.030387206003069878, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.7534, "q": [-0.3138831555843353, -0.01394559070467949, 0.015668518841266632, 0.632034182548523, -0.326521635055542, 0.021098682656884193, -0.3229762613773346, 0.029929669573903084, -0.014623950235545635, 0.6239637136459351, -0.3366941809654236, -0.021006964147090912, 0.002718554809689522, 0.0060928924940526485, -0.030416416004300117, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.061658840626478195, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008856342174112797]} +{"t": 3.7701, "q": [-0.31391724944114685, -0.01395411230623722, 0.015681909397244453, 0.632034182548523, -0.3265257775783539, 0.02110590599477291, -0.3229762613773346, 0.029929669573903084, -0.014610557816922665, 0.6239551901817322, -0.33669009804725647, -0.021014254540205002, 0.0026783791836351156, 0.006184038706123829, -0.030377432703971863, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792190790176392, 0.1575925648212433, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22378143668174744, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 3.7871, "q": [-0.31387463212013245, -0.01394559070467949, 0.01564173400402069, 0.6320512294769287, -0.326521635055542, 0.021113192662596703, -0.3229762613773346, 0.02991262450814247, -0.014610557816922665, 0.6239722371101379, -0.33669009804725647, -0.021028777584433556, 0.002691771136596799, 0.00622960738837719, -0.03034811094403267, 0.2904736399650574, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.0409141443669796, 0.2950875759124756, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 3.8038, "q": [-0.31387463212013245, -0.01395411230623722, 0.01565512642264366, 0.6320427060127258, -0.326521635055542, 0.021098682656884193, -0.3229762613773346, 0.029921147972345352, -0.014623950235545635, 0.6239551901817322, -0.33669009804725647, -0.021014254540205002, 0.002651595277711749, 0.006206817924976349, -0.030352942645549774, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15762852132320404, 0.06169478967785835, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835503876209259, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.8205, "q": [-0.31387463212013245, -0.01395411230623722, 0.01565512642264366, 0.632034182548523, -0.3265257775783539, 0.02110590599477291, -0.32295069098472595, 0.029929669573903084, -0.014637341722846031, 0.6239466667175293, -0.33668598532676697, -0.021036067977547646, 0.0026649872306734324, 0.006138456054031849, -0.030377265065908432, 0.2905096113681793, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.0409141443669796, 0.2950875759124756, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 3.8376, "q": [-0.3138831555843353, -0.01394559070467949, 0.015668518841266632, 0.632034182548523, -0.326521635055542, 0.021098682656884193, -0.32295921444892883, 0.029921147972345352, -0.014610557816922665, 0.6239466667175293, -0.33669009804725647, -0.021014254540205002, 0.0027051628567278385, 0.006191617343574762, -0.030343057587742805, 0.29049763083457947, 0.21596772968769073, -0.005225121974945068, 0.9792070984840393, 0.1576405018568039, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22378143668174744, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.8545, "q": [-0.31387463212013245, -0.01394559070467949, 0.015681909397244453, 0.632034182548523, -0.326521635055542, 0.021098682656884193, -0.3229677379131317, 0.029929669573903084, -0.014623950235545635, 0.6239466667175293, -0.33668187260627747, -0.02102883718907833, 0.0027051628567278385, 0.006237202323973179, -0.030343223363161087, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008868326433002949]} +{"t": 3.8712, "q": [-0.31387463212013245, -0.01394559070467949, 0.01565512642264366, 0.6320256590843201, -0.326521635055542, 0.021098682656884193, -0.3229677379131317, 0.029921147972345352, -0.014637341722846031, 0.6239381432533264, -0.3366941809654236, -0.021021487191319466, 0.0027051628567278385, 0.006275165360420942, -0.030299130827188492, 0.2905215919017792, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.157616525888443, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008844357915222645]} +{"t": 3.888, "q": [-0.3138575851917267, -0.01394559070467949, 0.01564173400402069, 0.6320256590843201, -0.3265257775783539, 0.02110590599477291, -0.32295921444892883, 0.029929669573903084, -0.014610557816922665, 0.6239040493965149, -0.33669009804725647, -0.021014254540205002, 0.0026649872306734324, 0.006282765883952379, -0.0303040724247694, 0.2905096113681793, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 3.905, "q": [-0.3138916790485382, -0.013937067240476608, 0.01565512642264366, 0.6320256590843201, -0.3265257775783539, 0.021091395989060402, -0.32295069098472595, 0.02991262450814247, -0.014637341722846031, 0.623895525932312, -0.33668187260627747, -0.02102883718907833, 0.0027051628567278385, 0.006282765883952379, -0.0303040724247694, 0.2905096113681793, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.9217, "q": [-0.3138575851917267, -0.01395411230623722, 0.01565512642264366, 0.6320171356201172, -0.3265298902988434, 0.02109861932694912, -0.32294216752052307, 0.029929669573903084, -0.014650734141469002, 0.6238784790039062, -0.33669009804725647, -0.021014254540205002, 0.002691771136596799, 0.006267587188631296, -0.03033350594341755, 0.2905096113681793, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 3.9384, "q": [-0.31387463212013245, -0.013937067240476608, 0.01564173400402069, 0.6320086121559143, -0.326521635055542, 0.021098682656884193, -0.3229336440563202, 0.02991262450814247, -0.014623950235545635, 0.6238529086112976, -0.3366941809654236, -0.021021487191319466, 0.0026649872306734324, 0.0062903608195483685, -0.03029918670654297, 0.2904616594314575, 0.21594375371932983, -0.005225121974945068, 0.9792070984840393, 0.15758058428764343, 0.06169478967785835, -0.040890175849199295, 0.29507559537887573, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 3.9552, "q": [-0.31387463212013245, -0.013937067240476608, 0.01565512642264366, 0.6319915652275085, -0.3265298902988434, 0.02109861932694912, -0.32295921444892883, 0.029938191175460815, -0.014623950235545635, 0.6238273978233337, -0.33669009804725647, -0.021014254540205002, 0.0027453387156128883, 0.006259981542825699, -0.030318733304739, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.0409141443669796, 0.2950636148452759, -0.2237934172153473, 0.023081617429852486, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 3.9719, "q": [-0.31387463212013245, -0.013911502435803413, 0.01562834158539772, 0.6319830417633057, -0.3265381157398224, 0.021084045991301537, -0.32294216752052307, 0.029921147972345352, -0.014623950235545635, 0.6238018274307251, -0.3366941809654236, -0.021006964147090912, 0.002731946762651205, 0.006267576012760401, -0.030313847586512566, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04087819159030914, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008832373656332493]} +{"t": 3.9886, "q": [-0.31387463212013245, -0.013928545638918877, 0.01562834158539772, 0.6319745182991028, -0.3265298902988434, 0.02109861932694912, -0.32294216752052307, 0.029929669573903084, -0.014610557816922665, 0.6237847805023193, -0.3366941809654236, -0.021021487191319466, 0.0026382035575807095, 0.006259986665099859, -0.03032856248319149, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.0055, "q": [-0.3138916790485382, -0.013920024037361145, 0.01565512642264366, 0.6319659948348999, -0.3265298902988434, 0.02111312933266163, -0.32294216752052307, 0.029921147972345352, -0.014650734141469002, 0.6237592101097107, -0.33668187260627747, -0.02102883718907833, 0.002691771136596799, 0.00622200733050704, -0.03034316934645176, 0.2904616594314575, 0.2159557342529297, -0.005225121974945068, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.2237934172153473, 0.02310558594763279, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.0223, "q": [-0.3138916790485382, -0.013902978971600533, 0.01562834158539772, 0.631957471370697, -0.3265298902988434, 0.02111312933266163, -0.32295069098472595, 0.029929669573903084, -0.014623950235545635, 0.6237251162528992, -0.33669009804725647, -0.021014254540205002, 0.002691771136596799, 0.006237186025828123, -0.030313735827803612, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.040890175849199295, 0.29507559537887573, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.039, "q": [-0.31390872597694397, -0.013911502435803413, 0.01562834158539772, 0.6319404244422913, -0.3265175223350525, 0.021105969324707985, -0.32294216752052307, 0.029938191175460815, -0.014623950235545635, 0.6237165927886963, -0.33669009804725647, -0.021014254540205002, 0.0026649872306734324, 0.006199228577315807, -0.030367659404873848, 0.2904616594314575, 0.2159797102212906, -0.0051891696639359, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.0409141443669796, 0.295051634311676, -0.22378143668174744, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008832373656332493]} +{"t": 4.0558, "q": [-0.3138831555843353, -0.013894457370042801, 0.01562834158539772, 0.6319404244422913, -0.3265298902988434, 0.02109861932694912, -0.3229251205921173, 0.02991262450814247, -0.014623950235545635, 0.6236910223960876, -0.33669009804725647, -0.021014254540205002, 0.0026649872306734324, 0.006184016820043325, -0.030338115990161896, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.0725, "q": [-0.31392577290534973, -0.013920024037361145, 0.015601558610796928, 0.6319233775138855, -0.3265298902988434, 0.02111312933266163, -0.32294216752052307, 0.029929669573903084, -0.014637341722846031, 0.6236484050750732, -0.3366983234882355, -0.02099965512752533, 0.0026649872306734324, 0.006252364721149206, -0.030284302309155464, 0.2904616594314575, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.040890175849199295, 0.29507559537887573, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835503876209259, 0.05243098363280296, 0.008868326433002949]} +{"t": 4.0893, "q": [-0.31390872597694397, -0.01388593576848507, 0.01564173400402069, 0.6319063305854797, -0.3265381455421448, 0.021113082766532898, -0.3229336440563202, 0.029929669573903084, -0.014623950235545635, 0.6236057877540588, -0.33669009804725647, -0.021014254540205002, 0.0026649872306734324, 0.006221969146281481, -0.030274363234639168, 0.2904856503009796, 0.21596772968769073, -0.00523710623383522, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.040890175849199295, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 4.106, "q": [-0.31391724944114685, -0.013902978971600533, 0.015601558610796928, 0.6319063305854797, -0.3265340030193329, 0.02112036943435669, -0.3229336440563202, 0.029921147972345352, -0.014637341722846031, 0.6235716938972473, -0.3366941809654236, -0.021021487191319466, 0.002691771136596799, 0.006221979856491089, -0.03029402159154415, 0.2905096113681793, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.157616525888443, 0.06169478967785835, -0.040890175849199295, 0.295051634311676, -0.22378143668174744, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 4.1227, "q": [-0.3139342963695526, -0.013920024037361145, 0.015601558610796928, 0.631889283657074, -0.3265340030193329, 0.02112036943435669, -0.32294216752052307, 0.029921147972345352, -0.014597166329622269, 0.6235631704330444, -0.33669009804725647, -0.021014254540205002, 0.002718554809689522, 0.006252370309084654, -0.030294133350253105, 0.2905096113681793, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.04090216010808945, 0.29507559537887573, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008844357915222645]} +{"t": 4.1396, "q": [-0.3139342963695526, -0.01388593576848507, 0.01562834158539772, 0.6318807601928711, -0.3265381455421448, 0.021113082766532898, -0.32289955019950867, 0.029929669573903084, -0.014623950235545635, 0.6235546469688416, -0.33669009804725647, -0.021014254540205002, 0.0026783791836351156, 0.0062827495858073235, -0.030274584889411926, 0.2904856503009796, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22378143668174744, 0.02310558594763279, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.1564, "q": [-0.3139428198337555, -0.01388593576848507, 0.015601558610796928, 0.6318722367286682, -0.3265340030193329, 0.021105842664837837, -0.32291659712791443, 0.029929669573903084, -0.014610557816922665, 0.6235376000404358, -0.33668598532676697, -0.021007023751735687, 0.0027453387156128883, 0.006297944579273462, -0.030274640768766403, 0.29044967889785767, 0.2159797102212906, -0.005225121974945068, 0.9792190790176392, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 4.1731, "q": [-0.3139342963695526, -0.013894457370042801, 0.01562834158539772, 0.6318637132644653, -0.3265381455421448, 0.021113082766532898, -0.32294216752052307, 0.029929669573903084, -0.014664125628769398, 0.62352055311203, -0.33669009804725647, -0.021014254540205002, 0.0027453387156128883, 0.006252370309084654, -0.030294133350253105, 0.29044967889785767, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22378143668174744, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05240701511502266, 0.008856342174112797]} +{"t": 4.1898, "q": [-0.3139513432979584, -0.013894457370042801, 0.01562834158539772, 0.6318551898002625, -0.3265381455421448, 0.021113082766532898, -0.32291659712791443, 0.029938191175460815, -0.014623950235545635, 0.62352055311203, -0.33669009804725647, -0.021014254540205002, 0.002691771136596799, 0.006275160238146782, -0.030289301648736, 0.29044967889785767, 0.21596772968769073, -0.005225121974945068, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 4.2066, "q": [-0.3139428198337555, -0.013894457370042801, 0.015601558610796928, 0.6318466663360596, -0.3265381455421448, 0.021113082766532898, -0.32291659712791443, 0.029938191175460815, -0.014597166329622269, 0.6235120296478271, -0.33669009804725647, -0.021014254540205002, 0.002718554809689522, 0.006267559714615345, -0.03028435818850994, 0.2904376983642578, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.0409141443669796, 0.29502764344215393, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.2233, "q": [-0.3139513432979584, -0.01388593576848507, 0.01562834158539772, 0.6318551898002625, -0.3265381455421448, 0.021113082766532898, -0.3229336440563202, 0.029938191175460815, -0.014650734141469002, 0.6234779357910156, -0.33669009804725647, -0.021014254540205002, 0.0026649872306734324, 0.0062523758970201015, -0.030303962528705597, 0.2904376983642578, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.157616525888443, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 4.24, "q": [-0.3139342963695526, -0.013877412304282188, 0.015614950098097324, 0.6318381428718567, -0.3265257775783539, 0.02110590599477291, -0.32291659712791443, 0.029938191175460815, -0.014623950235545635, 0.6234779357910156, -0.33668187260627747, -0.02102883718907833, 0.002758730435743928, 0.006252370309084654, -0.030294133350253105, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.0409141443669796, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.2568, "q": [-0.31392577290534973, -0.01388593576848507, 0.015614950098097324, 0.6318381428718567, -0.3265298902988434, 0.02111312933266163, -0.32289955019950867, 0.029921147972345352, -0.014664125628769398, 0.6234694719314575, -0.33669009804725647, -0.021014254540205002, 0.0027051628567278385, 0.0063055395148694515, -0.03026975318789482, 0.2904736399650574, 0.21594375371932983, -0.0051891696639359, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.0088803106918931]} +{"t": 4.2736, "q": [-0.31391724944114685, -0.01388593576848507, 0.015614950098097324, 0.6318381428718567, -0.3265381455421448, 0.021113082766532898, -0.32290807366371155, 0.029946712777018547, -0.014664125628769398, 0.6234779357910156, -0.33669009804725647, -0.021014254540205002, 0.002718554809689522, 0.006267554592341185, -0.03027452901005745, 0.2904736399650574, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.061658840626478195, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18353840708732605, 0.05240701511502266, 0.008892294950783253]} +{"t": 4.2904, "q": [-0.31392577290534973, -0.013902978971600533, 0.015601558610796928, 0.6318210959434509, -0.3265298902988434, 0.02111312933266163, -0.3228825032711029, 0.029938191175460815, -0.014637341722846031, 0.6234694719314575, -0.33669009804725647, -0.021014254540205002, 0.002691771136596799, 0.0062751490622758865, -0.030269641429185867, 0.2904736399650574, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.040890175849199295, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835983246564865, 0.05240701511502266, 0.008868326433002949]} +{"t": 4.3071, "q": [-0.3139342963695526, -0.013877412304282188, 0.015601558610796928, 0.6318296194076538, -0.3265175223350525, 0.021120497956871986, -0.3228825032711029, 0.029938191175460815, -0.014650734141469002, 0.6234694719314575, -0.33669009804725647, -0.021014254540205002, 0.0027453387156128883, 0.006290349643677473, -0.030279526486992836, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.0409141443669796, 0.29507559537887573, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 4.3239, "q": [-0.3139513432979584, -0.013894457370042801, 0.015588166192173958, 0.6318210959434509, -0.3265298902988434, 0.02109861932694912, -0.3228825032711029, 0.029938191175460815, -0.014664125628769398, 0.6234524250030518, -0.33668190240859985, -0.02099977247416973, 0.002691771136596799, 0.006335907615721226, -0.03023054637014866, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.0409141443669796, 0.295051634311676, -0.22378143668174744, 0.023081617429852486, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008844357915222645]} +{"t": 4.3407, "q": [-0.3139342963695526, -0.013894457370042801, 0.015588166192173958, 0.6318296194076538, -0.3265381157398224, 0.021098555997014046, -0.3228739798069, 0.029938191175460815, -0.014677518047392368, 0.6234524250030518, -0.33669009804725647, -0.021014254540205002, 0.002718554809689522, 0.00629032775759697, -0.03024020977318287, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29507559537887573, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 4.3574, "q": [-0.3139342963695526, -0.013877412304282188, 0.015601558610796928, 0.6318296194076538, -0.3265298902988434, 0.02111312933266163, -0.32285693287849426, 0.029938191175460815, -0.014677518047392368, 0.623435378074646, -0.33669009804725647, -0.021014254540205002, 0.002731946762651205, 0.006320723332464695, -0.030250148847699165, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.2237934172153473, 0.023081617429852486, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.3742, "q": [-0.31392577290534973, -0.01388593576848507, 0.015601558610796928, 0.6318210959434509, -0.3265340030193329, 0.021105842664837837, -0.3228910267353058, 0.029946712777018547, -0.014637341722846031, 0.6234524250030518, -0.33669009804725647, -0.021014254540205002, 0.002758730435743928, 0.006305523216724396, -0.030240265652537346, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792190790176392, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.3909, "q": [-0.31390872597694397, -0.01388593576848507, 0.015574774704873562, 0.6318210959434509, -0.3265175223350525, 0.021120497956871986, -0.3228484094142914, 0.029938191175460815, -0.014650734141469002, 0.623435378074646, -0.33668598532676697, -0.021007023751735687, 0.0027051628567278385, 0.0063283126801252365, -0.03023543395102024, 0.2904616594314575, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 4.4077, "q": [-0.31390872597694397, -0.013868890702724457, 0.015574774704873562, 0.6318210959434509, -0.3265298902988434, 0.02109861932694912, -0.3228398859500885, 0.029946712777018547, -0.014650734141469002, 0.623435378074646, -0.33668187260627747, -0.02102883718907833, 0.002758730435743928, 0.006313128862529993, -0.030255036428570747, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 4.4244, "q": [-0.31390872597694397, -0.013868890702724457, 0.015601558610796928, 0.6318040490150452, -0.3265340030193329, 0.021105842664837837, -0.32285693287849426, 0.029946712777018547, -0.014677518047392368, 0.6234268546104431, -0.33668598532676697, -0.021021544933319092, 0.0027453387156128883, 0.006389076821506023, -0.030206166207790375, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1576405018568039, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 4.4411, "q": [-0.3138916790485382, -0.013868890702724457, 0.015588166192173958, 0.6318040490150452, -0.3265340030193329, 0.021105842664837837, -0.3228398859500885, 0.029946712777018547, -0.014637341722846031, 0.6234183311462402, -0.33668598532676697, -0.021021544933319092, 0.002718554809689522, 0.006320712622255087, -0.030230490490794182, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22378143668174744, 0.023093601688742638, 0.9780925512313843, -0.1835743635892868, 0.05241899937391281, 0.008868326433002949]} +{"t": 4.4578, "q": [-0.3138916790485382, -0.013877412304282188, 0.015601558610796928, 0.6317955255508423, -0.3265257775783539, 0.02110590599477291, -0.3228313624858856, 0.029938191175460815, -0.014664125628769398, 0.6234183311462402, -0.33667367696762085, -0.02101435326039791, 0.002718554809689522, 0.006434645503759384, -0.03017684444785118, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.052442967891693115, 0.008856342174112797]} +{"t": 4.4746, "q": [-0.31390872597694397, -0.013877412304282188, 0.015574774704873562, 0.6317870020866394, -0.3265257775783539, 0.02110590599477291, -0.3228398859500885, 0.029946712777018547, -0.014650734141469002, 0.6233842372894287, -0.33668187260627747, -0.02102883718907833, 0.002731946762651205, 0.006396660581231117, -0.03018162027001381, 0.2904616594314575, 0.21593177318572998, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06169478967785835, -0.040890175849199295, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05243098363280296, 0.008856342174112797]} +{"t": 4.4913, "q": [-0.3138831555843353, -0.013877412304282188, 0.015588166192173958, 0.6317784786224365, -0.3265257775783539, 0.02110590599477291, -0.3228313624858856, 0.029946712777018547, -0.014690909534692764, 0.6233757138252258, -0.33669009804725647, -0.021014254540205002, 0.002651595277711749, 0.006389066111296415, -0.03018650785088539, 0.2904616594314575, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.0409141443669796, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 4.5082, "q": [-0.31387463212013245, -0.013868890702724457, 0.015601558610796928, 0.6317784786224365, -0.3265257775783539, 0.02112041600048542, -0.32282283902168274, 0.029955236241221428, -0.014677518047392368, 0.6233757138252258, -0.33669009804725647, -0.021014254540205002, 0.002691771136596799, 0.006404266692698002, -0.03019639290869236, 0.2904616594314575, 0.21594375371932983, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.5249, "q": [-0.31390872597694397, -0.013868890702724457, 0.015574774704873562, 0.6317529678344727, -0.3265298902988434, 0.02109861932694912, -0.3228313624858856, 0.029955236241221428, -0.014677518047392368, 0.623367190361023, -0.33668598532676697, -0.021007023751735687, 0.002718554809689522, 0.006449829787015915, -0.030157241970300674, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.5417, "q": [-0.31387463212013245, -0.013868890702724457, 0.015574774704873562, 0.6317444443702698, -0.3265298902988434, 0.02109861932694912, -0.32281434535980225, 0.029955236241221428, -0.014650734141469002, 0.623367190361023, -0.33667367696762085, -0.02101435326039791, 0.002624811604619026, 0.006366276182234287, -0.030191339552402496, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06169478967785835, -0.04087819159030914, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.1835743635892868, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.5584, "q": [-0.31387463212013245, -0.013877412304282188, 0.015588166192173958, 0.6317359209060669, -0.3265298902988434, 0.02109861932694912, -0.32281434535980225, 0.02996375784277916, -0.014664125628769398, 0.6233586668968201, -0.33668598532676697, -0.021021544933319092, 0.0027453387156128883, 0.006449829787015915, -0.030157241970300674, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.15760454535484314, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22378143668174744, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 4.5752, "q": [-0.3138490617275238, -0.013868890702724457, 0.015601558610796928, 0.631727397441864, -0.3265257775783539, 0.021091395989060402, -0.32282283902168274, 0.02996375784277916, -0.014677518047392368, 0.6233586668968201, -0.33668187260627747, -0.02102883718907833, 0.0027453387156128883, 0.006434640381485224, -0.03016701526939869, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.157616525888443, 0.06169478967785835, -0.04090216010808945, 0.295051634311676, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.5919, "q": [-0.3138405382633209, -0.013868890702724457, 0.015574774704873562, 0.631727397441864, -0.3265257775783539, 0.021091395989060402, -0.32282283902168274, 0.02996375784277916, -0.014664125628769398, 0.6233501434326172, -0.33667778968811035, -0.021021604537963867, 0.0027453387156128883, 0.006434629205614328, -0.030147356912493706, 0.2904736399650574, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 4.6087, "q": [-0.31383201479911804, -0.013877412304282188, 0.015588166192173958, 0.6317188739776611, -0.326521635055542, 0.021098682656884193, -0.3227972984313965, 0.029955236241221428, -0.014664125628769398, 0.6233330965042114, -0.33667778968811035, -0.021021604537963867, 0.0026649872306734324, 0.0064118392765522, -0.03015218861401081, 0.2904856503009796, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.6254, "q": [-0.3138490617275238, -0.013868890702724457, 0.015588166192173958, 0.6316762566566467, -0.3265133798122406, 0.02111327461898327, -0.3227887749671936, 0.02996375784277916, -0.014677518047392368, 0.6233160495758057, -0.33668187260627747, -0.02102883718907833, 0.002691771136596799, 0.006533356383442879, -0.030073996633291245, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.2237934172153473, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.052442967891693115, 0.008856342174112797]} +{"t": 4.6422, "q": [-0.3138405382633209, -0.013843324035406113, 0.015574774704873562, 0.631659209728241, -0.326521635055542, 0.021098682656884193, -0.3227972984313965, 0.029955236241221428, -0.014677518047392368, 0.6233075261116028, -0.33668190240859985, -0.021014314144849777, 0.0026783791836351156, 0.006540956906974316, -0.030078938230872154, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.15760454535484314, 0.06167082488536835, -0.04090216010808945, 0.29503965377807617, -0.2237934172153473, 0.023081617429852486, 0.9780685901641846, -0.1835743635892868, 0.05243098363280296, 0.008868326433002949]} +{"t": 4.6591, "q": [-0.31382349133491516, -0.013851847499608994, 0.015588166192173958, 0.6316421627998352, -0.3265257775783539, 0.021091395989060402, -0.32276320457458496, 0.02996375784277916, -0.014677518047392368, 0.6232734322547913, -0.33668187260627747, -0.02102883718907833, 0.002651595277711749, 0.00654094573110342, -0.03005927987396717, 0.29049763083457947, 0.2159797102212906, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.29507559537887573, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 4.6758, "q": [-0.3138064444065094, -0.013868890702724457, 0.015588166192173958, 0.6316251158714294, -0.326521635055542, 0.021098682656884193, -0.3227546811103821, 0.02996375784277916, -0.014650734141469002, 0.6232649087905884, -0.33668187260627747, -0.02102883718907833, 0.0027051628567278385, 0.006601709872484207, -0.030030013993382454, 0.2904616594314575, 0.21594375371932983, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.061658840626478195, -0.04086620733141899, 0.2950636148452759, -0.2237934172153473, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008892294950783253]} +{"t": 4.6925, "q": [-0.3138064444065094, -0.013868890702724457, 0.015574774704873562, 0.6315995454788208, -0.326521635055542, 0.021098682656884193, -0.3227376341819763, 0.02996375784277916, -0.014690909534692764, 0.6232308149337769, -0.33666548132896423, -0.020999889820814133, 0.002691771136596799, 0.0066548679023981094, -0.029985975474119186, 0.2905096113681793, 0.2159797102212906, -0.005213138181716204, 0.9791830778121948, 0.15754462778568268, 0.06169478967785835, -0.04090216010808945, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.052442967891693115, 0.008856342174112797]} +{"t": 4.7093, "q": [-0.3138064444065094, -0.013868890702724457, 0.015601558610796928, 0.6315654516220093, -0.3265257775783539, 0.02110590599477291, -0.3227546811103821, 0.02996375784277916, -0.014731084927916527, 0.6231796741485596, -0.33668187260627747, -0.02102883718907833, 0.0026649872306734324, 0.0066548679023981094, -0.029985975474119186, 0.2904856503009796, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.1575925648212433, 0.06169478967785835, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 4.726, "q": [-0.31378939747810364, -0.013860369101166725, 0.015574774704873562, 0.6315313577651978, -0.3265298902988434, 0.02109861932694912, -0.3227376341819763, 0.02996375784277916, -0.014664125628769398, 0.6231200098991394, -0.33669009804725647, -0.021014254540205002, 0.002731946762651205, 0.0066548679023981094, -0.029985975474119186, 0.2904856503009796, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 4.7427, "q": [-0.31378090381622314, -0.013860369101166725, 0.015601558610796928, 0.6314802169799805, -0.3265257775783539, 0.02110590599477291, -0.32272058725357056, 0.02996375784277916, -0.014650734141469002, 0.6230944395065308, -0.33669009804725647, -0.021014254540205002, 0.002691771136596799, 0.006609299220144749, -0.03001529723405838, 0.29049763083457947, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.06169478967785835, -0.0409141443669796, 0.29507559537887573, -0.2237934172153473, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.052395034581422806, 0.008844357915222645]} +{"t": 4.7595, "q": [-0.3138064444065094, -0.013834802433848381, 0.015588166192173958, 0.6314802169799805, -0.3265257775783539, 0.02110590599477291, -0.32272058725357056, 0.029980802908539772, -0.014664125628769398, 0.6230348348617554, -0.33667778968811035, -0.021021604537963867, 0.002691771136596799, 0.006586514879018068, -0.030029958114027977, 0.2904736399650574, 0.21596772968769073, -0.005225121974945068, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 4.7762, "q": [-0.31378939747810364, -0.013834802433848381, 0.015588166192173958, 0.6314375996589661, -0.326521635055542, 0.021113192662596703, -0.3227120637893677, 0.029980802908539772, -0.014664125628769398, 0.6229751706123352, -0.33668187260627747, -0.02102883718907833, 0.0027051628567278385, 0.006632083561271429, -0.030000636354088783, 0.2904616594314575, 0.21594375371932983, -0.005201153922826052, 0.9792190790176392, 0.15760454535484314, 0.061658840626478195, -0.040890175849199295, 0.29507559537887573, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 4.793, "q": [-0.31377238035202026, -0.01382628083229065, 0.015588166192173958, 0.6313949823379517, -0.3265381157398224, 0.021084045991301537, -0.32272058725357056, 0.02997227944433689, -0.014690909534692764, 0.622915506362915, -0.33667367696762085, -0.02101435326039791, 0.002718554809689522, 0.006616894155740738, -0.030010409653186798, 0.29049763083457947, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.04090216010808945, 0.2950875759124756, -0.22378143668174744, 0.023093601688742638, 0.9781045317649841, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.8097, "q": [-0.31382349133491516, -0.01382628083229065, 0.015561383217573166, 0.6313694715499878, -0.3265257775783539, 0.02110590599477291, -0.3227035403251648, 0.02997227944433689, -0.014690909534692764, 0.6228728890419006, -0.33669009804725647, -0.021014254540205002, 0.0026783791836351156, 0.006586514879018068, -0.030029958114027977, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.040890175849199295, 0.29507559537887573, -0.2237934172153473, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.8265, "q": [-0.3137979209423065, -0.013834802433848381, 0.015588166192173958, 0.6313439011573792, -0.3265257775783539, 0.02110590599477291, -0.3227035403251648, 0.029989324510097504, -0.014677518047392368, 0.6228217482566833, -0.33667778968811035, -0.021021604537963867, 0.0027051628567278385, 0.0066017042845487595, -0.030020184814929962, 0.2904856503009796, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.04090216010808945, 0.29507559537887573, -0.22378143668174744, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 4.8433, "q": [-0.3138149678707123, -0.013809235766530037, 0.015588166192173958, 0.6313353776931763, -0.3265340030193329, 0.021105842664837837, -0.32264387607574463, 0.029997846111655235, -0.01470430102199316, 0.6227876543998718, -0.33666959404945374, -0.021007122471928596, 0.0026649872306734324, 0.006586509291082621, -0.030020128935575485, 0.29049763083457947, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.22378143668174744, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.86, "q": [-0.31378090381622314, -0.013817759230732918, 0.015561383217573166, 0.6312757134437561, -0.3265340030193329, 0.02112036943435669, -0.32262685894966125, 0.029989324510097504, -0.014677518047392368, 0.6227194666862488, -0.33667367696762085, -0.02101435326039791, 0.0027051628567278385, 0.006632077973335981, -0.02999080717563629, 0.2904616594314575, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008868326433002949]} +{"t": 4.8767, "q": [-0.31377238035202026, -0.013834802433848381, 0.015561383217573166, 0.6312160491943359, -0.3265257775783539, 0.02110590599477291, -0.32262685894966125, 0.029989324510097504, -0.01470430102199316, 0.6226769089698792, -0.33667367696762085, -0.02101435326039791, 0.0026649872306734324, 0.006662462837994099, -0.029981087893247604, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.04090216010808945, 0.2950636148452759, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 4.8935, "q": [-0.3137979209423065, -0.013843324035406113, 0.015588166192173958, 0.6311563849449158, -0.3265298902988434, 0.02109861932694912, -0.32259276509284973, 0.029989324510097504, -0.014677518047392368, 0.6226428151130676, -0.33667367696762085, -0.02101435326039791, 0.0027051628567278385, 0.0066700465977191925, -0.02995654195547104, 0.2904616594314575, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.02310558594763279, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 4.9116, "q": [-0.31378090381622314, -0.013817759230732918, 0.015574774704873562, 0.63113933801651, -0.3265298902988434, 0.02109861932694912, -0.3226012885570526, 0.029997846111655235, -0.01470430102199316, 0.6225831508636475, -0.33669009804725647, -0.021014254540205002, 0.0027453387156128883, 0.006639656610786915, -0.029956432059407234, 0.29044967889785767, 0.2159797102212906, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04090216010808945, 0.29502764344215393, -0.22380541265010834, 0.023069633170962334, 0.9780925512313843, -0.1835503876209259, 0.05243098363280296, 0.0088803106918931]} +{"t": 4.9283, "q": [-0.31378939747810364, -0.01382628083229065, 0.015561383217573166, 0.6311052441596985, -0.3265298902988434, 0.02111312933266163, -0.3225671947002411, 0.029997846111655235, -0.014731084927916527, 0.6225490570068359, -0.33668190240859985, -0.02099977247416973, 0.002718554809689522, 0.006624466739594936, -0.02996620535850525, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.040890175849199295, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 4.9451, "q": [-0.3137979209423065, -0.013817759230732918, 0.015561383217573166, 0.6310455799102783, -0.3265298902988434, 0.02109861932694912, -0.32258424162864685, 0.029997846111655235, -0.01470430102199316, 0.6225234866142273, -0.33666959404945374, -0.021007122471928596, 0.002731946762651205, 0.006723205093294382, -0.02991250343620777, 0.29044967889785767, 0.2159797102212906, -0.005225121974945068, 0.9791951179504395, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.2237934172153473, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 4.9618, "q": [-0.31377238035202026, -0.013817759230732918, 0.015561383217573166, 0.6310114860534668, -0.3265298902988434, 0.02109861932694912, -0.32253310084342957, 0.029997846111655235, -0.014731084927916527, 0.62247234582901, -0.33667367696762085, -0.02101435326039791, 0.002731946762651205, 0.0067080045118927956, -0.029902620241045952, 0.2904856503009796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05240701511502266, 0.008844357915222645]} +{"t": 4.9786, "q": [-0.31377238035202026, -0.013817759230732918, 0.015547990798950195, 0.6309689283370972, -0.3265298902988434, 0.02109861932694912, -0.3225245773792267, 0.029989324510097504, -0.014690909534692764, 0.622404158115387, -0.33667367696762085, -0.02101435326039791, 0.0026783791836351156, 0.006707998923957348, -0.02989278919994831, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9791830778121948, 0.15756858885288239, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008868326433002949]} +{"t": 4.9953, "q": [-0.3137553334236145, -0.013809235766530037, 0.015561383217573166, 0.6309348344802856, -0.3265381157398224, 0.021098555997014046, -0.32249900698661804, 0.029989324510097504, -0.014731084927916527, 0.6223785877227783, -0.33666959404945374, -0.021007122471928596, 0.002718554809689522, 0.006700403988361359, -0.029897676780819893, 0.2904736399650574, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.040890175849199295, 0.29507559537887573, -0.2238173931837082, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.052442967891693115, 0.008856342174112797]} +{"t": 5.012, "q": [-0.3137553334236145, -0.013800714164972305, 0.015561383217573166, 0.6309263110160828, -0.3265257775783539, 0.02110590599477291, -0.3224819600582123, 0.029989324510097504, -0.01471769344061613, 0.6223530173301697, -0.33666548132896423, -0.020999889820814133, 0.002691771136596799, 0.00665482971817255, -0.029917169362306595, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15758058428764343, 0.0616828091442585, -0.04086620733141899, 0.29503965377807617, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008868326433002949]} +{"t": 5.0288, "q": [-0.31372976303100586, -0.013800714164972305, 0.015561383217573166, 0.6309007406234741, -0.326521635055542, 0.021113192662596703, -0.3224649131298065, 0.030006367713212967, -0.01470430102199316, 0.6223104596138, -0.33667778968811035, -0.020992539823055267, 0.0027453387156128883, 0.006662430241703987, -0.029922112822532654, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 5.0455, "q": [-0.3137127161026001, -0.013800714164972305, 0.015547990798950195, 0.6308240294456482, -0.3265257775783539, 0.02110590599477291, -0.3224819600582123, 0.029997846111655235, -0.01471769344061613, 0.622242271900177, -0.33667370676994324, -0.020985309034585953, 0.002718554809689522, 0.006806714925915003, -0.029829397797584534, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.29502764344215393, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 5.0623, "q": [-0.3136786222457886, -0.013809235766530037, 0.015561383217573166, 0.6307814121246338, -0.3265298902988434, 0.02109861932694912, -0.32245638966560364, 0.029997846111655235, -0.014731084927916527, 0.6222081780433655, -0.33667778968811035, -0.020992539823055267, 0.002691771136596799, 0.0068067223764956, -0.029839208349585533, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.0791, "q": [-0.31368714570999146, -0.013792192563414574, 0.015547990798950195, 0.6307558417320251, -0.3265298902988434, 0.02109861932694912, -0.32243937253952026, 0.029997846111655235, -0.014744477346539497, 0.6221996545791626, -0.33667370676994324, -0.020985309034585953, 0.0027051628567278385, 0.006829492282122374, -0.029814792796969414, 0.2904616594314575, 0.2159557342529297, -0.0051891696639359, 0.9792070984840393, 0.1575925648212433, 0.06169478967785835, -0.04090216010808945, 0.2950636148452759, -0.22378143668174744, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 5.0958, "q": [-0.31368714570999146, -0.013792192563414574, 0.0155345993116498, 0.6307217478752136, -0.326521635055542, 0.021098682656884193, -0.3224308490753174, 0.030006367713212967, -0.014731084927916527, 0.6221314668655396, -0.33666548132896423, -0.020999889820814133, 0.002691771136596799, 0.0068067223764956, -0.029839208349585533, 0.2904736399650574, 0.21594375371932983, -0.0051891696639359, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9781045317649841, -0.1835503876209259, 0.05243098363280296, 0.008856342174112797]} +{"t": 5.1126, "q": [-0.31363600492477417, -0.01376662589609623, 0.015521206893026829, 0.6307047009468079, -0.3265257775783539, 0.02110590599477291, -0.32239675521850586, 0.030006367713212967, -0.014731084927916527, 0.6221058964729309, -0.33667367696762085, -0.02101435326039791, 0.0027051628567278385, 0.006776344496756792, -0.029848871752619743, 0.2904736399650574, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.06169478967785835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.1293, "q": [-0.3136274814605713, -0.01376662589609623, 0.0155345993116498, 0.6306791305541992, -0.3265298902988434, 0.02109861932694912, -0.32235413789749146, 0.030014891177415848, -0.014731084927916527, 0.6220718026161194, -0.33666959404945374, -0.021007122471928596, 0.0027051628567278385, 0.006821899674832821, -0.029819661751389503, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008868326433002949]} +{"t": 5.146, "q": [-0.31363600492477417, -0.01376662589609623, 0.015521206893026829, 0.6306279897689819, -0.3265298902988434, 0.02109861932694912, -0.3223711848258972, 0.030014891177415848, -0.014731084927916527, 0.6220206618309021, -0.33667370676994324, -0.020985309034585953, 0.0027453387156128883, 0.006859861314296722, -0.029795320704579353, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9791830778121948, 0.15760454535484314, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.1628, "q": [-0.31363600492477417, -0.013775147497653961, 0.015521206893026829, 0.6305939555168152, -0.3265257775783539, 0.02110590599477291, -0.3223711848258972, 0.030006367713212967, -0.014731084927916527, 0.6219525337219238, -0.33667367696762085, -0.02101435326039791, 0.0026783791836351156, 0.006867461837828159, -0.029800262302160263, 0.2904856503009796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.052442967891693115, 0.008856342174112797]} +{"t": 5.1795, "q": [-0.31363600492477417, -0.013749580830335617, 0.015521206893026829, 0.6305513381958008, -0.3265298902988434, 0.02109861932694912, -0.3223711848258972, 0.030014891177415848, -0.014731084927916527, 0.6218843460083008, -0.33666548132896423, -0.020999889820814133, 0.0026783791836351156, 0.006837077438831329, -0.029800113290548325, 0.29044967889785767, 0.2159557342529297, -0.005225121974945068, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29502764344215393, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.1835503876209259, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.1962, "q": [-0.31363600492477417, -0.013749580830335617, 0.015507815405726433, 0.630534291267395, -0.326521635055542, 0.021098682656884193, -0.32236266136169434, 0.03002341277897358, -0.014690909534692764, 0.621867299079895, -0.33668190240859985, -0.02099977247416973, 0.002731946762651205, 0.006859861314296722, -0.029795320704579353, 0.2904376983642578, 0.2159557342529297, -0.0051891696639359, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.2237934172153473, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 5.213, "q": [-0.31364452838897705, -0.013758104294538498, 0.015507815405726433, 0.6305001974105835, -0.3265298902988434, 0.02109861932694912, -0.3223456144332886, 0.030014891177415848, -0.014731084927916527, 0.6218332052230835, -0.33666548132896423, -0.020999889820814133, 0.002718554809689522, 0.006844677496701479, -0.029805056750774384, 0.2904616594314575, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.06169478967785835, -0.04090216010808945, 0.29507559537887573, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.2297, "q": [-0.3136189579963684, -0.013749580830335617, 0.015507815405726433, 0.6304575800895691, -0.3265298902988434, 0.02109861932694912, -0.32235413789749146, 0.03002341277897358, -0.014731084927916527, 0.6217650175094604, -0.33666959404945374, -0.020992599427700043, 0.002718554809689522, 0.006859861314296722, -0.029795320704579353, 0.2904856503009796, 0.2159557342529297, -0.005225121974945068, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.04090216010808945, 0.29507559537887573, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 5.2465, "q": [-0.31360191106796265, -0.013749580830335617, 0.015507815405726433, 0.6303723454475403, -0.3265340030193329, 0.021091332659125328, -0.32236266136169434, 0.03002341277897358, -0.014731084927916527, 0.6216627359390259, -0.33666548132896423, -0.020999889820814133, 0.002718554809689522, 0.006897824816405773, -0.029770951718091965, 0.2904856503009796, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 5.2635, "q": [-0.31363600492477417, -0.013749580830335617, 0.015494422987103462, 0.630321204662323, -0.3265298902988434, 0.02109861932694912, -0.3223370909690857, 0.03003193438053131, -0.01471769344061613, 0.6215945482254028, -0.33666959404945374, -0.021007122471928596, 0.0027453387156128883, 0.006875039078295231, -0.029775751754641533, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.061658840626478195, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.2802, "q": [-0.31363600492477417, -0.013749580830335617, 0.015507815405726433, 0.6303041577339172, -0.3265340030193329, 0.021105842664837837, -0.32231152057647705, 0.03003193438053131, -0.014731084927916527, 0.6215605139732361, -0.33666959404945374, -0.020992599427700043, 0.002731946762651205, 0.006905409973114729, -0.02975625731050968, 0.2904616594314575, 0.21596772968769073, -0.0051891696639359, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.2969, "q": [-0.31364452838897705, -0.013741059228777885, 0.015507815405726433, 0.6302530169487, -0.3265340030193329, 0.021105842664837837, -0.3222944736480713, 0.030040455982089043, -0.01471769344061613, 0.6215093731880188, -0.33666959404945374, -0.021007122471928596, 0.0027453387156128883, 0.00692818034440279, -0.029731813818216324, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.061706773936748505, -0.04090216010808945, 0.295051634311676, -0.2237934172153473, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 5.3138, "q": [-0.31359341740608215, -0.013741059228777885, 0.015507815405726433, 0.630236029624939, -0.3265298902988434, 0.02109861932694912, -0.32226890325546265, 0.030048979446291924, -0.014744477346539497, 0.6214582324028015, -0.33667367696762085, -0.02101435326039791, 0.0026783791836351156, 0.006928187794983387, -0.02974163554608822, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18358634412288666, 0.052442967891693115, 0.008856342174112797]} +{"t": 5.3305, "q": [-0.31360191106796265, -0.013749580830335617, 0.015494422987103462, 0.6302104592323303, -0.3265340030193329, 0.021105842664837837, -0.32226890325546265, 0.030040455982089043, -0.014744477346539497, 0.62142413854599, -0.33667370676994324, -0.020985309034585953, 0.002731946762651205, 0.006897802464663982, -0.02974148653447628, 0.2904376983642578, 0.21593177318572998, -0.005201153922826052, 0.9792070984840393, 0.15760454535484314, 0.061706773936748505, -0.04090216010808945, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 5.3473, "q": [-0.3136189579963684, -0.013749580830335617, 0.015507815405726433, 0.6301678419113159, -0.3265298902988434, 0.02109861932694912, -0.3222433626651764, 0.030040455982089043, -0.014744477346539497, 0.6213474273681641, -0.33666136860847473, -0.021021703258156776, 0.0027051628567278385, 0.006966136395931244, -0.02969762124121189, 0.29042571783065796, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.040890175849199295, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 5.3641, "q": [-0.3136104345321655, -0.013749580830335617, 0.015507815405726433, 0.6301252245903015, -0.3265257775783539, 0.02110590599477291, -0.3222348392009735, 0.030040455982089043, -0.014757868833839893, 0.6213048100471497, -0.33666959404945374, -0.020992599427700043, 0.0026783791836351156, 0.006996514275670052, -0.029687948524951935, 0.29044967889785767, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.0409141443669796, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18358634412288666, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.3808, "q": [-0.3136104345321655, -0.013749580830335617, 0.015507815405726433, 0.6301252245903015, -0.3265298902988434, 0.02109861932694912, -0.32221779227256775, 0.030048979446291924, -0.014771261252462864, 0.6212877631187439, -0.3366572856903076, -0.021014470607042313, 0.002718554809689522, 0.0070041147992014885, -0.029692895710468292, 0.29042571783065796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.2950156629085541, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008844357915222645]} +{"t": 5.3976, "q": [-0.31360191106796265, -0.013749580830335617, 0.015481031499803066, 0.6300996541976929, -0.3265298902988434, 0.02111312933266163, -0.322200745344162, 0.030057501047849655, -0.014771261252462864, 0.6212536692619324, -0.3366572856903076, -0.02099994756281376, 0.002731946762651205, 0.006981306709349155, -0.029668230563402176, 0.2904137372970581, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.0409141443669796, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 5.4143, "q": [-0.31360191106796265, -0.013749580830335617, 0.015521206893026829, 0.6300826072692871, -0.326521635055542, 0.021098682656884193, -0.3221836984157562, 0.030048979446291924, -0.01479804515838623, 0.6212451457977295, -0.33666136860847473, -0.021021703258156776, 0.0027051628567278385, 0.006981306709349155, -0.029668230563402176, 0.29042571783065796, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 5.431, "q": [-0.31359341740608215, -0.013732537627220154, 0.015507815405726433, 0.6300570368766785, -0.3265298902988434, 0.02109861932694912, -0.3221836984157562, 0.030048979446291924, -0.01479804515838623, 0.6211855411529541, -0.33666959404945374, -0.021007122471928596, 0.002731946762651205, 0.0070344251580536366, -0.029594825580716133, 0.2904376983642578, 0.21596772968769073, -0.005225121974945068, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 5.4478, "q": [-0.3135763704776764, -0.013749580830335617, 0.015507815405726433, 0.6300314664840698, -0.3265257775783539, 0.02110590599477291, -0.32216665148735046, 0.030048979446291924, -0.01478465273976326, 0.6211514472961426, -0.33666548132896423, -0.020999889820814133, 0.002731946762651205, 0.007057209499180317, -0.029590027406811714, 0.29042571783065796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.29502764344215393, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 5.4646, "q": [-0.31360191106796265, -0.013732537627220154, 0.015481031499803066, 0.6300144195556641, -0.3265298902988434, 0.02109861932694912, -0.32217517495155334, 0.030048979446291924, -0.01478465273976326, 0.6211258769035339, -0.33667778968811035, -0.020992539823055267, 0.002758730435743928, 0.007042017765343189, -0.029589951038360596, 0.2904616594314575, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04087819159030914, 0.29502764344215393, -0.22378143668174744, 0.023069633170962334, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 5.4813, "q": [-0.31359341740608215, -0.013732537627220154, 0.015481031499803066, 0.6299803256988525, -0.3265298902988434, 0.02109861932694912, -0.3221581280231476, 0.030048979446291924, -0.014757868833839893, 0.6211003065109253, -0.33666959404945374, -0.02097807638347149, 0.0027453387156128883, 0.007057209499180317, -0.029590027406811714, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15756858885288239, 0.0616828091442585, -0.0409141443669796, 0.29503965377807617, -0.2237934172153473, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 5.4981, "q": [-0.31354227662086487, -0.01370697095990181, 0.015481031499803066, 0.629946231842041, -0.3265298902988434, 0.02109861932694912, -0.3221581280231476, 0.030057501047849655, -0.014744477346539497, 0.6210576891899109, -0.33666548132896423, -0.020999889820814133, 0.0026783791836351156, 0.0070344251580536366, -0.029594825580716133, 0.2904616594314575, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008844357915222645]} +{"t": 5.5148, "q": [-0.31355932354927063, -0.01370697095990181, 0.015481031499803066, 0.629946231842041, -0.3265340030193329, 0.021091332659125328, -0.32212403416633606, 0.030057501047849655, -0.014731084927916527, 0.621049165725708, -0.33667370676994324, -0.020985309034585953, 0.002718554809689522, 0.006996462121605873, -0.02961919456720352, 0.2904736399650574, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.29503965377807617, -0.2237934172153473, 0.023093601688742638, 0.9780685901641846, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 5.5315, "q": [-0.31354227662086487, -0.01370697095990181, 0.0154542475938797, 0.6299377083778381, -0.3265298902988434, 0.02109861932694912, -0.32212403416633606, 0.030048979446291924, -0.01479804515838623, 0.6210065484046936, -0.33666959404945374, -0.021007122471928596, 0.002731946762651205, 0.0070648095570504665, -0.02959497459232807, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9791830778121948, 0.1576405018568039, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.5485, "q": [-0.31355080008506775, -0.013698449358344078, 0.015481031499803066, 0.6299036145210266, -0.3265381455421448, 0.021113082766532898, -0.3221069872379303, 0.030048979446291924, -0.014757868833839893, 0.6210065484046936, -0.33666548132896423, -0.020999889820814133, 0.0026783791836351156, 0.007049609441310167, -0.029585078358650208, 0.2904376983642578, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.1835743635892868, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.5654, "q": [-0.31354227662086487, -0.013672882691025734, 0.015481031499803066, 0.6299036145210266, -0.3265340030193329, 0.021091332659125328, -0.3221155107021332, 0.030074546113610268, -0.014771261252462864, 0.6209980249404907, -0.3366572856903076, -0.02099994756281376, 0.002718554809689522, 0.00705720204859972, -0.02958020567893982, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18358634412288666, 0.05243098363280296, 0.008844357915222645]} +{"t": 5.5821, "q": [-0.313533753156662, -0.013681404292583466, 0.015467639081180096, 0.6298950910568237, -0.3265298902988434, 0.02109861932694912, -0.3221155107021332, 0.030066022649407387, -0.01478465273976326, 0.620980978012085, -0.33666548132896423, -0.020999889820814133, 0.002731946762651205, 0.00706480210646987, -0.029585152864456177, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15758058428764343, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22378143668174744, 0.023093601688742638, 0.9780685901641846, -0.18356238305568695, 0.052442967891693115, 0.008856342174112797]} +{"t": 5.5988, "q": [-0.3135252296924591, -0.013655837625265121, 0.015481031499803066, 0.6298695802688599, -0.3265298902988434, 0.02109861932694912, -0.3221069872379303, 0.030066022649407387, -0.014771261252462864, 0.6209639310836792, -0.33667370676994324, -0.020985309034585953, 0.002758730435743928, 0.007057209499180317, -0.029590027406811714, 0.29044967889785767, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.040890175849199295, 0.29502764344215393, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.6156, "q": [-0.313533753156662, -0.013672882691025734, 0.015481031499803066, 0.629878044128418, -0.3265298902988434, 0.02109861932694912, -0.3220984637737274, 0.030074546113610268, -0.01478465273976326, 0.6209468841552734, -0.33666548132896423, -0.020999889820814133, 0.0026783791836351156, 0.007049609441310167, -0.029585078358650208, 0.29042571783065796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.29503965377807617, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.0088803106918931]} +{"t": 5.6324, "q": [-0.313533753156662, -0.013672882691025734, 0.015467639081180096, 0.6298269629478455, -0.3265298902988434, 0.02109861932694912, -0.32212403416633606, 0.030066022649407387, -0.014811436645686626, 0.6209298372268677, -0.33666548132896423, -0.020999889820814133, 0.0027051628567278385, 0.007079987786710262, -0.02957540564239025, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9791830778121948, 0.15758058428764343, 0.06167082488536835, -0.040890175849199295, 0.29502764344215393, -0.22380541265010834, 0.023069633170962334, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 5.6491, "q": [-0.31354227662086487, -0.013664361089468002, 0.015467639081180096, 0.6298184394836426, -0.3265298902988434, 0.02109861932694912, -0.3220643997192383, 0.030083067715168, -0.01479804515838623, 0.6209127902984619, -0.3366613984107971, -0.02099265716969967, 0.002718554809689522, 0.007064794655889273, -0.029575331136584282, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 5.6658, "q": [-0.3135252296924591, -0.013655837625265121, 0.015481031499803066, 0.6298184394836426, -0.3265257775783539, 0.02110590599477291, -0.3220473527908325, 0.030083067715168, -0.01478465273976326, 0.6208616495132446, -0.3366572856903076, -0.020985424518585205, 0.0026649872306734324, 0.00704960199072957, -0.029575256630778313, 0.2904137372970581, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 5.6826, "q": [-0.3135167062282562, -0.013655837625265121, 0.015481031499803066, 0.6298099160194397, -0.3265298902988434, 0.02109861932694912, -0.32198768854141235, 0.030083067715168, -0.01479804515838623, 0.620836079120636, -0.3366613984107971, -0.02099265716969967, 0.0027051628567278385, 0.007064787205308676, -0.029565509408712387, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 5.6993, "q": [-0.3134911358356476, -0.013672882691025734, 0.0154542475938797, 0.6298099160194397, -0.3265340030193329, 0.021105842664837837, -0.32193654775619507, 0.03009158931672573, -0.01479804515838623, 0.6208105683326721, -0.3366613984107971, -0.02099265716969967, 0.0027051628567278385, 0.007064787205308676, -0.029565509408712387, 0.29042571783065796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 5.716, "q": [-0.31350818276405334, -0.013638794422149658, 0.0154542475938797, 0.629784345626831, -0.3265298902988434, 0.02109861932694912, -0.32194507122039795, 0.030083067715168, -0.014838220551609993, 0.6207168102264404, -0.33666959404945374, -0.021007122471928596, 0.0027051628567278385, 0.00704960199072957, -0.029575256630778313, 0.29042571783065796, 0.21594375371932983, -0.005201153922826052, 0.9791951179504395, 0.15755660831928253, 0.06167082488536835, -0.040890175849199295, 0.2950636148452759, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008832373656332493]} +{"t": 5.7328, "q": [-0.3134826123714447, -0.013664361089468002, 0.0154542475938797, 0.6297502517700195, -0.3265257775783539, 0.021091395989060402, -0.3219109773635864, 0.030083067715168, -0.014838220551609993, 0.6207082867622375, -0.33666548132896423, -0.020999889820814133, 0.002691771136596799, 0.007133121136575937, -0.029521644115447998, 0.2904137372970581, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05240701511502266, 0.008856342174112797]} +{"t": 5.7498, "q": [-0.3134826123714447, -0.01364731602370739, 0.015440856106579304, 0.6297417283058167, -0.3265298902988434, 0.02109861932694912, -0.3218854069709778, 0.03009158931672573, -0.014811436645686626, 0.6206912398338318, -0.33666548132896423, -0.020999889820814133, 0.002731946762651205, 0.007110335864126682, -0.029526444151997566, 0.29042571783065796, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.157616525888443, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.7665, "q": [-0.3134826123714447, -0.01364731602370739, 0.015467639081180096, 0.6297332048416138, -0.3265298902988434, 0.02111312933266163, -0.3218683898448944, 0.03009158931672573, -0.014811436645686626, 0.6206486225128174, -0.33667370676994324, -0.020985309034585953, 0.002718554809689522, 0.007102735806256533, -0.02952149510383606, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.7833, "q": [-0.3134826123714447, -0.013655837625265121, 0.015467639081180096, 0.629716157913208, -0.3265257775783539, 0.02110590599477291, -0.3218342959880829, 0.03009158931672573, -0.014824828132987022, 0.6206145286560059, -0.3366531729698181, -0.021007239818572998, 0.002731946762651205, 0.007095127832144499, -0.029506726190447807, 0.29044967889785767, 0.2159557342529297, -0.005213138181716204, 0.9791830778121948, 0.15756858885288239, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008868326433002949]} +{"t": 5.8, "q": [-0.31346556544303894, -0.01364731602370739, 0.0154542475938797, 0.6297076344490051, -0.3265257775783539, 0.02110590599477291, -0.32182577252388, 0.030083067715168, -0.014811436645686626, 0.6205889582633972, -0.3366572856903076, -0.02099994756281376, 0.002691771136596799, 0.0071027278900146484, -0.029511673375964165, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15765248239040375, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22378143668174744, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 5.8168, "q": [-0.3134485185146332, -0.013638794422149658, 0.015467639081180096, 0.6296905875205994, -0.3265298902988434, 0.02109861932694912, -0.32182577252388, 0.030100110918283463, -0.014838220551609993, 0.6205548644065857, -0.336649090051651, -0.021014530211687088, 0.002731946762651205, 0.0071027204394340515, -0.02950185164809227, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.040890175849199295, 0.29503965377807617, -0.22378143668174744, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.8336, "q": [-0.3134399950504303, -0.013638794422149658, 0.0154542475938797, 0.6296905875205994, -0.3265257775783539, 0.02110590599477291, -0.3217746317386627, 0.030100110918283463, -0.014824828132987022, 0.6205037236213684, -0.3366572856903076, -0.020985424518585205, 0.002758730435743928, 0.007110306061804295, -0.029487160965800285, 0.2904376983642578, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.0409141443669796, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 5.8503, "q": [-0.31342294812202454, -0.013638794422149658, 0.015427463687956333, 0.6296394467353821, -0.3265298902988434, 0.02109861932694912, -0.3217746317386627, 0.030100110918283463, -0.014838220551609993, 0.6204696297645569, -0.336649090051651, -0.02098548412322998, 0.0027051628567278385, 0.0071254912763834, -0.02947741374373436, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 5.8671, "q": [-0.3134314715862274, -0.013621749356389046, 0.015427463687956333, 0.6296138763427734, -0.3265422582626343, 0.021105796098709106, -0.3217490613460541, 0.030100110918283463, -0.014838220551609993, 0.6203929781913757, -0.3366572856903076, -0.020985424518585205, 0.0027051628567278385, 0.0071254912763834, -0.02947741374373436, 0.2904376983642578, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008868326433002949]} +{"t": 5.8838, "q": [-0.31341442465782166, -0.013630270957946777, 0.015400679782032967, 0.6295627355575562, -0.3265298902988434, 0.02109861932694912, -0.3217320144176483, 0.030108634382486343, -0.014851612038910389, 0.6203588843345642, -0.336649090051651, -0.02098548412322998, 0.002731946762651205, 0.007140676956623793, -0.029467666521668434, 0.29044967889785767, 0.2159797102212906, -0.005225121974945068, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 5.9006, "q": [-0.31340593099594116, -0.013630270957946777, 0.015427463687956333, 0.6295201182365417, -0.3265298902988434, 0.02109861932694912, -0.32171496748924255, 0.03009158931672573, -0.014851612038910389, 0.6203333139419556, -0.3366572856903076, -0.020985424518585205, 0.002691771136596799, 0.007140670903027058, -0.029457861557602882, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04087819159030914, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.9173, "q": [-0.31340593099594116, -0.013621749356389046, 0.015414072200655937, 0.6295286417007446, -0.3265257775783539, 0.02110590599477291, -0.3216553330421448, 0.030100110918283463, -0.014851612038910389, 0.6202566027641296, -0.3366572856903076, -0.020985424518585205, 0.0026649872306734324, 0.007125479634851217, -0.029457805678248405, 0.2904736399650574, 0.21596772968769073, -0.005225121974945068, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.9343, "q": [-0.31340593099594116, -0.013613227754831314, 0.01538728829473257, 0.6294775605201721, -0.3265340030193329, 0.021105842664837837, -0.32166385650634766, 0.030100110918283463, -0.014905179850757122, 0.620162844657898, -0.3366572856903076, -0.020985424518585205, 0.002718554809689522, 0.007171047385782003, -0.029448170214891434, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.951, "q": [-0.3133974075317383, -0.013604706153273582, 0.015347111970186234, 0.629417896270752, -0.3265340030193329, 0.021105842664837837, -0.32166385650634766, 0.03009158931672573, -0.014918571338057518, 0.6200435757637024, -0.3366572856903076, -0.020985424518585205, 0.0027051628567278385, 0.00714826351031661, -0.029452988877892494, 0.2904616594314575, 0.21596772968769073, -0.005201153922826052, 0.9791951179504395, 0.15760454535484314, 0.06169478967785835, -0.04090216010808945, 0.29507559537887573, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008832373656332493]} +{"t": 5.9677, "q": [-0.3133888840675354, -0.013604706153273582, 0.015347111970186234, 0.6293582320213318, -0.3265340030193329, 0.021105842664837837, -0.32167237997055054, 0.03009158931672573, -0.014905179850757122, 0.6200180053710938, -0.336649090051651, -0.020970961079001427, 0.002691771136596799, 0.00714826351031661, -0.029452988877892494, 0.2904376983642578, 0.2159797102212906, -0.005225121974945068, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22378143668174744, 0.02310558594763279, 0.9780805706977844, -0.1835503876209259, 0.05241899937391281, 0.008856342174112797]} +{"t": 5.9846, "q": [-0.3133974075317383, -0.013596182689070702, 0.015347111970186234, 0.6292900443077087, -0.3265298902988434, 0.02111312933266163, -0.3216809034347534, 0.030100110918283463, -0.014905179850757122, 0.6199157238006592, -0.3366572856903076, -0.020985424518585205, 0.0027453387156128883, 0.007186232600361109, -0.029438422992825508, 0.2904616594314575, 0.21594375371932983, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008844357915222645]} +{"t": 6.0013, "q": [-0.3133974075317383, -0.013613227754831314, 0.015347111970186234, 0.6292048096656799, -0.3265381455421448, 0.021113082766532898, -0.3216809034347534, 0.030108634382486343, -0.014891788363456726, 0.6198475360870361, -0.3366572856903076, -0.020985424518585205, 0.002691771136596799, 0.007148257456719875, -0.029443183913826942, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.018, "q": [-0.31340593099594116, -0.013621749356389046, 0.015333720482885838, 0.6291195750236511, -0.3265298902988434, 0.02109861932694912, -0.3216894268989563, 0.030117155984044075, -0.01486500445753336, 0.6197367310523987, -0.3366532027721405, -0.02097819373011589, 0.002758730435743928, 0.007186220958828926, -0.029418814927339554, 0.2904616594314575, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.040890175849199295, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.0351, "q": [-0.31341442465782166, -0.013613227754831314, 0.015320328995585442, 0.6290770173072815, -0.3265340030193329, 0.021105842664837837, -0.3216809034347534, 0.030108634382486343, -0.014878395944833755, 0.6196515560150146, -0.336649090051651, -0.020970961079001427, 0.002691771136596799, 0.007155856117606163, -0.029448114335536957, 0.2904376983642578, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15765248239040375, 0.06167082488536835, -0.04090216010808945, 0.29502764344215393, -0.2237934172153473, 0.023081617429852486, 0.9780805706977844, -0.18358634412288666, 0.05241899937391281, 0.008868326433002949]} +{"t": 6.0519, "q": [-0.3133974075317383, -0.013596182689070702, 0.015320328995585442, 0.6290684938430786, -0.3265298902988434, 0.02109861932694912, -0.3216468095779419, 0.030108634382486343, -0.014931963756680489, 0.6196174621582031, -0.3366532027721405, -0.02097819373011589, 0.002691771136596799, 0.007171041332185268, -0.02943836711347103, 0.29044967889785767, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.0409141443669796, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 6.0686, "q": [-0.3134314715862274, -0.013596182689070702, 0.015306936576962471, 0.6290343999862671, -0.3265381455421448, 0.021113082766532898, -0.3216468095779419, 0.030125677585601807, -0.014878395944833755, 0.6195748448371887, -0.3366532027721405, -0.02097819373011589, 0.0026783791836351156, 0.007171035744249821, -0.02942856401205063, 0.2904616594314575, 0.2159797102212906, -0.005213138181716204, 0.9791951179504395, 0.1575925648212433, 0.06169478967785835, -0.04090216010808945, 0.29503965377807617, -0.2237934172153473, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 6.0854, "q": [-0.31341442465782166, -0.01358766108751297, 0.015320328995585442, 0.6289917826652527, -0.3265340030193329, 0.021105842664837837, -0.3216553330421448, 0.030125677585601807, -0.014891788363456726, 0.6194725632667542, -0.336649090051651, -0.020970961079001427, 0.0026649872306734324, 0.007193813566118479, -0.029413942247629166, 0.2904616594314575, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 6.1023, "q": [-0.31342294812202454, -0.013613227754831314, 0.015320328995585442, 0.6289406418800354, -0.3265340030193329, 0.021105842664837837, -0.3216468095779419, 0.030134201049804688, -0.014878395944833755, 0.6193873286247253, -0.3366450071334839, -0.020978251472115517, 0.002691771136596799, 0.007178616244345903, -0.029404081404209137, 0.2904616594314575, 0.21594375371932983, -0.0051891696639359, 0.9791830778121948, 0.15758058428764343, 0.0616828091442585, -0.0409141443669796, 0.2950156629085541, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.1835264265537262, 0.05243098363280296, 0.008856342174112797]} +{"t": 6.119, "q": [-0.31341442465782166, -0.01358766108751297, 0.015320328995585442, 0.6289406418800354, -0.3265298902988434, 0.02111312933266163, -0.3215530514717102, 0.03014272265136242, -0.014905179850757122, 0.6193873286247253, -0.3366408944129944, -0.02100006490945816, 0.002691771136596799, 0.007148239761590958, -0.029413774609565735, 0.29040175676345825, 0.2159557342529297, -0.005201153922826052, 0.9791830778121948, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780685901641846, -0.1835503876209259, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.1358, "q": [-0.31342294812202454, -0.01358766108751297, 0.015347111970186234, 0.6289491653442383, -0.3265381455421448, 0.021113082766532898, -0.32153600454330444, 0.03015124425292015, -0.014891788363456726, 0.6193192005157471, -0.3366532027721405, -0.02097819373011589, 0.002691771136596799, 0.007140625733882189, -0.029379436746239662, 0.29040175676345825, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.040890175849199295, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 6.1526, "q": [-0.3133974075317383, -0.013579139485955238, 0.015360504388809204, 0.6289406418800354, -0.3265257775783539, 0.02110590599477291, -0.32153600454330444, 0.03015124425292015, -0.014931963756680489, 0.6193192005157471, -0.33663269877433777, -0.021000124514102936, 0.002718554809689522, 0.007178594823926687, -0.029364870861172676, 0.2904137372970581, 0.21596772968769073, -0.0051891696639359, 0.9791830778121948, 0.15756858885288239, 0.0616828091442585, -0.0409141443669796, 0.2950036823749542, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18353840708732605, 0.05240701511502266, 0.008856342174112797]} +{"t": 6.1693, "q": [-0.3133974075317383, -0.013570617884397507, 0.015360504388809204, 0.6289406418800354, -0.3265298902988434, 0.02109861932694912, -0.3215104341506958, 0.03015124425292015, -0.014918571338057518, 0.6192936301231384, -0.3366367816925049, -0.02100735530257225, 0.002731946762651205, 0.007193774450570345, -0.0293453186750412, 0.2903897762298584, 0.21594375371932983, -0.005201153922826052, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008856342174112797]} +{"t": 6.186, "q": [-0.3133888840675354, -0.013596182689070702, 0.0153738958761096, 0.6289406418800354, -0.3265340030193329, 0.021105842664837837, -0.32147637009620667, 0.030159765854477882, -0.014905179850757122, 0.6192936301231384, -0.33663269877433777, -0.021000124514102936, 0.002691771136596799, 0.007186163682490587, -0.02932078205049038, 0.29040175676345825, 0.2159797102212906, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023069633170962334, 0.9780805706977844, -0.18356238305568695, 0.052383050322532654, 0.008832373656332493]} +{"t": 6.2029, "q": [-0.31337183713912964, -0.01358766108751297, 0.01538728829473257, 0.6289406418800354, -0.326521635055542, 0.021113192662596703, -0.3214252293109894, 0.030159765854477882, -0.014918571338057518, 0.6192765831947327, -0.33663269877433777, -0.021000124514102936, 0.002691771136596799, 0.0071861459873616695, -0.029291372746229172, 0.29037776589393616, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15760454535484314, 0.06167082488536835, -0.040890175849199295, 0.2950156629085541, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 6.2196, "q": [-0.31327807903289795, -0.013579139485955238, 0.015360504388809204, 0.6289321184158325, -0.3265298902988434, 0.02109861932694912, -0.3213314712047577, 0.03015124425292015, -0.014905179850757122, 0.6192680597305298, -0.33663269877433777, -0.021000124514102936, 0.0026649872306734324, 0.007239282596856356, -0.029237648472189903, 0.29037776589393616, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.1576405018568039, 0.0616828091442585, -0.0409141443669796, 0.2950036823749542, -0.2238173931837082, 0.023081617429852486, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008844357915222645]} +{"t": 6.2365, "q": [-0.31319287419319153, -0.013570617884397507, 0.015400679782032967, 0.6289321184158325, -0.3265298902988434, 0.02109861932694912, -0.3212292194366455, 0.030168289318680763, -0.014918571338057518, 0.6192765831947327, -0.33663269877433777, -0.021000124514102936, 0.0026783791836351156, 0.007239239756017923, -0.029169024899601936, 0.29040175676345825, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.157616525888443, 0.0616828091442585, -0.0409141443669796, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 6.2533, "q": [-0.31313320994377136, -0.013596182689070702, 0.0153738958761096, 0.6289321184158325, -0.3265257775783539, 0.02110590599477291, -0.32116955518722534, 0.030176810920238495, -0.014931963756680489, 0.6192595362663269, -0.33662858605384827, -0.021007433533668518, 0.002691771136596799, 0.007246814668178558, -0.029134739190340042, 0.2904137372970581, 0.2159797102212906, -0.005201153922826052, 0.9792190790176392, 0.15760454535484314, 0.0616828091442585, -0.040890175849199295, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008856342174112797]} +{"t": 6.27, "q": [-0.3130309283733368, -0.013579139485955238, 0.01538728829473257, 0.6289321184158325, -0.3265257775783539, 0.02110590599477291, -0.3211354613304138, 0.030176810920238495, -0.014918571338057518, 0.6192595362663269, -0.33662447333335876, -0.021000200882554054, 0.002718554809689522, 0.007360704708844423, -0.029061632230877876, 0.29040175676345825, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.157616525888443, 0.0616828091442585, -0.040890175849199295, 0.2950036823749542, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.2867, "q": [-0.3129883408546448, -0.013570617884397507, 0.0153738958761096, 0.6289065480232239, -0.3265257775783539, 0.02110590599477291, -0.3210587799549103, 0.030185332521796227, -0.014931963756680489, 0.619251012802124, -0.33662858605384827, -0.020992891862988472, 0.002691771136596799, 0.007368279621005058, -0.029027346521615982, 0.29042571783065796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.0409141443669796, 0.2950036823749542, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.3036, "q": [-0.3129883408546448, -0.01358766108751297, 0.0153738958761096, 0.6288809776306152, -0.3265257775783539, 0.02110590599477291, -0.3210502564907074, 0.030185332521796227, -0.014931963756680489, 0.6192595362663269, -0.33663269877433777, -0.021000124514102936, 0.002691771136596799, 0.007383470889180899, -0.02902740240097046, 0.29042571783065796, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 6.3204, "q": [-0.3129883408546448, -0.013570617884397507, 0.015347111970186234, 0.6288639307022095, -0.3265340030193329, 0.021105842664837837, -0.32103320956230164, 0.030193854123353958, -0.014945355243980885, 0.6192424893379211, -0.33662858605384827, -0.020992891862988472, 0.002691771136596799, 0.007421433925628662, -0.02900303341448307, 0.29042571783065796, 0.2159797102212906, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22378143668174744, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 6.3371, "q": [-0.3129883408546448, -0.013562094420194626, 0.015347111970186234, 0.6288809776306152, -0.3265298902988434, 0.02109861932694912, -0.32103320956230164, 0.030185332521796227, -0.014931963756680489, 0.6192169189453125, -0.33662858605384827, -0.020992891862988472, 0.002731946762651205, 0.007375883869826794, -0.0290420800447464, 0.29040175676345825, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.0409141443669796, 0.295051634311676, -0.2237934172153473, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 6.354, "q": [-0.31294572353363037, -0.013553572818636894, 0.015347111970186234, 0.6288639307022095, -0.3265257775783539, 0.02110590599477291, -0.3210417330265045, 0.030193854123353958, -0.014918571338057518, 0.6192339658737183, -0.33663269877433777, -0.020985601469874382, 0.002731946762651205, 0.007391063496470451, -0.02902252972126007, 0.2904137372970581, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.0409141443669796, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.3707, "q": [-0.3129372000694275, -0.013553572818636894, 0.015320328995585442, 0.6288383603096008, -0.3265298902988434, 0.02109861932694912, -0.3210161626338959, 0.030193854123353958, -0.014918571338057518, 0.6192169189453125, -0.33663269877433777, -0.021000124514102936, 0.002691771136596799, 0.007413847371935844, -0.02901771105825901, 0.2904376983642578, 0.21594375371932983, -0.005213138181716204, 0.9791951179504395, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 6.3874, "q": [-0.31292015314102173, -0.013553572818636894, 0.015306936576962471, 0.6288383603096008, -0.3265298902988434, 0.02109861932694912, -0.32102468609809875, 0.030185332521796227, -0.014931963756680489, 0.6192083954811096, -0.3366367816925049, -0.020992834120988846, 0.002718554809689522, 0.007368285208940506, -0.029037151485681534, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22378143668174744, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 6.4042, "q": [-0.31292015314102173, -0.013553572818636894, 0.015293545089662075, 0.6288213133811951, -0.3265257775783539, 0.02110590599477291, -0.32103320956230164, 0.030193854123353958, -0.014958747662603855, 0.6191913485527039, -0.33663681149482727, -0.020978311076760292, 0.002718554809689522, 0.007375883869826794, -0.0290420800447464, 0.2904616594314575, 0.2159557342529297, -0.005225121974945068, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.1835743635892868, 0.05243098363280296, 0.008856342174112797]} +{"t": 6.4209, "q": [-0.31290310621261597, -0.01352800615131855, 0.015293545089662075, 0.6287616491317749, -0.3265298902988434, 0.02108410932123661, -0.3210417330265045, 0.03020237758755684, -0.014905179850757122, 0.619182825088501, -0.3366408944129944, -0.020985541865229607, 0.002718554809689522, 0.007398668210953474, -0.02903726138174534, 0.2904736399650574, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.4376, "q": [-0.31291162967681885, -0.01352800615131855, 0.015293545089662075, 0.6287105679512024, -0.3265257775783539, 0.021091395989060402, -0.3210587799549103, 0.030193854123353958, -0.014905179850757122, 0.6191146373748779, -0.33663269877433777, -0.020985601469874382, 0.002731946762651205, 0.007383482530713081, -0.029047010466456413, 0.2904616594314575, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.040890175849199295, 0.29502764344215393, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.4543, "q": [-0.31292015314102173, -0.01352800615131855, 0.015280152671039104, 0.6286935210227966, -0.3265381157398224, 0.021084045991301537, -0.3210587799549103, 0.030193854123353958, -0.014918571338057518, 0.6190890669822693, -0.3366408944129944, -0.020985541865229607, 0.002758730435743928, 0.007383494637906551, -0.02906661666929722, 0.29044967889785767, 0.2159797102212906, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 6.4711, "q": [-0.3129286766052246, -0.013536527752876282, 0.015306936576962471, 0.6286764740943909, -0.3265298902988434, 0.02109861932694912, -0.32106730341911316, 0.030193854123353958, -0.014931963756680489, 0.6190634965896606, -0.3366408944129944, -0.020985541865229607, 0.002691771136596799, 0.007375895977020264, -0.029061686247587204, 0.2904616594314575, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 6.4878, "q": [-0.3129372000694275, -0.013519484549760818, 0.015266761183738708, 0.6286509037017822, -0.3265257775783539, 0.02110590599477291, -0.3210587799549103, 0.03020237758755684, -0.014945355243980885, 0.619037926197052, -0.336649090051651, -0.020970961079001427, 0.0027453387156128883, 0.007368297316133976, -0.02905675768852234, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15754462778568268, 0.06167082488536835, -0.040890175849199295, 0.2950636148452759, -0.22378143668174744, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 6.5045, "q": [-0.3129286766052246, -0.013545051217079163, 0.015280152671039104, 0.6286168098449707, -0.3265257775783539, 0.02110590599477291, -0.32106730341911316, 0.03020237758755684, -0.014945355243980885, 0.6189953088760376, -0.3366408944129944, -0.020971018821001053, 0.002718554809689522, 0.007360698655247688, -0.029051827266812325, 0.29042571783065796, 0.2159797102212906, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 6.5214, "q": [-0.31296277046203613, -0.013545051217079163, 0.015280152671039104, 0.6285741925239563, -0.3265298902988434, 0.02109861932694912, -0.32106730341911316, 0.03020237758755684, -0.014891788363456726, 0.6189101338386536, -0.3366449773311615, -0.02099277451634407, 0.002731946762651205, 0.007375883869826794, -0.0290420800447464, 0.29040175676345825, 0.2159557342529297, -0.005213138181716204, 0.9791830778121948, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 6.5383, "q": [-0.31294572353363037, -0.013553572818636894, 0.015280152671039104, 0.6285400986671448, -0.3265257775783539, 0.021091395989060402, -0.32103320956230164, 0.03020237758755684, -0.014918571338057518, 0.618876039981842, -0.3366367816925049, -0.020963769406080246, 0.0027051628567278385, 0.007368297316133976, -0.02905675768852234, 0.29042571783065796, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008856342174112797]} +{"t": 6.555, "q": [-0.31291162967681885, -0.01352800615131855, 0.015306936576962471, 0.628523051738739, -0.3265257775783539, 0.02110590599477291, -0.32103320956230164, 0.03020237758755684, -0.014918571338057518, 0.6188248991966248, -0.3366367816925049, -0.020992834120988846, 0.002691771136596799, 0.007345507387071848, -0.029051773250102997, 0.29040175676345825, 0.2159557342529297, -0.005213138181716204, 0.9791830778121948, 0.15760454535484314, 0.0616828091442585, -0.040890175849199295, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05243098363280296, 0.008856342174112797]} +{"t": 6.5718, "q": [-0.31294572353363037, -0.013545051217079163, 0.015320328995585442, 0.6284889578819275, -0.3265340030193329, 0.021105842664837837, -0.3210161626338959, 0.03020237758755684, -0.014918571338057518, 0.6187737584114075, -0.33663269877433777, -0.021000124514102936, 0.002731946762651205, 0.007368291262537241, -0.029046954587101936, 0.29042571783065796, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008856342174112797]} +{"t": 6.5885, "q": [-0.3129372000694275, -0.013536527752876282, 0.015333720482885838, 0.6284719109535217, -0.3265381157398224, 0.021084045991301537, -0.321007639169693, 0.03020237758755684, -0.014918571338057518, 0.6187481880187988, -0.33662858605384827, -0.020992891862988472, 0.002691771136596799, 0.007353088352829218, -0.029027290642261505, 0.2904137372970581, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15762852132320404, 0.06167082488536835, -0.040890175849199295, 0.2950036823749542, -0.22380541265010834, 0.023069633170962334, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 6.6053, "q": [-0.31292015314102173, -0.013553572818636894, 0.015333720482885838, 0.6284463405609131, -0.3265257775783539, 0.02110590599477291, -0.3209564983844757, 0.03020237758755684, -0.014918571338057518, 0.6186970472335815, -0.33662447333335876, -0.020985642448067665, 0.002691771136596799, 0.007330292370170355, -0.02901250310242176, 0.29037776589393616, 0.21594375371932983, -0.005201153922826052, 0.9791830778121948, 0.1575925648212433, 0.06167082488536835, -0.040890175849199295, 0.29499170184135437, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008844357915222645]} +{"t": 6.622, "q": [-0.3129372000694275, -0.013553572818636894, 0.015360504388809204, 0.6284037232398987, -0.3265381157398224, 0.021084045991301537, -0.3209564983844757, 0.030193854123353958, -0.014918571338057518, 0.61866295337677, -0.33662447333335876, -0.020985642448067665, 0.002731946762651205, 0.007330280262976885, -0.028992895036935806, 0.29040175676345825, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.1576405018568039, 0.0616828091442585, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 6.6389, "q": [-0.31290310621261597, -0.013545051217079163, 0.015347111970186234, 0.6283951997756958, -0.3265298902988434, 0.02109861932694912, -0.32087981700897217, 0.03020237758755684, -0.014918571338057518, 0.6186288595199585, -0.33662858605384827, -0.020992891862988472, 0.002691771136596799, 0.007322681602090597, -0.02898796647787094, 0.29040175676345825, 0.21594375371932983, -0.005201153922826052, 0.9792070984840393, 0.1575925648212433, 0.06167082488536835, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780685901641846, -0.1835743635892868, 0.05243098363280296, 0.008868326433002949]} +{"t": 6.6556, "q": [-0.31290310621261597, -0.013553572818636894, 0.015333720482885838, 0.62837815284729, -0.3265298902988434, 0.02109861932694912, -0.32088834047317505, 0.03021089918911457, -0.014958747662603855, 0.6186118125915527, -0.33663269877433777, -0.020971059799194336, 0.002691771136596799, 0.007383422926068306, -0.02894897572696209, 0.29042571783065796, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15760454535484314, 0.06167082488536835, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.052442967891693115, 0.0088803106918931]} +{"t": 6.6724, "q": [-0.3128775358200073, -0.013545051217079163, 0.015333720482885838, 0.6283696293830872, -0.3265381455421448, 0.021113082766532898, -0.32083719968795776, 0.03020237758755684, -0.014958747662603855, 0.6185862421989441, -0.33663269877433777, -0.020971059799194336, 0.002718554809689522, 0.007391003891825676, -0.028924494981765747, 0.29040175676345825, 0.2159557342529297, -0.005225121974945068, 0.9792070984840393, 0.15762852132320404, 0.06169478967785835, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780685901641846, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 6.6893, "q": [-0.31286048889160156, -0.013545051217079163, 0.015306936576962471, 0.6283526420593262, -0.3265340030193329, 0.021105842664837837, -0.3208286762237549, 0.03020237758755684, -0.014945355243980885, 0.6186032891273499, -0.33663269877433777, -0.020985601469874382, 0.002758730435743928, 0.007391003891825676, -0.028924494981765747, 0.2904137372970581, 0.2159557342529297, -0.005201153922826052, 0.9791951179504395, 0.15760454535484314, 0.0616828091442585, -0.040890175849199295, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05243098363280296, 0.008868326433002949]} +{"t": 6.706, "q": [-0.3128434419631958, -0.013553572818636894, 0.015320328995585442, 0.6283526420593262, -0.3265340030193329, 0.021105842664837837, -0.32080310583114624, 0.030193854123353958, -0.014931963756680489, 0.6186032891273499, -0.3366408944129944, -0.020985541865229607, 0.002718554809689522, 0.007398590445518494, -0.028909817337989807, 0.29040175676345825, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.2950156629085541, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.7227, "q": [-0.31281790137290955, -0.013536527752876282, 0.015320328995585442, 0.6283100247383118, -0.3265257775783539, 0.02110590599477291, -0.32080310583114624, 0.03020237758755684, -0.014958747662603855, 0.618594765663147, -0.3366408944129944, -0.020985541865229607, 0.002731946762651205, 0.007444152608513832, -0.028890376910567284, 0.29042571783065796, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.7395, "q": [-0.31280937790870667, -0.013553572818636894, 0.015293545089662075, 0.6282674074172974, -0.3265257775783539, 0.02110590599477291, -0.320820152759552, 0.030193854123353958, -0.014945355243980885, 0.6185692548751831, -0.3366450071334839, -0.020963728427886963, 0.002718554809689522, 0.007436553947627544, -0.02888544835150242, 0.2904137372970581, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.1575925648212433, 0.0616828091442585, -0.040890175849199295, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.7562, "q": [-0.31281790137290955, -0.013519484549760818, 0.015280152671039104, 0.628224790096283, -0.3265298902988434, 0.02109861932694912, -0.32080310583114624, 0.03020237758755684, -0.014931963756680489, 0.6185266375541687, -0.336649090051651, -0.020970961079001427, 0.002691771136596799, 0.00745173916220665, -0.028875699266791344, 0.2904376983642578, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04090216010808945, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.7731, "q": [-0.3128008544445038, -0.013510962948203087, 0.015253368765115738, 0.628224790096283, -0.3265340030193329, 0.021091332659125328, -0.320820152759552, 0.03020237758755684, -0.014945355243980885, 0.6184925436973572, -0.33663681149482727, -0.020978311076760292, 0.0027453387156128883, 0.007413770072162151, -0.02889026515185833, 0.2903897762298584, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.04086620733141899, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.7898, "q": [-0.3128264248371124, -0.013502439484000206, 0.015266761183738708, 0.6281906962394714, -0.3265463709831238, 0.021083982661366463, -0.3208542466163635, 0.030193854123353958, -0.014931963756680489, 0.6184499263763428, -0.33665731549263, -0.020941857248544693, 0.002758730435743928, 0.007390991784632206, -0.028904886916279793, 0.2904137372970581, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.1835743635892868, 0.05241899937391281, 0.008844357915222645]} +{"t": 6.8066, "q": [-0.3128264248371124, -0.01346835121512413, 0.015239977277815342, 0.6281310319900513, -0.3265422582626343, 0.021091269329190254, -0.3209053874015808, 0.03020237758755684, -0.014958747662603855, 0.6183732151985168, -0.3366655111312866, -0.020941779017448425, 0.002691771136596799, 0.007421380374580622, -0.02891480177640915, 0.2904616594314575, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29502764344215393, -0.2237934172153473, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 6.8233, "q": [-0.3128434419631958, -0.013476874679327011, 0.015239977277815342, 0.6280969381332397, -0.3265340030193329, 0.021091332659125328, -0.3209139108657837, 0.030193854123353958, -0.014958747662603855, 0.6183391213417053, -0.3366655111312866, -0.020941779017448425, 0.002691771136596799, 0.007406201213598251, -0.028934353962540627, 0.2904616594314575, 0.21594375371932983, -0.00523710623383522, 0.9792070984840393, 0.15758058428764343, 0.06169478967785835, -0.04090216010808945, 0.29502764344215393, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 6.84, "q": [-0.31286901235580444, -0.01346835121512413, 0.015253368765115738, 0.628079891204834, -0.3265340030193329, 0.021091332659125328, -0.3209139108657837, 0.030193854123353958, -0.014931963756680489, 0.6183220744132996, -0.336665540933609, -0.02092725783586502, 0.002731946762651205, 0.007406201213598251, -0.028934353962540627, 0.2904616594314575, 0.21594375371932983, -0.005201153922826052, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008832373656332493]} +{"t": 6.8568, "q": [-0.3128434419631958, -0.013485396280884743, 0.015226585790514946, 0.6280202269554138, -0.3265381157398224, 0.021098555997014046, -0.3209053874015808, 0.030193854123353958, -0.014918571338057518, 0.6182794570922852, -0.3366655111312866, -0.020941779017448425, 0.002758730435743928, 0.007406207267194986, -0.02894415706396103, 0.29044967889785767, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835503876209259, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.8736, "q": [-0.3128434419631958, -0.01346835121512413, 0.015239977277815342, 0.6279776692390442, -0.3265381157398224, 0.021098555997014046, -0.3209224343299866, 0.03020237758755684, -0.014918571338057518, 0.618219792842865, -0.33665731549263, -0.020941857248544693, 0.0027051628567278385, 0.007398608606308699, -0.028939226642251015, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9791830778121948, 0.15752065181732178, 0.0616828091442585, -0.04087819159030914, 0.295051634311676, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008868326433002949]} +{"t": 6.8903, "q": [-0.3128434419631958, -0.013459829613566399, 0.015239977277815342, 0.6279350519180298, -0.3265463709831238, 0.021098509430885315, -0.3209224343299866, 0.030185332521796227, -0.014945355243980885, 0.6181260943412781, -0.3366532027721405, -0.020949147641658783, 0.002718554809689522, 0.007383422926068306, -0.02894897572696209, 0.2904376983642578, 0.21596772968769073, -0.005201153922826052, 0.9791951179504395, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008844357915222645]} +{"t": 6.9071, "q": [-0.3128434419631958, -0.013459829613566399, 0.015239977277815342, 0.6279009580612183, -0.3265381157398224, 0.021084045991301537, -0.3209139108657837, 0.030193854123353958, -0.014931963756680489, 0.6181175708770752, -0.3366532027721405, -0.020949147641658783, 0.002718554809689522, 0.007383422926068306, -0.02894897572696209, 0.29042571783065796, 0.21594375371932983, -0.0051891696639359, 0.9791951179504395, 0.15756858885288239, 0.06167082488536835, -0.040890175849199295, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.924, "q": [-0.3128519654273987, -0.013485396280884743, 0.015253368765115738, 0.6278839111328125, -0.3265422582626343, 0.021091269329190254, -0.3209139108657837, 0.030193854123353958, -0.014945355243980885, 0.6180238127708435, -0.336649090051651, -0.020941896364092827, 0.002758730435743928, 0.007391015999019146, -0.028944101184606552, 0.2904376983642578, 0.21593177318572998, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04086620733141899, 0.29503965377807617, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18353840708732605, 0.05243098363280296, 0.008868326433002949]} +{"t": 6.9407, "q": [-0.31286048889160156, -0.013502439484000206, 0.015226585790514946, 0.6278157234191895, -0.3265422582626343, 0.021105796098709106, -0.3209053874015808, 0.03020237758755684, -0.014918571338057518, 0.6179726719856262, -0.3366532027721405, -0.020949147641658783, 0.0027721223887056112, 0.007368231657892466, -0.028948919847607613, 0.2904376983642578, 0.21596772968769073, -0.005201153922826052, 0.9791830778121948, 0.15762852132320404, 0.06169478967785835, -0.040890175849199295, 0.29502764344215393, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 6.9575, "q": [-0.31286901235580444, -0.013493917882442474, 0.015253368765115738, 0.6277986764907837, -0.3265340030193329, 0.021105842664837837, -0.32087981700897217, 0.03020237758755684, -0.014918571338057518, 0.6179471015930176, -0.33665731549263, -0.020941857248544693, 0.002718554809689522, 0.007360639050602913, -0.028953792527318, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.06169478967785835, -0.04090216010808945, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008844357915222645]} +{"t": 6.9744, "q": [-0.31286901235580444, -0.013493917882442474, 0.015280152671039104, 0.6278157234191895, -0.3265381455421448, 0.021113082766532898, -0.32089686393737793, 0.03020237758755684, -0.014918571338057518, 0.6179300546646118, -0.3366655111312866, -0.020941779017448425, 0.002731946762651205, 0.007368226069957018, -0.02893911674618721, 0.29042571783065796, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.15760454535484314, 0.0616828091442585, -0.04090216010808945, 0.2950156629085541, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 6.9911, "q": [-0.3128775358200073, -0.013510962948203087, 0.015266761183738708, 0.6277986764907837, -0.3265422582626343, 0.021076759323477745, -0.32083719968795776, 0.03021089918911457, -0.014931963756680489, 0.617913007736206, -0.33663269877433777, -0.021000124514102936, 0.0026382035575807095, 0.007368226069957018, -0.02893911674618721, 0.29040175676345825, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.1576644629240036, 0.0616828091442585, -0.0409141443669796, 0.29499170184135437, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18358634412288666, 0.05243098363280296, 0.008856342174112797]} +{"t": 7.0078, "q": [-0.3128775358200073, -0.013519484549760818, 0.015280152671039104, 0.6277901530265808, -0.3265381455421448, 0.021113082766532898, -0.32079458236694336, 0.03020237758755684, -0.014958747662603855, 0.6178874373435974, -0.3366367816925049, -0.020992834120988846, 0.002691771136596799, 0.007368226069957018, -0.02893911674618721, 0.29040175676345825, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.040890175849199295, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008856342174112797]} +{"t": 7.0247, "q": [-0.3128519654273987, -0.013510962948203087, 0.015293545089662075, 0.6277986764907837, -0.3265381455421448, 0.021113082766532898, -0.32079458236694336, 0.03020237758755684, -0.014931963756680489, 0.6178789138793945, -0.3366367816925049, -0.020992834120988846, 0.002718554809689522, 0.007383393589407206, -0.028899958357214928, 0.29040175676345825, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023081617429852486, 0.9780685901641846, -0.18356238305568695, 0.05241899937391281, 0.0088803106918931]} +{"t": 7.0414, "q": [-0.31286048889160156, -0.01352800615131855, 0.015280152671039104, 0.6277986764907837, -0.3265340030193329, 0.021105842664837837, -0.32075196504592896, 0.03020237758755684, -0.014958747662603855, 0.6178874373435974, -0.3366367816925049, -0.020992834120988846, 0.002691771136596799, 0.007375794928520918, -0.028895027935504913, 0.29040175676345825, 0.2159557342529297, -0.0051891696639359, 0.9791830778121948, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.29499170184135437, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.0582, "q": [-0.3128519654273987, -0.01352800615131855, 0.015293545089662075, 0.6277986764907837, -0.3265298902988434, 0.02109861932694912, -0.32070085406303406, 0.03020237758755684, -0.014931963756680489, 0.6178874373435974, -0.3366367816925049, -0.020992834120988846, 0.002691771136596799, 0.007368178106844425, -0.02886068820953369, 0.2903897762298584, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.157616525888443, 0.0616828091442585, -0.04090216010808945, 0.29499170184135437, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008868326433002949]} +{"t": 7.0749, "q": [-0.31276676058769226, -0.013510962948203087, 0.015293545089662075, 0.627773106098175, -0.3265381455421448, 0.021113082766532898, -0.32066676020622253, 0.03020237758755684, -0.014918571338057518, 0.6178789138793945, -0.3366367816925049, -0.020992834120988846, 0.002718554809689522, 0.007421315182000399, -0.028806963935494423, 0.2903897762298584, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04087819159030914, 0.2949797213077545, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.1835503876209259, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.0916, "q": [-0.3127582371234894, -0.013519484549760818, 0.015293545089662075, 0.6277901530265808, -0.3265257775783539, 0.02110590599477291, -0.32065823674201965, 0.030193854123353958, -0.014945355243980885, 0.6179044842720032, -0.3366408944129944, -0.020956479012966156, 0.002718554809689522, 0.007451679557561874, -0.02877766452729702, 0.2903897762298584, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 7.1086, "q": [-0.3127411901950836, -0.013519484549760818, 0.015293545089662075, 0.6277901530265808, -0.3265298902988434, 0.02111312933266163, -0.32060709595680237, 0.03020237758755684, -0.014985531568527222, 0.6178959608078003, -0.33663269877433777, -0.020985601469874382, 0.002691771136596799, 0.007436482701450586, -0.02876780554652214, 0.29040175676345825, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.2950036823749542, -0.22378143668174744, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.1253, "q": [-0.3127070963382721, -0.013510962948203087, 0.015280152671039104, 0.6277645826339722, -0.3265340030193329, 0.021091332659125328, -0.3205985724925995, 0.03021089918911457, -0.014985531568527222, 0.6179044842720032, -0.33663269877433777, -0.020985601469874382, 0.0027855143416672945, 0.007512426003813744, -0.02874847874045372, 0.29040175676345825, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.04090216010808945, 0.2950036823749542, -0.22378143668174744, 0.023081617429852486, 0.9780805706977844, -0.1835503876209259, 0.05243098363280296, 0.008844357915222645]} +{"t": 7.142, "q": [-0.31259632110595703, -0.013510962948203087, 0.015266761183738708, 0.627773106098175, -0.3265340030193329, 0.021105842664837837, -0.3204622268676758, 0.03021089918911457, -0.014998923055827618, 0.617913007736206, -0.33663681149482727, -0.020978311076760292, 0.002691771136596799, 0.007482043467462063, -0.028748366981744766, 0.2904137372970581, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15758058428764343, 0.061706773936748505, -0.04090216010808945, 0.2950036823749542, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.1835503876209259, 0.05241899937391281, 0.008844357915222645]} +{"t": 7.1588, "q": [-0.3124769926071167, -0.013519484549760818, 0.015226585790514946, 0.6277645826339722, -0.3265340030193329, 0.021091332659125328, -0.32034292817115784, 0.030219420790672302, -0.015025706961750984, 0.617913007736206, -0.3366367816925049, -0.020963769406080246, 0.002691771136596799, 0.007520012557506561, -0.02873380109667778, 0.2904137372970581, 0.21594375371932983, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.040890175849199295, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.1756, "q": [-0.31235769391059875, -0.013519484549760818, 0.015239977277815342, 0.6277560591697693, -0.3265381157398224, 0.021084045991301537, -0.32020655274391174, 0.03021089918911457, -0.014998923055827618, 0.617913007736206, -0.33663269877433777, -0.020971059799194336, 0.002691771136596799, 0.007504827342927456, -0.028743548318743706, 0.29042571783065796, 0.21594375371932983, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.1923, "q": [-0.31217020750045776, -0.013485396280884743, 0.015226585790514946, 0.6277645826339722, -0.3265340030193329, 0.021091332659125328, -0.320002019405365, 0.030227944254875183, -0.015025706961750984, 0.6179044842720032, -0.3366408944129944, -0.020956479012966156, 0.002691771136596799, 0.0074896421283483505, -0.02875329554080963, 0.2904137372970581, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.157616525888443, 0.0616828091442585, -0.0409141443669796, 0.2950036823749542, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.209, "q": [-0.31203386187553406, -0.013459829613566399, 0.015226585790514946, 0.6277645826339722, -0.3265257775783539, 0.02110590599477291, -0.3198060393333435, 0.030236465856432915, -0.01505249086767435, 0.6179215312004089, -0.3366408944129944, -0.020956479012966156, 0.0027989062946289778, 0.00747445086017251, -0.028753241524100304, 0.29040175676345825, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.2258, "q": [-0.3118293285369873, -0.013459829613566399, 0.015173017978668213, 0.6277645826339722, -0.3265298902988434, 0.02108410932123661, -0.31959298253059387, 0.030244987457990646, -0.01503909844905138, 0.6179044842720032, -0.336649090051651, -0.020941896364092827, 0.002758730435743928, 0.007504815235733986, -0.0287239421159029, 0.29040175676345825, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.2425, "q": [-0.3116588890552521, -0.013451308012008667, 0.01519980188459158, 0.6277475357055664, -0.3265340030193329, 0.02107682265341282, -0.3194566071033478, 0.030236465856432915, -0.015079274773597717, 0.6178789138793945, -0.3366450071334839, -0.020963728427886963, 0.002758730435743928, 0.007512419950217009, -0.028738675639033318, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9791830778121948, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.2592, "q": [-0.31159070134162903, -0.013459829613566399, 0.015173017978668213, 0.6277475357055664, -0.3265381157398224, 0.021084045991301537, -0.3193202614784241, 0.030244987457990646, -0.015092666260898113, 0.6178703904151917, -0.3366450071334839, -0.020963728427886963, 0.0027989062946289778, 0.007520012557506561, -0.02873380109667778, 0.2904376983642578, 0.21594375371932983, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.2950036823749542, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 7.276, "q": [-0.31153956055641174, -0.013451308012008667, 0.015132841654121876, 0.6277304887771606, -0.3265340030193329, 0.021091332659125328, -0.31927764415740967, 0.030244987457990646, -0.015079274773597717, 0.617844820022583, -0.336649090051651, -0.020970961079001427, 0.0027453387156128883, 0.007497228682041168, -0.02873861975967884, 0.2904137372970581, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.2950036823749542, -0.22378143668174744, 0.02310558594763279, 0.9780805706977844, -0.1835503876209259, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.2927, "q": [-0.31153956055641174, -0.013451308012008667, 0.015132841654121876, 0.6277560591697693, -0.3265422582626343, 0.021091269329190254, -0.31924358010292053, 0.030244987457990646, -0.01505249086767435, 0.617844820022583, -0.3366532027721405, -0.020949147641658783, 0.0027453387156128883, 0.0074896421283483505, -0.02875329554080963, 0.29040175676345825, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.29499170184135437, -0.22378143668174744, 0.023093601688742638, 0.9780685901641846, -0.18353840708732605, 0.05241899937391281, 0.008844357915222645]} +{"t": 7.3094, "q": [-0.3115480840206146, -0.013451308012008667, 0.015146234072744846, 0.6277560591697693, -0.3265340030193329, 0.021105842664837837, -0.31923505663871765, 0.030244987457990646, -0.015079274773597717, 0.6178277730941772, -0.33665731549263, -0.020956380292773247, 0.002731946762651205, 0.0074896300211548805, -0.028733689337968826, 0.2904137372970581, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.040890175849199295, 0.29502764344215393, -0.22380541265010834, 0.023081617429852486, 0.9780685901641846, -0.1835503876209259, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.3261, "q": [-0.3114969730377197, -0.013451308012008667, 0.015132841654121876, 0.6277560591697693, -0.3265381157398224, 0.021084045991301537, -0.3192180097103119, 0.030236465856432915, -0.015079274773597717, 0.6178277730941772, -0.3366450071334839, -0.020963728427886963, 0.0027453387156128883, 0.007512426003813744, -0.02874847874045372, 0.2904736399650574, 0.2159797102212906, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04087819159030914, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835264265537262, 0.05243098363280296, 0.008868326433002949]} +{"t": 7.3428, "q": [-0.3114969730377197, -0.013459829613566399, 0.01511945016682148, 0.6277560591697693, -0.3265381157398224, 0.021084045991301537, -0.31920096278190613, 0.030236465856432915, -0.01505249086767435, 0.6178107857704163, -0.3366450071334839, -0.020963728427886963, 0.002624811604619026, 0.007466863840818405, -0.028767917305231094, 0.2904856503009796, 0.21594375371932983, -0.005213138181716204, 0.9791951179504395, 0.1575925648212433, 0.0616828091442585, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05240701511502266, 0.008868326433002949]} +{"t": 7.3595, "q": [-0.3114628791809082, -0.013442784547805786, 0.015146234072744846, 0.6277560591697693, -0.3265381157398224, 0.021098555997014046, -0.31920096278190613, 0.030236465856432915, -0.015065882354974747, 0.6178022623062134, -0.3366408944129944, -0.020956479012966156, 0.002731946762651205, 0.007451679557561874, -0.02877766452729702, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15760454535484314, 0.06169478967785835, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.1835503876209259, 0.05243098363280296, 0.008856342174112797]} +{"t": 7.3763, "q": [-0.3115054965019226, -0.01346835121512413, 0.015146234072744846, 0.6277475357055664, -0.3265381157398224, 0.021098555997014046, -0.319209486246109, 0.030219420790672302, -0.01503909844905138, 0.6177511215209961, -0.3366450071334839, -0.020963728427886963, 0.002758730435743928, 0.0074592712335288525, -0.028772791847586632, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05243098363280296, 0.008844357915222645]} +{"t": 7.393, "q": [-0.3115054965019226, -0.01346835121512413, 0.015146234072744846, 0.6277475357055664, -0.3265381455421448, 0.021113082766532898, -0.31920096278190613, 0.030219420790672302, -0.015079274773597717, 0.6177170276641846, -0.3366532027721405, -0.020949147641658783, 0.0027855143416672945, 0.007474462501704693, -0.02877284772694111, 0.29044967889785767, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.04092612862586975, 0.29502764344215393, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 7.4097, "q": [-0.31153103709220886, -0.013476874679327011, 0.015132841654121876, 0.6277475357055664, -0.3265422582626343, 0.021105796098709106, -0.31919243931770325, 0.030219420790672302, -0.015092666260898113, 0.6176658868789673, -0.33665731549263, -0.020956380292773247, 0.002718554809689522, 0.007451679557561874, -0.02877766452729702, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.040890175849199295, 0.2950036823749542, -0.22378143668174744, 0.02310558594763279, 0.9780805706977844, -0.18353840708732605, 0.05243098363280296, 0.008844357915222645]} +{"t": 7.4264, "q": [-0.31153103709220886, -0.013459829613566399, 0.01511945016682148, 0.6277390122413635, -0.3265463709831238, 0.021113019436597824, -0.31920096278190613, 0.030219420790672302, -0.015092666260898113, 0.6176317930221558, -0.3366450071334839, -0.020949188619852066, 0.002718554809689522, 0.007428901735693216, -0.028792286291718483, 0.29042571783065796, 0.2159557342529297, -0.005201153922826052, 0.9791951179504395, 0.15760454535484314, 0.06167082488536835, -0.04090216010808945, 0.29503965377807617, -0.2237934172153473, 0.023069633170962334, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 7.4432, "q": [-0.3115651309490204, -0.013451308012008667, 0.015106058679521084, 0.6277304887771606, -0.3265463709831238, 0.021098509430885315, -0.319209486246109, 0.030227944254875183, -0.015092666260898113, 0.6175891757011414, -0.3366532027721405, -0.020949147641658783, 0.0026783791836351156, 0.007451691664755344, -0.028797272592782974, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06169478967785835, -0.040890175849199295, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.4599, "q": [-0.31157365441322327, -0.013459829613566399, 0.015092666260898113, 0.6277304887771606, -0.3265422582626343, 0.021105796098709106, -0.31920096278190613, 0.030219420790672302, -0.015079274773597717, 0.6175550818443298, -0.3366532027721405, -0.020949147641658783, 0.002718554809689522, 0.007451679557561874, -0.02877766452729702, 0.2904616594314575, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.4768, "q": [-0.3115566074848175, -0.01346835121512413, 0.015092666260898113, 0.6277134418487549, -0.3265463709831238, 0.021098509430885315, -0.31920096278190613, 0.030227944254875183, -0.015106058679521084, 0.6174954175949097, -0.33665731549263, -0.020956380292773247, 0.002691771136596799, 0.007436494342982769, -0.028787413612008095, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22378143668174744, 0.02310558594763279, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008856342174112797]} +{"t": 7.4935, "q": [-0.3115651309490204, -0.013485396280884743, 0.015092666260898113, 0.6276793479919434, -0.3265504837036133, 0.021105732768774033, -0.31920096278190613, 0.030219420790672302, -0.015079274773597717, 0.6174954175949097, -0.3366532027721405, -0.020949147641658783, 0.002691771136596799, 0.007436494342982769, -0.028787413612008095, 0.2904376983642578, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.157616525888443, 0.0616828091442585, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.5103, "q": [-0.3115992248058319, -0.01346835121512413, 0.015106058679521084, 0.6276793479919434, -0.3265422582626343, 0.021105796098709106, -0.31920096278190613, 0.030227944254875183, -0.015079274773597717, 0.6174954175949097, -0.336649090051651, -0.020941896364092827, 0.002758730435743928, 0.007444086950272322, -0.028782539069652557, 0.29042571783065796, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 7.527, "q": [-0.31159070134162903, -0.013476874679327011, 0.015092666260898113, 0.6276878714561462, -0.3265422582626343, 0.021120306104421616, -0.31920096278190613, 0.030227944254875183, -0.015079274773597717, 0.6174613237380981, -0.3366532027721405, -0.020949147641658783, 0.0026783791836351156, 0.007444086950272322, -0.028782539069652557, 0.2903897762298584, 0.21596772968769073, -0.005213138181716204, 0.9791830778121948, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008856342174112797]} +{"t": 7.5437, "q": [-0.31159070134162903, -0.013451308012008667, 0.015106058679521084, 0.6276793479919434, -0.3265422582626343, 0.021120306104421616, -0.319209486246109, 0.030236465856432915, -0.015106058679521084, 0.6174442768096924, -0.336649090051651, -0.020941896364092827, 0.0027453387156128883, 0.007436494342982769, -0.028787413612008095, 0.29040175676345825, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.29499170184135437, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.052442967891693115, 0.008868326433002949]} +{"t": 7.5606, "q": [-0.31159070134162903, -0.01346835121512413, 0.015106058679521084, 0.6276793479919434, -0.3265381455421448, 0.021113082766532898, -0.3191668689250946, 0.030227944254875183, -0.01511945016682148, 0.6174358129501343, -0.3366532027721405, -0.020949147641658783, 0.002691771136596799, 0.007413704413920641, -0.028782427310943604, 0.2903897762298584, 0.21596772968769073, -0.005213138181716204, 0.9791830778121948, 0.15760454535484314, 0.06167082488536835, -0.040890175849199295, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780685901641846, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} +{"t": 7.5773, "q": [-0.31159070134162903, -0.01346835121512413, 0.015106058679521084, 0.6276708245277405, -0.3265463709831238, 0.021098509430885315, -0.31914129853248596, 0.030227944254875183, -0.01511945016682148, 0.6174358129501343, -0.33665731549263, -0.020941857248544693, 0.0026783791836351156, 0.007421297021210194, -0.028777554631233215, 0.2904137372970581, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835503876209259, 0.05241899937391281, 0.008844357915222645]} +{"t": 7.594, "q": [-0.31157365441322327, -0.013451308012008667, 0.01511945016682148, 0.6276708245277405, -0.3265381455421448, 0.021113082766532898, -0.31914129853248596, 0.030236465856432915, -0.01511945016682148, 0.6174358129501343, -0.3366532027721405, -0.020949147641658783, 0.002691771136596799, 0.007444086950272322, -0.028782539069652557, 0.2904137372970581, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835503876209259, 0.05241899937391281, 0.008844357915222645]} +{"t": 7.6109, "q": [-0.3115651309490204, -0.013485396280884743, 0.01511945016682148, 0.6276623010635376, -0.3265422582626343, 0.021105796098709106, -0.31914129853248596, 0.030236465856432915, -0.01511945016682148, 0.6174358129501343, -0.3366532027721405, -0.020949147641658783, 0.0026649872306734324, 0.007428890094161034, -0.028772680088877678, 0.29040175676345825, 0.21596772968769073, -0.005225121974945068, 0.9791830778121948, 0.15760454535484314, 0.06167082488536835, -0.040890175849199295, 0.2950036823749542, -0.22378143668174744, 0.023081617429852486, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008856342174112797]} +{"t": 7.6277, "q": [-0.3115651309490204, -0.013459829613566399, 0.01511945016682148, 0.6276623010635376, -0.3265340030193329, 0.02112036943435669, -0.3191327750682831, 0.030219420790672302, -0.015132841654121876, 0.6174528002738953, -0.336649090051651, -0.020941896364092827, 0.0027453387156128883, 0.0074592651799321175, -0.02876298874616623, 0.2904137372970581, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008856342174112797]} +{"t": 7.6444, "q": [-0.3115651309490204, -0.013485396280884743, 0.015106058679521084, 0.6276623010635376, -0.3265381455421448, 0.021113082766532898, -0.3190816342830658, 0.030244987457990646, -0.015092666260898113, 0.6174358129501343, -0.3366532027721405, -0.020949147641658783, 0.002691771136596799, 0.007474456448107958, -0.028763044625520706, 0.29042571783065796, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008868326433002949]} +{"t": 7.6611, "q": [-0.31153103709220886, -0.013476874679327011, 0.015092666260898113, 0.6276878714561462, -0.3265340030193329, 0.021105842664837837, -0.31901347637176514, 0.03026203252375126, -0.015186409465968609, 0.6174613237380981, -0.3366408944129944, -0.020956479012966156, 0.002691771136596799, 0.0074592651799321175, -0.02876298874616623, 0.2904137372970581, 0.2159797102212906, -0.005213138181716204, 0.9792070984840393, 0.15758058428764343, 0.06167082488536835, -0.04090216010808945, 0.2950036823749542, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 7.6778, "q": [-0.3114714026451111, -0.013493917882442474, 0.015092666260898113, 0.6276878714561462, -0.3265381455421448, 0.021113082766532898, -0.31891971826553345, 0.030253509059548378, -0.015186409465968609, 0.6174783706665039, -0.3366450071334839, -0.020963728427886963, 0.002731946762651205, 0.007436482701450586, -0.02876780554652214, 0.29042571783065796, 0.2159557342529297, -0.0051891696639359, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.0409141443669796, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.6946, "q": [-0.31140321493148804, -0.01346835121512413, 0.015079274773597717, 0.6276963949203491, -0.3265340030193329, 0.021091332659125328, -0.3188430368900299, 0.030244987457990646, -0.01519980188459158, 0.6174442768096924, -0.336649090051651, -0.020941896364092827, 0.002691771136596799, 0.00747445086017251, -0.028753241524100304, 0.2904376983642578, 0.2159797102212906, -0.005225121974945068, 0.9791830778121948, 0.15762852132320404, 0.0616828091442585, -0.04090216010808945, 0.2950156629085541, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008832373656332493]} +{"t": 7.7113, "q": [-0.31135207414627075, -0.013459829613566399, 0.01503909844905138, 0.6276963949203491, -0.3265422582626343, 0.021105796098709106, -0.31878337264060974, 0.030244987457990646, -0.01519980188459158, 0.6174868941307068, -0.33663681149482727, -0.020949246361851692, 0.002691771136596799, 0.0074668521992862225, -0.02874831110239029, 0.2904137372970581, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.061658840626478195, -0.04087819159030914, 0.2950036823749542, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 7.728, "q": [-0.31134355068206787, -0.01346835121512413, 0.01503909844905138, 0.627704918384552, -0.3265381455421448, 0.021113082766532898, -0.31873223185539246, 0.030244987457990646, -0.01519980188459158, 0.6174783706665039, -0.33663681149482727, -0.020949246361851692, 0.002718554809689522, 0.00745166651904583, -0.028758058324456215, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9791830778121948, 0.15760454535484314, 0.0616828091442585, -0.0409141443669796, 0.2950036823749542, -0.22380541265010834, 0.023069633170962334, 0.9780805706977844, -0.1835743635892868, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.745, "q": [-0.31126686930656433, -0.013451308012008667, 0.01505249086767435, 0.6277134418487549, -0.3265340030193329, 0.021091332659125328, -0.3186555504798889, 0.030253509059548378, -0.015213193371891975, 0.6174783706665039, -0.33663681149482727, -0.020978311076760292, 0.002691771136596799, 0.0074668521992862225, -0.02874831110239029, 0.2904137372970581, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.157616525888443, 0.0616828091442585, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780685901641846, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 7.7617, "q": [-0.3112412989139557, -0.013459829613566399, 0.01505249086767435, 0.627704918384552, -0.3265381455421448, 0.021113082766532898, -0.3186555504798889, 0.030244987457990646, -0.015226585790514946, 0.6174783706665039, -0.3366450071334839, -0.020963728427886963, 0.0026649872306734324, 0.0074896421283483505, -0.02875329554080963, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9791830778121948, 0.1575925648212433, 0.06167082488536835, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.7784, "q": [-0.3112327754497528, -0.013459829613566399, 0.015025706961750984, 0.627704918384552, -0.3265422582626343, 0.021105796098709106, -0.3186555504798889, 0.030244987457990646, -0.015226585790514946, 0.617469847202301, -0.336649090051651, -0.020970961079001427, 0.002691771136596799, 0.00747445086017251, -0.028753241524100304, 0.29044967889785767, 0.21596772968769073, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.06169478967785835, -0.04087819159030914, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18353840708732605, 0.05241899937391281, 0.008844357915222645]} +{"t": 7.7952, "q": [-0.3112412989139557, -0.013451308012008667, 0.015025706961750984, 0.6276963949203491, -0.3265381157398224, 0.021098555997014046, -0.3186555504798889, 0.030244987457990646, -0.015226585790514946, 0.617469847202301, -0.3366408944129944, -0.020956479012966156, 0.002718554809689522, 0.0074668521992862225, -0.02874831110239029, 0.29042571783065796, 0.2159557342529297, -0.005213138181716204, 0.9791830778121948, 0.15758058428764343, 0.0616828091442585, -0.04090216010808945, 0.2950036823749542, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 7.8119, "q": [-0.31126686930656433, -0.013451308012008667, 0.015025706961750984, 0.627704918384552, -0.3265504837036133, 0.02107669599354267, -0.31868109107017517, 0.030244987457990646, -0.015226585790514946, 0.617469847202301, -0.3366408944129944, -0.020971018821001053, 0.0027453387156128883, 0.007504821289330721, -0.028733745217323303, 0.2904616594314575, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.295051634311676, -0.22380541265010834, 0.023093601688742638, 0.9780685901641846, -0.1835503876209259, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.8288, "q": [-0.3112839162349701, -0.013451308012008667, 0.015025706961750984, 0.6277134418487549, -0.3265381157398224, 0.021098555997014046, -0.31868109107017517, 0.030244987457990646, -0.015226585790514946, 0.6174613237380981, -0.3366450071334839, -0.020963728427886963, 0.002731946762651205, 0.007444067858159542, -0.02875312976539135, 0.2904616594314575, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.040890175849199295, 0.2950636148452759, -0.22380541265010834, 0.023081617429852486, 0.9780685901641846, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 7.8456, "q": [-0.31125834584236145, -0.013451308012008667, 0.015012315474450588, 0.627704918384552, -0.3265381157398224, 0.021098555997014046, -0.3187066614627838, 0.030244987457990646, -0.015213193371891975, 0.6174528002738953, -0.33663681149482727, -0.020949246361851692, 0.002731946762651205, 0.007451679557561874, -0.02877766452729702, 0.2904616594314575, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.22380541265010834, 0.02310558594763279, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.8623, "q": [-0.31126686930656433, -0.01346835121512413, 0.015012315474450588, 0.627704918384552, -0.3265463709831238, 0.021098509430885315, -0.31868109107017517, 0.030244987457990646, -0.015213193371891975, 0.617469847202301, -0.3366408944129944, -0.020956479012966156, 0.002731946762651205, 0.007444086950272322, -0.028782539069652557, 0.29044967889785767, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15756858885288239, 0.06167082488536835, -0.04090216010808945, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780925512313843, -0.18356238305568695, 0.05240701511502266, 0.008868326433002949]} +{"t": 7.879, "q": [-0.31126686930656433, -0.013442784547805786, 0.015012315474450588, 0.627704918384552, -0.3265422582626343, 0.021105796098709106, -0.31868961453437805, 0.030244987457990646, -0.01519980188459158, 0.6174613237380981, -0.3366408944129944, -0.020956479012966156, 0.002731946762651205, 0.007451679557561874, -0.02877766452729702, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.15760454535484314, 0.0616828091442585, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008856342174112797]} +{"t": 7.8958, "q": [-0.31126686930656433, -0.01346835121512413, 0.015012315474450588, 0.6277219653129578, -0.3265298902988434, 0.02109861932694912, -0.31868961453437805, 0.030253509059548378, -0.015226585790514946, 0.6174954175949097, -0.3366450071334839, -0.020963728427886963, 0.002691771136596799, 0.007428895682096481, -0.02878248319029808, 0.29044967889785767, 0.21594375371932983, -0.005213138181716204, 0.9792070984840393, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.29502764344215393, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008856342174112797]} +{"t": 7.9126, "q": [-0.31125834584236145, -0.013451308012008667, 0.015025706961750984, 0.627704918384552, -0.3265298902988434, 0.02111312933266163, -0.3186640739440918, 0.030253509059548378, -0.01519980188459158, 0.617469847202301, -0.3366367816925049, -0.020963769406080246, 0.002691771136596799, 0.007421309128403664, -0.02879716083407402, 0.2904376983642578, 0.21596772968769073, -0.005201153922826052, 0.9792070984840393, 0.15762852132320404, 0.06167082488536835, -0.04087819159030914, 0.2950036823749542, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05243098363280296, 0.008868326433002949]} +{"t": 7.9293, "q": [-0.31126686930656433, -0.013459829613566399, 0.01503909844905138, 0.6277304887771606, -0.3265340030193329, 0.021105842664837837, -0.31864702701568604, 0.03027055412530899, -0.01519980188459158, 0.6174954175949097, -0.33662447333335876, -0.020985642448067665, 0.002691771136596799, 0.007413704413920641, -0.028782427310943604, 0.2904137372970581, 0.21596772968769073, -0.005213138181716204, 0.9791830778121948, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.2950036823749542, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05240701511502266, 0.008856342174112797]} +{"t": 7.946, "q": [-0.31126686930656433, -0.01346835121512413, 0.015065882354974747, 0.6277219653129578, -0.3265381455421448, 0.021113082766532898, -0.3186299800872803, 0.03027055412530899, -0.01519980188459158, 0.6174868941307068, -0.33662036061286926, -0.020992949604988098, 0.002731946762651205, 0.007406105753034353, -0.02877749875187874, 0.2904137372970581, 0.2159797102212906, -0.005201153922826052, 0.9791951179504395, 0.15762852132320404, 0.0616828091442585, -0.040890175849199295, 0.2950036823749542, -0.22380541265010834, 0.023069633170962334, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 7.9627, "q": [-0.31124982237815857, -0.01346835121512413, 0.015065882354974747, 0.6277304887771606, -0.3265463709831238, 0.021098509430885315, -0.3186299800872803, 0.03026203252375126, -0.01519980188459158, 0.6174868941307068, -0.33662858605384827, -0.020992891862988472, 0.0027453387156128883, 0.007413692772388458, -0.0287628211081028, 0.2904376983642578, 0.2159557342529297, -0.005201153922826052, 0.9792070984840393, 0.1576405018568039, 0.0616828091442585, -0.04090216010808945, 0.2950036823749542, -0.22380541265010834, 0.023081617429852486, 0.9780925512313843, -0.18356238305568695, 0.05241899937391281, 0.008868326433002949]} +{"t": 7.9795, "q": [-0.31125834584236145, -0.01346835121512413, 0.015079274773597717, 0.6277560591697693, -0.3265340030193329, 0.021105842664837837, -0.3186299800872803, 0.03026203252375126, -0.01519980188459158, 0.617469847202301, -0.33661627769470215, -0.020971177145838737, 0.002758730435743928, 0.007390908896923065, -0.02876763977110386, 0.29040175676345825, 0.2159557342529297, -0.005213138181716204, 0.9792070984840393, 0.1575925648212433, 0.0616828091442585, -0.04090216010808945, 0.295051634311676, -0.2237934172153473, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05243098363280296, 0.008868326433002949]} +{"t": 7.9962, "q": [-0.31124982237815857, -0.013476874679327011, 0.015079274773597717, 0.6277645826339722, -0.3265422582626343, 0.021105796098709106, -0.31858736276626587, 0.030253509059548378, -0.015213193371891975, 0.6174954175949097, -0.33662447333335876, -0.020985642448067665, 0.002718554809689522, 0.007398495450615883, -0.02875296212732792, 0.29040175676345825, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.15765248239040375, 0.06167082488536835, -0.04090216010808945, 0.29502764344215393, -0.22378143668174744, 0.023093601688742638, 0.9780805706977844, -0.1835743635892868, 0.05240701511502266, 0.008844357915222645]} +{"t": 8.0129, "q": [-0.3112327754497528, -0.01346835121512413, 0.015079274773597717, 0.627773106098175, -0.3265340030193329, 0.021105842664837837, -0.3185703158378601, 0.030279075726866722, -0.015226585790514946, 0.6174868941307068, -0.33662039041519165, -0.0209784097969532, 0.002691771136596799, 0.007398495450615883, -0.02875296212732792, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.1576405018568039, 0.0616828091442585, -0.0409381128847599, 0.2950036823749542, -0.22378143668174744, 0.02310558594763279, 0.9780805706977844, -0.18353840708732605, 0.05243098363280296, 0.008856342174112797]} +{"t": 8.0299, "q": [-0.3112327754497528, -0.01346835121512413, 0.015106058679521084, 0.6277645826339722, -0.3265340030193329, 0.021105842664837837, -0.318578839302063, 0.03027055412530899, -0.01519980188459158, 0.6174868941307068, -0.33661627769470215, -0.020971177145838737, 0.0026783791836351156, 0.00746682845056057, -0.02870909683406353, 0.2904376983642578, 0.2159557342529297, -0.005213138181716204, 0.9791951179504395, 0.15760454535484314, 0.0616828091442585, -0.04090216010808945, 0.2950156629085541, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.052395034581422806, 0.008844357915222645]} +{"t": 8.0469, "q": [-0.3112412989139557, -0.01346835121512413, 0.015079274773597717, 0.6277645826339722, -0.3265381157398224, 0.021098555997014046, -0.31854474544525146, 0.03026203252375126, -0.015253368765115738, 0.617469847202301, -0.33662039041519165, -0.0209784097969532, 0.002691771136596799, 0.00745923537760973, -0.028713971376419067, 0.2903897762298584, 0.21596772968769073, -0.005213138181716204, 0.9791951179504395, 0.15756858885288239, 0.0616828091442585, -0.04090216010808945, 0.29503965377807617, -0.22380541265010834, 0.023093601688742638, 0.9780805706977844, -0.18356238305568695, 0.05241899937391281, 0.008844357915222645]} diff --git a/recorder/g1_record_replay.py b/recorder/g1_record_replay.py new file mode 100644 index 0000000..3b23bf2 --- /dev/null +++ b/recorder/g1_record_replay.py @@ -0,0 +1,1103 @@ +#!/usr/bin/env python3 +""" +G1 RECORD + REPLAY — one script for the arm and the Inspire hand. + +RECORD + Arms go limp after a 3s safe-start: move them BY HAND. + Fingers stay open; TOUCH a finger to set how far it closes on replay (detected from + Δbase, the force change from rest — the RH56 can't be posed by hand, so touch is the + signal). Keyboard also works: o/c = open/close both, [ ] = right, ; ' = left. + Each finger is auto-scaled to its own hardest touch, so light/medium/firm touches + replay as distinct depths. Then it offers a preview, then saves. + +REPLAY + Drives the arm and the hand through the recording together. + + conda activate g1_env + cd ~/Robotics_workspace/yslootahtech/G1/G1_Lootah/Manual_Recorder + + python3 g1_record_replay.py enp3s0 record --output wave --seconds 20 + python3 g1_record_replay.py enp3s0 replay --input wave + python3 g1_record_replay.py enp3s0 record --output arm_only --no-hand + python3 g1_record_replay.py enp3s0 record --output hand_only --no-arm + +Needs inspire_g1 running on the robot (the Docker container) for the hand. +Files go in DataG1/. q: 1.00 = finger open, 0.00 = closed. + +WHY REPLAY USED TO MISS THE RECORDED POSE + You record with the arm LIMP (kp=0) — YOUR hand supplies the torque that holds it up. + On replay a pure PD controller can only make that torque out of position error + (err = tau_gravity / kp), so at kp=80 the arm settles 4-6° low at the shoulder, + ~4-5cm at the fingertip, every time. Three things fix it here: + 1. gravity feedforward — a clamped integral learns each joint's holding torque and + sends it as tau, so the arm can sit ON the target instead of below it. No robot + model needed; it also absorbs the hand's mass and anything you're carrying. + 2. dq feedforward — the recorded velocity is sent too, so kd stops fighting + the motion and adding lag. + 3. the waist is replayed — it used to be pinned to a single pose and the recorded + torso motion thrown away. The balance controller leans the torso several degrees + while you pose the arm; discarding it moved the hand 5-10cm in space even when + every arm joint was perfect. + Replay prints the measured tracking error at the end so you can see it working. +""" +import sys, os, time, json, argparse, termios, tty, select +import urllib.request +from pathlib import Path + +from unitree_sdk2py.core.channel import ChannelPublisher, ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowCmd_ +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowCmd_, LowState_ +from unitree_sdk2py.utils.crc import CRC +from unitree_sdk2py.idl.unitree_go.msg.dds_ import MotorCmds_, MotorStates_ +from unitree_sdk2py.idl.default import unitree_go_msg_dds__MotorCmd_ + +# ---- arm ---- +G1_NUM_MOTOR = 29 +ENABLE_ARM_SDK_INDEX = 29 +LEG_INDICES = list(range(0, 12)) +WAIST_INDICES = [12, 13, 14] +ARM_INDICES = list(range(15, 29)) +SHOULDER_ELBOW = [15, 16, 17, 18, 22, 23, 24, 25] +WRIST_INDICES = [19, 20, 21, 26, 27, 28] +# wrist_ROLL is a 25 Nm joint like the shoulders; only wrist pitch/yaw are the small 5 Nm +# ones. Grouping all six "wrists" together capped roll at 3 Nm — an 8x under-estimate. +WRIST_ROLL = [19, 26] +WRIST_SMALL = [20, 21, 27, 28] +RECORD_HZ = 30.0 +SAFE_START = 3.0 +WAIST_KP, WAIST_KD = 120.0, 6.0 # was 60/4 — the torso sagged while you posed the arm, + # and that sag is baked into the recording +HOLD_KP, HOLD_KD = 60.0, 4.0 +TEACH_KP, TEACH_KD = 0.0, 2.0 +PLAY_KP, PLAY_KD = 80.0, 3.0 # matches g1_replay so the arm reaches full height + +# ---- gravity feedforward ---- +# A clamped integral: tau += KI * (q_target - q_actual) * dt. It converges to exactly the +# torque each joint needs to hold its pose, with no robot model. +# +# The clamps bound a runaway integral — but set too LOW they starve a joint that legitimately +# needs the torque, and the error explodes instead of shrinking. Measured on a `fold` replay +# with the first guess (shoulders/elbow 12 Nm, all wrists 3 Nm): +# +# joint % of time at clamp mean error AT clamp vs NOT at clamp +# L_sh_pitch 0.5% 10.40 deg 1.06 deg +# L_wr_pitch 2.5% 5.04 deg 0.33 deg +# L_wr_roll 0.8% 3.48 deg 0.27 deg +# +# i.e. saturation was rare but produced ~10x the error, and was the entire source of the +# 12.54 deg / 8.84 deg peaks. Now sized from the URDF effort limits (shoulders, elbow and +# wrist_roll 25 Nm; wrist pitch/yaw 5 Nm) at ~80% of limit, leaving real headroom. +GRAV_KI = 60.0 +TAU_CLAMP = {} +for _j in WAIST_INDICES: + TAU_CLAMP[_j] = 20.0 +for _j in SHOULDER_ELBOW: + TAU_CLAMP[_j] = 20.0 # was 12.0; URDF limit 25 Nm +for _j in WRIST_ROLL: + TAU_CLAMP[_j] = 12.0 # was 3.0; URDF limit 25 Nm — this one was 8x too tight +for _j in WRIST_SMALL: + TAU_CLAMP[_j] = 4.0 # was 3.0; URDF limit 5 Nm + +# ---- hand ---- +HAND_MOTORS = 12 +GRIP_FORCE = 500.0 +# The RH56 refuses to drive a finger whose MEASURED force already exceeds its force limit. +# A drifted force-sensor zero (the G1's right R.mid reads ~856g, R.index ~570g at rest, +# untouched) therefore reads exactly like a dead motor: 0 current, no movement. We measure +# the resting offset during calibration and raise that hand's limit to clear it. +FORCE_MAX = 1000.0 +FORCE_MARGIN = 250.0 + +# ---- force-follow (admittance) ---- +# The RH56 cannot be back-driven, so pushing a finger moves it 0.000. Admittance control — +# the standard technique for non-backdrivable force-sensed devices — closes the loop in +# software: read the push, drive the finger that way, hold when you stop. The DEADBAND is +# what makes "touched but not moved" record nothing: a light contact never moves the finger, +# so no angle change exists to capture. +FOLLOW_GAIN = 0.0006 # travel per gram per second +# Measured 2026-08-03: idle force drift over 90s, untouched, is 0g on all 12 fingers. So the +# baseline never needs re-centring — the earlier self-motion was the INTEGRATOR accumulating +# incidental brushes while the arm was being posed, not sensor drift. Hence the arming key +# below rather than a fancier filter. +FOLLOW_DEADBAND = 60.0 # grams of push before a finger reacts +FOLLOW_MAXRATE = 0.35 # travel per second cap (~3s for full range) +FOLLOW_FORCE = 400.0 # grip force while shaping (below the 500g normal) + +NAMES = ["R.pinky", "R.ring", "R.mid", "R.index", "R.thumbB", "R.thumbR", + "L.pinky", "L.ring", "L.mid", "L.index", "L.thumbB", "L.thumbR"] +THUMB_ROT = [5, 11] # R.thumbR, L.thumbR + +# REST pose — where a finger sits when nothing is touching it, used during calibration, the +# whole recording, replay and the return-to-rest at the end. It used to be hardcoded to +# all-open, which leaves both thumbs rotated out where they stick out and foul the fingers. +# Default parks thumb_rot CLOSED on both hands and leaves everything else open; --thumb-rot +# open restores the old behaviour. +REST = [1.0] * 12 +for _i in THUMB_ROT: + REST[_i] = 0.0 +DATA_DIR = Path("DataG1") +CLR = "\x1b[K" + + +class KeyPoller: + def __enter__(self): + self.fd = sys.stdin.fileno() + self.old = termios.tcgetattr(self.fd) + tty.setcbreak(self.fd) + return self + def __exit__(self, *a): + termios.tcsetattr(self.fd, termios.TCSADRAIN, self.old) + def poll(self): + if select.select([sys.stdin], [], [], 0)[0]: + return sys.stdin.read(1) + return None + + +class NullPoller: + """Stand-in for KeyPoller when stdin is not a terminal. + + Replay reads a pause key, but it is also run with stdin piped or closed (scripts, cron). + tty.setcbreak would raise there, and a replay that refuses to start because nobody can + press pause is worse than a replay without pause. + """ + def __enter__(self): + return self + + def __exit__(self, *a): + pass + + def poll(self): + return None + + +def key_poller(): + return KeyPoller() if sys.stdin.isatty() else NullPoller() + + +def bar(v, w=12): + v = 0.0 if v < 0 else (1.0 if v > 1 else v) + n = int(round(v * w)) + return "█" * n + "·" * (w - n) + + +def unique_path(name): + p = Path(name if str(name).endswith(".jsonl") else str(name) + ".jsonl") + if len(p.parts) == 1: + DATA_DIR.mkdir(parents=True, exist_ok=True) + p = DATA_DIR / p.name + if not p.exists(): + return p + i = 1 + while (c := p.with_name(f"{p.stem}({i}){p.suffix}")).exists(): + i += 1 + return c + + +def resolve(name): + p = Path(name if str(name).endswith(".jsonl") else str(name) + ".jsonl") + return p if len(p.parts) > 1 else DATA_DIR / p.name + + +def load_shapes(url, timeout=4): + """Pull the dashboard's saved finger shapes so they can be fired by number key. + + The hand cannot be posed by hand — measured every way: 0.000 rotation under 233g, 0mA at + rest, +-3g on a push, and rigid at every force threshold down to 1g while the sensor read + 611g. So the only way to get a finger pose into a recording is to COMMAND it. Pre-building + the poses on the dashboard and firing them with one key keeps both your hands on the arm. + """ + try: + with urllib.request.urlopen(url.rstrip("/") + "/api/poses", timeout=timeout) as r: + d = json.load(r) + return [(n, d[n]) for n in sorted(d) if isinstance(d[n], list) and len(d[n]) == 12][:9] + except Exception as e: + print(f" (no shapes from {url}: {e})") + return [] + + +def clamp(v, lo, hi): + return lo if v < lo else (hi if v > hi else v) + + +# Fingers named for --free. Sending q < 0 makes the RH56 driver stop driving that joint +# (0xFFFF = release), so it goes limp and CAN be posed by hand — unlike the default, where +# every finger is actively held open and pushing on one just fights the motor. +FREE_GROUPS = { + "none": set(), + "thumbrot": {5, 11}, + "thumbs": {4, 5, 10, 11}, + "right": set(range(0, 6)), + "left": set(range(6, 12)), + "all": set(range(12)), +} + + +def parse_free(spec): + """--free is DEAD — the hardware cannot do it. Kept only to explain why. + + It was built on a comment in inspire.h claiming q<0 (0xFFFF) releases the motor so the + finger can be posed by hand. That comment is wrong: the upstream docstring three lines + above it says "-1: no change", i.e. the hand IGNORES that joint and keeps driving it to + its previous target. Which is why a "freed" finger feels locked. + + There is no motor-release command on the RH56, and the drive is a non-backdrivable worm + gear, so no force setting makes a finger hand-poseable either. Touch-to-teach is not a + workaround — it is the only mechanism the hardware offers. + """ + if str(spec).strip().lower() not in ("", "none"): + raise SystemExit( + "--free does not work and has been disabled.\n" + " The RH56 cannot release a finger: q<0 means 'no change' (the joint keeps its\n" + " previous target and stays driven), NOT 'free to move'. The drive is also a\n" + " non-backdrivable worm gear, so no force setting makes it hand-poseable.\n" + " Use touch-to-teach instead: leave --free off, and TOUCH a finger during the\n" + " recording — how hard you press sets how far it closes on replay.") + out = set() + for tok in (t.strip() for t in str(spec).split(",") if t.strip()): + low = tok.lower() + if low in FREE_GROUPS: + out |= FREE_GROUPS[low] + continue + hit = [i for i, n in enumerate(NAMES) if n.lower() == low] + if hit: + out.add(hit[0]); continue + if tok.isdigit() and 0 <= int(tok) < 12: + out.add(int(tok)); continue + raise SystemExit(f"--free: don't know '{tok}'. Use one of {sorted(FREE_GROUPS)}, " + f"a finger name like R.thumbR, or an index 0-11.") + return frozenset(out) + + +class G1RecordReplay: + def __init__(self, iface, use_arm=True, use_hand=True): + ChannelFactoryInitialize(0, iface) + self.use_arm, self.use_hand = use_arm, use_hand + self.low_state = None + self.got_state = False + if use_arm: + self.crc = CRC() + self.low_cmd = unitree_hg_msg_dds__LowCmd_() + self.arm_pub = ChannelPublisher("rt/arm_sdk", LowCmd_) + self.arm_pub.Init() + ChannelSubscriber("rt/lowstate", LowState_).Init(self._on_arm, 10) + self.hq = [1.0] * 12 + self.hf = [0.0] * 12 + self.hn = 0 + # Per-hand grip force actually commanded (raised above GRIP_FORCE if a finger's + # force-sensor zero has drifted). index 0 = right, 1 = left. + self.hand_force = [GRIP_FORCE, GRIP_FORCE] + self.tau_ff = [0.0] * G1_NUM_MOTOR + if use_hand: + ChannelSubscriber("rt/inspire/state", MotorStates_).Init(self._on_hand, 10) + self.hand_pub = ChannelPublisher("rt/inspire/cmd", MotorCmds_) + self.hand_pub.Init() + self.hand_msg = MotorCmds_() + self.hand_msg.cmds = [unitree_go_msg_dds__MotorCmd_() for _ in range(12)] + time.sleep(0.5) + + # ---------------- io ---------------- + def _on_arm(self, m): + self.low_state = m + self.got_state = True + + def _on_hand(self, m): + try: + self.hq = [float(m.states[i].q) for i in range(12)] + self.hf = [float(m.states[i].tau_est) for i in range(12)] + self.hn += 1 + except Exception: + pass + + def send_hand(self, vals): + if not self.use_hand: + return + for i in range(12): + self.hand_msg.cmds[i].q = float(vals[i]) + # inspire_g1 reads cmds[0].kp for the right hand and cmds[6].kp for the left. + self.hand_msg.cmds[i].kp = self.hand_force[0 if i < 6 else 1] + self.hand_pub.Write(self.hand_msg) + + def send_arm(self, q, kp, kd, dq=None, tau=None): + if not self.use_arm: + return + self.low_cmd.motor_cmd[ENABLE_ARM_SDK_INDEX].q = 1.0 + for i in WAIST_INDICES: + c = self.low_cmd.motor_cmd[i] + c.mode, c.q, c.kp, c.kd = 1, q[i], WAIST_KP, WAIST_KD + c.dq = dq[i] if dq else 0.0 + c.tau = tau[i] if tau else 0.0 + for i in ARM_INDICES: + c = self.low_cmd.motor_cmd[i] + c.mode, c.q, c.kp, c.kd = 1, q[i], kp, kd + c.dq = dq[i] if dq else 0.0 + c.tau = tau[i] if tau else 0.0 + self.low_cmd.crc = self.crc.Crc(self.low_cmd) + self.arm_pub.Write(self.low_cmd) + + def grav_step(self, q_target, dt, joints): + """Integrate the position error into a feedforward torque. Converges to the exact + holding torque for the current pose — model-free, and it absorbs the hand's mass + and any payload. Clamped per joint so it can never run away.""" + if not self.low_state: + return self.tau_ff + for j in joints: + err = q_target[j] - float(self.low_state.motor_state[j].q) + self.tau_ff[j] = clamp(self.tau_ff[j] + GRAV_KI * err * dt, + -TAU_CLAMP[j], TAU_CLAMP[j]) + return self.tau_ff + + def release_arm(self): + if not self.use_arm: + return + print("🔌 Handing arm control back to the internal brain...") + self.low_cmd.motor_cmd[ENABLE_ARM_SDK_INDEX].q = 0.0 + self.low_cmd.crc = self.crc.Crc(self.low_cmd) + for _ in range(5): + self.arm_pub.Write(self.low_cmd) + time.sleep(0.02) + + def wait_arm(self): + if not self.use_arm: + return True + print("Waiting for robot...", end="", flush=True) + t0 = time.time() + while not self.got_state: + if time.time() - t0 > 10: + print(" TIMEOUT — is the robot up and on this interface?") + return False + time.sleep(0.1) + print(" connected.") + return True + + # ---------------- hand helpers ---------------- + def calibrate_hand(self, thresh): + """Open the hand, learn each finger's resting force + noise, and raise the grip + limit past any drifted force-sensor zero so that finger can still be driven.""" + parked = ", ".join(NAMES[i] for i in range(12) if REST[i] < 0.5) + print(f"🖐️ Hand to rest pose + calibrating touch baseline — DON'T touch (2s)..." + + (f"\n parked closed: {parked}" if parked else "")) + # Calibrate in the SAME pose the hand will hold all recording. A finger's resting + # force depends on where it is sitting, so measuring open and then holding closed + # would give every touch a false offset. + for _ in range(25): + self.send_hand(REST) + time.sleep(0.02) + acc = [0.0] * 12 + fmn = [1e9] * 12 + fmx = [-1e9] * 12 + nc = 0 + t0 = time.time() + while time.time() - t0 < 2.0: + self.send_hand(REST) + if any(abs(v) > 1e-6 for v in self.hf): + for i in range(12): + acc[i] += self.hf[i] + fmn[i] = min(fmn[i], self.hf[i]) + fmx[i] = max(fmx[i], self.hf[i]) + nc += 1 + time.sleep(0.05) + if not nc: + print(" ⚠️ no hand data — is inspire_g1 running and the adapter connected?") + return [0.0] * 12, [thresh] * 12 + base = [acc[i] / nc for i in range(12)] + thr = [max(thresh, (fmx[i] - fmn[i]) * 1.5 + 5) for i in range(12)] + + # A finger whose resting force already exceeds the grip limit can never be driven: + # the RH56 stops the motor the instant measured force >= the limit, so it draws 0 + # current and looks dead. Raise that hand's limit above the offset instead. + for hand, lo in (("RIGHT", 0), ("LEFT", 6)): + worst = max(abs(base[i]) for i in range(lo, lo + 6)) + if worst > GRIP_FORCE - FORCE_MARGIN: + want = min(FORCE_MAX, worst + FORCE_MARGIN) + self.hand_force[0 if lo == 0 else 1] = want + drifted = [f"{NAMES[i]}({base[i]:.0f}g)" for i in range(lo, lo + 6) + if abs(base[i]) > GRIP_FORCE - FORCE_MARGIN] + print(f" ⚠️ {hand} hand force-sensor zero has DRIFTED: " + ", ".join(drifted)) + print(f" those fingers would read as 'dead' (0 current) at the {GRIP_FORCE:.0f}g " + f"limit — raising this hand's limit to {want:.0f}g so they still drive.") + if want < worst: + print(f" ⚠️ offset {worst:.0f}g exceeds the {FORCE_MAX:.0f}g max — " + f"re-zero the force sensor (hand unloaded) to recover it.") + return base, thr + + def open_hand(self, secs=1.2): + """Drive every finger fully open and hold it there briefly so the command lands. + + Recording already holds the fingers open, but a REPLAY ends on whatever the last + frame commanded — usually a grip — and go_home only moves the arm. So a take used to + finish with the hand still clenched around whatever it was holding. Paired with + go_home: release first, then bring the arm back.""" + if not self.use_hand: + return + if all(abs(self.hq[i] - REST[i]) < 0.1 for i in range(12)): + return # already at rest — stay quiet + print("🖐️ hands to rest pose...") + t0 = time.time() + while time.time() - t0 < secs: + self.send_hand(REST) + time.sleep(0.03) + # Report anything that did not reach its rest position — a finger stuck part-closed is + # worth knowing about before the arm starts moving again. + stuck = [NAMES[i] for i in range(12) if abs(self.hq[i] - REST[i]) > 0.15] + if stuck: + print(f" ⚠️ did not reach rest: {', '.join(stuck)} — check grip force / obstruction.") + else: + print("✅ hands at rest.") + + def remap(self, frames, thr, min_close, hold, auto_scale, span, hz, freed=frozenset()): + """Re-derive finger positions from the recorded Δbase so the DEGREE of each touch + survives; each finger is normalised to its own hardest touch. + + Freed fingers are skipped entirely — their recorded 'h' is the real measured position + you posed by hand, so there is nothing to infer.""" + if not frames or "d" not in frames[0]: + return frames, [0.0] * 12, [0] * 12 + pk = [0.0] * 12 + ntouch = [0] * 12 + for f in frames: + for i in range(12): + pk[i] = max(pk[i], abs(f["d"][i])) + # Count distinct touch events so we can tell whether auto-scaling is meaningful. + active = [False] * 12 + for f in frames: + for i in range(12): + if abs(f["d"][i]) > thr[i] and not active[i]: + ntouch[i] += 1 + active[i] = True + elif abs(f["d"][i]) < thr[i] * 0.5: + active[i] = False + for f in frames: + for i in range(12): + if i in freed: + continue # already holds the true posed position + amt = abs(f["d"][i]) + if amt <= thr[i]: + c = 0.0 + else: + # Auto-scale is only meaningful with >=2 touches: with a single touch + # that touch IS the peak, so it would always map to 100% no matter how + # light it was. Fall back to the absolute --span scale in that case. + use_auto = auto_scale and ntouch[i] >= 2 and pk[i] > thr[i] + 5 + top = pk[i] if use_auto else span + top = max(top, thr[i] + 5) + c = min_close + (1.0 - min_close) * min(1.0, (amt - thr[i]) / (top - thr[i])) + # A touch closes the finger FROM ITS REST POSE, not always from fully open. + # rest 1.0 -> h = 1-c, exactly as before. rest 0.0 (thumb_rot parked closed) + # -> h stays 0.0, so it holds its parked pose instead of springing open. + f["h"][i] = REST[i] * (1.0 - c) + w = int(hold * hz) + for i in range(12): + if i in freed: + continue # no touch-hold smoothing on a hand-posed trajectory + cl = [1.0 - f["h"][i] for f in frames] + run, cnt = 0.0, 0 + for k in range(len(frames)): + if cl[k] > 0: + run, cnt = max(run, cl[k]), w + elif cnt > 0: + cnt -= 1 + else: + run = 0.0 + frames[k]["h"][i] = 1.0 - max(cl[k], run if cnt > 0 else 0.0) + return frames, pk, ntouch + + # ---------------- record ---------------- + def record(self, seconds, thresh, span, min_close, hold, auto_scale, freed=frozenset(), + pos_mode=False, ext_mode=False, follow_mode=False, shapes=()): + if not self.wait_arm(): + return None + base = thr = None + if self.use_hand: + base, thr = self.calibrate_hand(thresh) + init_q = ([float(self.low_state.motor_state[i].q) for i in range(G1_NUM_MOTOR)] + if self.use_arm else [0.0] * G1_NUM_MOTOR) + + print("\n" + "=" * 58) + print("🟢 RECORDING") + if self.use_arm: + print(f" 0-{SAFE_START:.0f}s: arms LOCKED, then LIMP — move them by hand") + if self.use_hand: + if pos_mode: + print(" fingers: ANGLE mode — only a finger you DRIVE is recorded.") + print(" Touching the hand records NOTHING.") + if follow_mode: + print(" FORCE-FOLLOW: press f to ARM, then push a fingertip") + print(" to shape it. Press f again to disarm.") + print(" While disarmed (the default) contact is fully ignored,") + print(" so posing the arm never moves a finger.") + elif ext_mode: + print(" EXTERNAL driver: this recorder sends NO hand commands.") + print(" Move the 12 sliders on the dashboard — :8088 — and the") + print(" angle each finger reaches is what gets recorded.") + else: + held = [i for i in range(12) if i not in freed] + if held: + print(" fingers: TOUCH one to set how far it closes on replay") + if freed: + print(f" FREED (move by hand, position recorded): " + f"{', '.join(NAMES[i] for i in sorted(freed))}") + if shapes: + print(" SHAPES — press the number to apply that finger pose:") + for n, (nm, _) in enumerate(shapes, 1): + print(f" {n} {nm}") + print(" keys: o/c both · [ ] right · ; ' left") + print("=" * 58 + "\n") + + if follow_mode: + self.hand_force = [FOLLOW_FORCE, FOLLOW_FORCE] + frames, dt, t0 = [], 1.0 / RECORD_HZ, time.time() + # Start from the REST pose, not all-open. In position mode this IS what gets sent to + # the hand, so initialising it to [1.0]*12 un-parked both thumb_rots the instant + # recording began — and the mode then dutifully recorded that as an intentional move. + hand_key = list(REST) + ftarget = list(REST) # force-follow target, starts at rest + follow_on = False # armed with F. Off by default so incidental + # contact while posing the arm moves nothing. + touches = [0] * 12 + touching = [False] * 12 + limp = False + try: + with KeyPoller() as kp: + while (t := time.time() - t0) < seconds: + loop = time.time() + k = kp.poll() + if k and self.use_hand: + kl = k.lower() + if k.isdigit() and k != "0" and shapes and int(k) <= len(shapes): + nm, vals = shapes[int(k) - 1] + hand_key = [float(v) for v in vals] + ftarget = list(hand_key) + print(f"\n✋ shape {k}: {nm}\n") + elif kl == "f" and follow_mode: + follow_on = not follow_on + print(f"\n{'🖐️ FOLLOW ARMED — push fingers to shape them' if follow_on else '🔒 follow disarmed — contact ignored'}\n") + elif kl == "o": hand_key = list(REST) # "open" = back to rest pose + elif kl == "c": hand_key = [0.0] * 12 + elif kl == "[": + for i in range(6): hand_key[i] = 1.0 + elif kl == "]": + for i in range(6): hand_key[i] = 0.0 + elif kl == ";": + for i in range(6, 12): hand_key[i] = 1.0 + elif kl == "'": + for i in range(6, 12): hand_key[i] = 0.0 + + devs = [0.0] * 12 + if self.use_hand: + for i in range(12): + if i in freed: + continue # limp: its force is you moving it, not a touch + devs[i] = self.hf[i] - base[i] + if abs(devs[i]) > thr[i]: + if not touching[i]: + touches[i] += 1 + touching[i] = True + elif abs(devs[i]) < thr[i] * 0.5: + touching[i] = False + if follow_mode and not follow_on: + self.send_hand(ftarget) # hold, ignore all contact + elif follow_mode: + # Admittance: push harder -> move faster, capped. Below the + # deadband nothing moves, so incidental contact leaves no trace. + for k in range(12): + dv = self.hf[k] - base[k] + # ADAPTIVE BASELINE: this hand's force zero drifts, and an + # integrator fed a biased signal walks the finger away by + # itself. Re-centre while idle, freeze during a real push. + if abs(dv) <= FOLLOW_DEADBAND: + continue + dv -= FOLLOW_DEADBAND if dv > 0 else -FOLLOW_DEADBAND + stp = -FOLLOW_GAIN * dv * dt + stp = clamp(stp, -FOLLOW_MAXRATE * dt, FOLLOW_MAXRATE * dt) + ftarget[k] = clamp(ftarget[k] + stp, 0.0, 1.0) + self.send_hand(ftarget) + elif ext_mode: + # EXTERNAL: publish NOTHING. The recorder streams rt/inspire/cmd at + # 30Hz, which would out-shout the dashboard's occasional writes and + # drag every finger back. Staying silent hands the hand over. + pass + elif pos_mode: + # POSITION mode: the keys drive the hand for real, and what gets + # recorded is the MEASURED angle. Note send_hand(hand_key) — the + # keys used to update a variable that was never sent, so pressing + # o/c during a take did nothing to the physical hand. + self.send_hand(hand_key) + else: + # TOUCH mode: fingers hold the REST pose all through the recording + # (thumb_rot parked closed by default, everything else open). + self.send_hand(REST) + + sample = {"t": round(t, 4)} + if self.use_arm: + sample["q"] = [float(self.low_state.motor_state[i].q) + for i in range(G1_NUM_MOTOR)] + if self.use_hand: + if pos_mode: + # Record the angle the finger ACTUALLY reached. A finger only + # changes angle when it is driven, so leaning on an object records + # nothing — which is the whole point of this mode. + sample["h"] = [round(float(self.hq[i]), 4) for i in range(12)] + else: + sample["h"] = list(hand_key) # replaced by remap() below + # A freed finger's TRUE measured position is the recording — no + # touch remapping, this is real pose-by-hand teaching. + for i in freed: + sample["h"][i] = round(float(self.hq[i]), 4) + sample["d"] = [round(x, 1) for x in devs] + frames.append(sample) + + if self.use_arm: + if t < SAFE_START: + self.send_arm(init_q, HOLD_KP, HOLD_KD) + print(f"\r💪 HOLDING {SAFE_START - t:.1f}s ", end="", flush=True) + else: + if not limp: + print("\n🔓 ARMS LIMP — move them now!\n") + limp = True + cq = list(init_q) + for i in ARM_INDICES: + cq[i] = 0.0 + self.send_arm(cq, TEACH_KP, TEACH_KD) + if self.use_hand and int(t * 4) % 4 == 0: + live = " ".join(f"{NAMES[i].split('.')[1][:3]}{'*' if abs(devs[i])>thr[i] else ''}" + for i in range(12) + if i not in freed and abs(devs[i]) > thr[i] * 0.6) + # Freed fingers show their LIVE position so you can see them follow. + pose = " ".join(f"{NAMES[i].split('.')[1][:3]}={self.hq[i]:.2f}" + for i in sorted(freed)) + line = (f"touching: {live or '-'}" if not freed + else f"free[{pose}] touching: {live or '-'}") + print(f"\r t={t:4.1f}s {line:58s}", end="", flush=True) + time.sleep(max(0, dt - (time.time() - loop))) + except KeyboardInterrupt: + print("\n⛔ stopped early.") + + span_t = frames[-1]["t"] if frames else 0 + hz = (len(frames) / span_t) if span_t > 0 else RECORD_HZ + print(f"\n\n✅ recorded {len(frames)} frames ({span_t:.1f}s, {hz:.1f} Hz)") + + self.open_hand() # always finish a take with the fingers open + + if self.use_arm: + self.report_body_drift(frames) + + if self.use_hand and pos_mode: + # Nothing to infer — 'h' already holds the measured angle. Report only fingers + # whose DEGREE actually changed; anything you merely leaned on stayed put and is + # therefore absent, which is the entire reason for this mode. + print(" captured by ANGLE CHANGE (contact alone records nothing):") + if follow_mode: + print(" (force-follow: only fingers you actually PUSHED into a new pose)") + moved = False + for i in range(12): + hs = [f["h"][i] for f in frames] + rng = max(hs) - min(hs) + if rng > 0.05: + moved = True + print(f" {NAMES[i]:9s} moved {min(hs):.2f} → {max(hs):.2f} " + f"(range {rng:.2f}) → replayed exactly as recorded") + if not moved: + print(" (no finger changed angle — drive them with the keys during the take:") + print(" o/c = both open/close · [ ] = right · ; ' = left)") + elif self.use_hand: + frames, pk, ntouch = self.remap(frames, thr, min_close, hold, auto_scale, span, hz, + freed=freed) + if freed: + print(" posed by hand (freed):") + for i in sorted(freed): + hs = [f["h"][i] for f in frames] + rng = max(hs) - min(hs) + if rng < 0.03: + print(f" {NAMES[i]:9s} range {rng:.3f} ⚠️ barely moved — it may not be " + f"back-drivable by hand; use touch instead (drop it from --free)") + else: + print(f" {NAMES[i]:9s} range {rng:.3f} ({min(hs):.2f}..{max(hs):.2f}) " + f"→ replayed as recorded") + tot = sum(touches) + if tot: + print(f" {tot} touch(es):") + for i in range(12): + if touches[i]: + mx = max(1.0 - f["h"][i] for f in frames) + how = "own peak" if (auto_scale and ntouch[i] >= 2) else f"--span {span:.0f}g" + print(f" {NAMES[i]:9s} peakΔ {pk[i]:5.0f}g {touches[i]} touch(es)" + f" → closes {mx*100:3.0f}% on replay (scale: {how})") + elif not freed: + # Do not just say "lower it" — say BY HOW MUCH, from what this take actually + # saw. Guessing the threshold blind is how takes get wasted. + peaks = [max(abs(f["d"][i]) for f in frames) for i in range(12)] + best = max(peaks) + who = NAMES[peaks.index(best)] + print(f" (no finger touches captured at --thresh {thresh:.0f}g)") + if best < 3: + print(" nothing touched the hand at all during this take.") + else: + sug = max(8, int(best * 0.5 / 5) * 5) + print(f" hardest press seen: {who} at {best:.0f}g") + print(f" -> try --thresh {sug} (half your hardest press)") + strong = sorted(((p, NAMES[i]) for i, p in enumerate(peaks) if p > 3), + reverse=True)[:5] + print(" per finger: " + " ".join(f"{n.split('.')[1][:3]}{p:.0f}g" + for p, n in strong)) + return frames, init_q, hz + + def report_body_drift(self, frames): + """The balance controller leans the whole robot while you pose the arm. Replay can + reproduce the waist but NOT the legs, so a take with big leg drift will not land the + hand where you put it however good the arm tracking is. Say so up front.""" + import math + if len(frames) < 2 or "q" not in frames[0]: + return + worst_leg, worst_leg_j = 0.0, None + for j in LEG_INDICES: + qs = [f["q"][j] for f in frames] + r = math.degrees(max(qs) - min(qs)) + if r > worst_leg: + worst_leg, worst_leg_j = r, j + waist = max(math.degrees(max(f["q"][j] for f in frames) - min(f["q"][j] for f in frames)) + for j in WAIST_INDICES) + if waist > 1.5: + print(f" ↻ torso moved {waist:.1f}° during the take — replayed (was discarded before).") + if worst_leg > 2.5: + print(f" ⚠️ the robot LEANED during this take (leg joint {worst_leg_j} moved " + f"{worst_leg:.1f}°). Legs can't be replayed safely, so the hand may land a few cm off.") + print(" → for repeatable placement, brace/steady the robot while you pose the arm.") + + # ---------------- replay ---------------- + def replay(self, frames, init_q=None, speed=1.0, home=None, hz=RECORD_HZ, + gravity=True, waist_mode="recorded"): + if not frames: + print(" (nothing to replay)") + return + has_arm = self.use_arm and "q" in frames[0] + has_hand = self.use_hand and "h" in frames[0] + print(f"▶️ REPLAY — {'arm + hand' if has_arm and has_hand else 'arm' if has_arm else 'hand'}" + f" ({len(frames)} frames @ {hz:.0f} Hz" + f"{', gravity ff ON' if (has_arm and gravity) else ''}" + f"{', waist=' + waist_mode if has_arm else ''})") + if has_arm and not self.wait_arm(): + return + if has_arm and init_q is None: + init_q = [float(self.low_state.motor_state[i].q) for i in range(G1_NUM_MOTOR)] + # The waist reference must come from the RECORDING, not from wherever the robot + # happens to be standing now — otherwise previewing a take and replaying the same + # file later pin the torso to two different poses and place the hand differently. + if waist_mode == "lock": + for j in WAIST_INDICES: + init_q[j] = frames[0]["q"][j] + + dt_out = 1.0 / max(hz, 1.0) + self.tau_ff = [0.0] * G1_NUM_MOTOR + ctrl = (WAIST_INDICES + ARM_INDICES) if has_arm else [] + + dqs = self._velocities(frames) if has_arm else None + + def target_at(tgt): + """Linearly interpolate between recorded frames — nearest-previous held a stale + target for up to a full frame, which shows up as lag on fast moves.""" + i = 0 + lo, hi = 0, len(frames) - 1 + while lo <= hi: + mid = (lo + hi) // 2 + if frames[mid]["t"] <= tgt: + i, lo = mid, mid + 1 + else: + hi = mid - 1 + if i >= len(frames) - 1: + return frames[-1], (dqs[-1] if dqs else None), 1.0, len(frames) - 1 + t0_, t1_ = frames[i]["t"], frames[i + 1]["t"] + a = 0.0 if t1_ <= t0_ else clamp((tgt - t0_) / (t1_ - t0_), 0.0, 1.0) + return frames[i], (dqs[i] if dqs else None), a, i + + def blend_q(i, a): + f0, f1 = frames[i], frames[min(i + 1, len(frames) - 1)] + q = [0.0] * G1_NUM_MOTOR + for j in range(G1_NUM_MOTOR): + q[j] = (1 - a) * f0["q"][j] + a * f1["q"][j] + if waist_mode == "lock": + for j in WAIST_INDICES: + q[j] = init_q[j] + return q + + # ease into the first frame (gravity ff settles here so playback starts on-target) + if has_arm: + cur = [float(self.low_state.motor_state[i].q) for i in range(G1_NUM_MOTOR)] + first = frames[0]["q"] + for k in range(int(2.0 * hz)): + a = k / (2.0 * hz) + q = list(cur) + for j in WAIST_INDICES: + q[j] = (1 - a) * cur[j] + a * (init_q[j] if waist_mode == "lock" else first[j]) + for j in ARM_INDICES: + q[j] = (1 - a) * cur[j] + a * first[j] + tau = self.grav_step(q, dt_out, ctrl) if gravity else None + self.send_arm(q, PLAY_KP, PLAY_KD, tau=tau) + if has_hand: + self.send_hand(frames[0]["h"]) + time.sleep(dt_out) + + err_sum = [0.0] * G1_NUM_MOTOR + err_max = [0.0] * G1_NUM_MOTOR + n_err = 0 + t0 = time.time() + # PAUSE (p or space): freeze the playback clock but KEEP publishing the frame the + # replay is sitting on. Stopping the stream instead would drop the arm — rt/arm_sdk + # needs a continuous command — so a pause holds the pose rather than releasing it. + paused, pause_at = False, 0.0 + print(" keys: p = pause/resume") + try: + with key_poller() as kp: + while True: + k = kp.poll() + if k and (k.lower() == "p" or k == " "): + paused = not paused + if paused: + pause_at = time.time() + else: + t0 += time.time() - pause_at # the pause costs no playback time + print("\n%s\n" % ("⏸ PAUSED — holding this pose (p to resume)" + if paused else "▶️ resumed"), flush=True) + tgt = ((pause_at if paused else time.time()) - t0) * speed + f, dqf, a, i = target_at(tgt) + if has_arm: + q = blend_q(i, a) + tau = self.grav_step(q, dt_out, ctrl) if gravity else None + dq = None + if dqf: + dq = [0.0] * G1_NUM_MOTOR + for j in ctrl: + dq[j] = dqf[j] * speed + self.send_arm(q, PLAY_KP, PLAY_KD, dq=dq, tau=tau) + if self.low_state: + n_err += 1 + for j in ARM_INDICES: + e = abs(q[j] - float(self.low_state.motor_state[j].q)) + err_sum[j] += e + err_max[j] = max(err_max[j], e) + if has_hand: + hf = f["h"] + self.send_hand(hf) + print(f"\r t={f['t']:5.1f}s " + + " ".join(f"{NAMES[j].split('.')[1][:3]}:{1-hf[j]:.0%}" + for j in range(12) if hf[j] < 0.95)[:70] + CLR, + end="", flush=True) + if frames[-1]["t"] < tgt: + break + time.sleep(dt_out) + print("\n✅ replay done.") + except KeyboardInterrupt: + print("\n⛔ replay stopped.") + + if has_arm and n_err: + self.report_tracking(err_sum, err_max, n_err, gravity) + # Release the grip BEFORE the arm travels home, so anything held is let go where it + # was picked up rather than carried back and dropped. + if has_hand: + self.open_hand() + if has_arm and home: + self.go_home(init_q, home, hz=hz, gravity=gravity) + + def _velocities(self, frames): + """Central-difference the recorded trajectory. Sent as dq so kd stops fighting the + motion — without it the damping term alone adds ~1° of lag on a fast move.""" + n = len(frames) + out = [] + for k in range(n): + a, b = max(0, k - 1), min(n - 1, k + 1) + dt = frames[b]["t"] - frames[a]["t"] + if dt <= 0: + out.append([0.0] * G1_NUM_MOTOR) + continue + out.append([(frames[b]["q"][j] - frames[a]["q"][j]) / dt for j in range(G1_NUM_MOTOR)]) + return out + + def report_tracking(self, err_sum, err_max, n, gravity): + import math + rows = [(j, math.degrees(err_sum[j] / n), math.degrees(err_max[j])) for j in ARM_INDICES] + rows.sort(key=lambda r: -r[1]) + avg = sum(r[1] for r in rows) / len(rows) + print(f"📐 tracking error (gravity ff {'ON' if gravity else 'OFF'}): " + f"mean {avg:.2f}° worst joints:") + for j, m, mx in rows[:4]: + print(f" motor {j:2d} mean {m:5.2f}° peak {mx:5.2f}°") + if avg > 3.0: + print(" ⚠️ still high — raise --grav-ki, or the arm is hitting a limit/obstruction.") + + def go_home(self, init_q, home_file, hz=RECORD_HZ, gravity=True): + hq = load_home(home_file) + if not hq: + return + print(f"🏡 returning arms to home ({home_file})...") + dt_out = 1.0 / max(hz, 1.0) + end = [float(self.low_state.motor_state[i].q) for i in range(G1_NUM_MOTOR)] + steps = int(3.0 * hz) + ctrl = WAIST_INDICES + ARM_INDICES + for k in range(steps): + a = k / steps + q = list(end) + for j in WAIST_INDICES: + q[j] = init_q[j] + for j in ARM_INDICES: + q[j] = (1 - a) * end[j] + a * hq[j] + # PLAY gains, not the softer hold gains — home used to settle even lower than + # replay did because it was driven at kp=60. + tau = self.grav_step(q, dt_out, ctrl) if gravity else None + self.send_arm(q, PLAY_KP, PLAY_KD, tau=tau) + time.sleep(dt_out) + print("✅ home.") + + +def load_home(name): + p = resolve(name) + try: + last = None + with open(p) as f: + for line in f: + d = json.loads(line) + if "q" in d and len(d["q"]) == G1_NUM_MOTOR: + last = d["q"] + if last: + print(f"✅ home pose from {p}") + return last + except FileNotFoundError: + print(f"⚠️ home file {p} not found — skipping home return") + return None + + +def save(frames, path, use_arm, use_hand, hz): + meta = {"hz": hz, "arm": use_arm, "hand": use_hand} + if use_arm: + meta["motors"] = G1_NUM_MOTOR + if use_hand: + meta["hand_motors"] = HAND_MOTORS + with open(path, "w") as f: + f.write(json.dumps({"meta": meta}) + "\n") + for fr in frames: + f.write(json.dumps(fr) + "\n") + sz = os.path.getsize(path) + print(f"💾 saved {len(frames)} frames to {path} ({sz/1024:.0f} KB)") + + +def load(name): + p = resolve(name) + frames = [] + with open(p) as f: + meta = json.loads(f.readline()).get("meta", {}) + for line in f: + d = json.loads(line) + if "q" in d or "h" in d: + frames.append(d) + # Honour the file's own rate. Every recording in DataG1/ predating this was 60 Hz while + # RECORD_HZ was hardcoded to 30, so half of every trajectory was being skipped. + hz = float(meta.get("hz") or 0) + if hz <= 0 and len(frames) > 1 and frames[-1]["t"] > 0: + hz = len(frames) / frames[-1]["t"] + if hz <= 0: + hz = RECORD_HZ + print(f"loaded {len(frames)} frames from {p} ({hz:.0f} Hz)") + return frames, meta, hz + + +def ask(q): + try: + return input(q).strip().lower() in ("y", "yes", "") + except KeyboardInterrupt: + return False + + +def main(): + global GRAV_KI + ap = argparse.ArgumentParser(description="G1 arm + hand record / replay") + ap.add_argument("iface", nargs="?", default="enp3s0") + ap.add_argument("action", choices=["record", "replay"]) + ap.add_argument("--output", default="take") + ap.add_argument("--input", default=None, help="replay: file in DataG1/") + ap.add_argument("--seconds", type=float, default=20.0) + ap.add_argument("--speed", type=float, default=1.0) + ap.add_argument("--home", default="arm_home.jsonl") + ap.add_argument("--no-arm", action="store_true") + ap.add_argument("--no-hand", action="store_true") + ap.add_argument("--thresh", type=float, default=12.0, help="finger touch threshold (g)") + # Defaults are DIRECT: closure tracks the press one-to-one, with nothing added. + # span 120 = grams of press that means fully closed (measured: your presses run + # 41-110g, so 120 puts a firm press near full and a light one near 30%) + # min-close 0= no floor. A light press closes a little, not 35% minimum. + # hold 0 = no latch. Release and it opens again, exactly when you released. + # auto-scale off = an absolute scale, so 41g and 110g do NOT both become 100%. + # The old behaviour is still reachable: --span 45 --min-close 0.35 --hold 0.8 --auto-scale + ap.add_argument("--span", type=float, default=120.0, + help="grams of press that means FULLY closed (default 120)") + ap.add_argument("--min-close", type=float, default=0.0, + help="floor closure for any touch (default 0 = proportional)") + ap.add_argument("--hold", type=float, default=0.0, + help="seconds a closure is held after you release (default 0 = follow exactly)") + ap.add_argument("--auto-scale", action="store_true", + help="normalise each finger to its OWN hardest press (makes every finger " + "reach 100%%; off by default because it destroys how hard you pressed)") + ap.add_argument("--no-auto-scale", action="store_true", help=argparse.SUPPRESS) + ap.add_argument("--no-gravity", action="store_true", + help="disable gravity feedforward (arm will sag below the recorded pose)") + ap.add_argument("--grav-ki", type=float, default=GRAV_KI, + help="gravity feedforward learning rate") + ap.add_argument("--waist", choices=["recorded", "lock"], default="recorded", + help="recorded: replay the torso you recorded (accurate placement). " + "lock: pin the torso to the start pose (old behaviour)") + ap.add_argument("--free", default="none", + help=argparse.SUPPRESS) # disabled: the RH56 cannot release a finger + ap.add_argument("--fingers", choices=["touch", "position", "external", "follow"], + default="touch", + help="touch: press a finger to teach how far it closes (any contact counts, " + "including leaning on something). position: record the finger's ACTUAL " + "angle, driven with the o/c/[ ]/; ' keys — contact alone records " + "nothing. external: same, but the recorder sends NO hand commands so " + "the dashboard sliders (:8088) drive all 12 fingers individually") + ap.add_argument("--shapes", default="http://10.255.254.88:8088", + help="dashboard URL to load saved finger shapes from; keys 1-9 apply them " + "during a take. Use --shapes none to disable") + ap.add_argument("--thumb-rot", choices=["closed", "open"], default="closed", + help="resting position of thumb_rot on BOTH hands, held during record, " + "replay and the return to rest (default: closed / tucked in)") + a = ap.parse_args() + freed = parse_free(a.free) + shape_list = [] if a.shapes.lower() == "none" else load_shapes(a.shapes) + + for _i in THUMB_ROT: # applies everywhere REST is used + REST[_i] = 0.0 if a.thumb_rot == "closed" else 1.0 + + GRAV_KI = a.grav_ki + gravity = not a.no_gravity + + rr = G1RecordReplay(a.iface, use_arm=not a.no_arm, use_hand=not a.no_hand) + + if a.action == "replay": + name = a.input or a.output + frames, meta, hz = load(name) + rr.replay(frames, speed=a.speed, home=a.home, hz=hz, + gravity=gravity, waist_mode=a.waist) + rr.release_arm() + return + + out = rr.record(a.seconds, a.thresh, a.span, a.min_close, a.hold, a.auto_scale, + freed=freed, pos_mode=(a.fingers in ("position", "external", "follow")), + shapes=shape_list, + ext_mode=(a.fingers == "external"), + follow_mode=(a.fingers == "follow")) + if not out: + return + frames, init_q, hz = out + if not a.no_arm: + rr.go_home(init_q, a.home, hz=hz, gravity=gravity) + while ask("▶️ Preview this recording before saving? [y/n]: "): + rr.replay(frames, init_q=init_q, speed=a.speed, home=a.home, hz=hz, + gravity=gravity, waist_mode=a.waist) + rr.open_hand() # safety net: whichever path we exited by, leave the hand open + rr.release_arm() + if ask("💾 Save recording? [y/n]: "): + save(frames, unique_path(a.output), not a.no_arm, not a.no_hand, hz) + else: + print("🗑️ not saved.") + print("done.") + + +if __name__ == "__main__": + main() diff --git a/robot.sh b/robot.sh new file mode 100755 index 0000000..1863056 --- /dev/null +++ b/robot.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# ============================================================================= +# robot.sh — WORKSTATION-side helper: run manage.sh on the G1 robot (PC2) over SSH. +# manage.sh itself must run on the robot (that's where the hands live); this just +# forwards your command there. +# +# ./robot.sh # robot status +# ./robot.sh status|restart|... # any manage.sh command (see: ./robot.sh help) +# ./robot.sh like|handshake # gestures (ARM MOVES on the robot) +# ./robot.sh arm "shake hand" # quotes/spaces preserved +# ./robot.sh deploy # rsync this repo -> robot, then build.sh +# +# ./robot.sh -ip 10.255.254.88 status # target a specific address (e.g. the +# # wifi IP, which changes via DHCP) +# +# Env: ROBOT (default unitree@192.168.123.164 — ethernet). -ip overrides it. +# Give -ip either "10.255.254.88" (user defaults to unitree) or "user@host". +# ============================================================================= +set -euo pipefail + +ROBOT="${ROBOT:-unitree@192.168.123.164}" + +# -ip : target a specific host (e.g. the changing wifi IP) instead of eth0. +if [ "${1:-}" = "-ip" ] || [ "${1:-}" = "--ip" ]; then + shift + [ $# -ge 1 ] || { echo "usage: ./robot.sh -ip " >&2; exit 1; } + case "$1" in *@*) ROBOT="$1" ;; *) ROBOT="unitree@$1" ;; esac + shift +fi +HERE="$(cd "$(dirname "$0")" && pwd)" +REMOTE='~/DFX_inspire_service' +TT=""; [ -t 0 ] && TT="-t" # allocate a TTY only when we have one (menu/pose/logs) +SSH_OPTS="-o ConnectTimeout=8" # fail fast if eth0 is down (use -ip instead) + +echo ">> target: $ROBOT" >&2 + +if [ "${1:-}" = "deploy" ]; then + echo ">> rsync $HERE -> $ROBOT:$REMOTE" + rsync -az --exclude='build/' -e "ssh $SSH_OPTS" "$HERE/" "$ROBOT:$REMOTE/" + # shellcheck disable=SC2086 + ssh $SSH_OPTS $TT "$ROBOT" "cd $REMOTE && ./build.sh" + exit 0 +fi + +# shellcheck disable=SC2086 +ssh $SSH_OPTS $TT "$ROBOT" "cd $REMOTE && ./manage.sh $(printf '%q ' "$@")" diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..99d175e --- /dev/null +++ b/start.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash +# ============================================================================= +# start.sh — ONE command to bring up the whole hand stack. +# +# Starts the Docker container that runs all three pieces together — the RS-485 +# service (inspire_g1), the TCP->DDS bridge (hand_bridge) and the web dashboard +# on :8088 — then waits until the dashboard actually answers before telling you +# it is up. +# +# ./start.sh start it (builds the image the first time) +# ./start.sh stop stop the container +# ./start.sh restart stop + start +# ./start.sh status is it up, what is it serving, are the hands alive +# ./start.sh logs follow the container log +# ./start.sh rebuild force an image rebuild, then start +# +# RUN IT ON THE ROBOT. Run it on the workstation and it rsyncs this folder to +# the robot and runs itself there — the hands, the DDS interface and Docker are +# all on the robot, so there is nothing for it to do locally. +# +# Env: ROBOT=unitree@192.168.123.164 (override for the wifi IP) +# PORT=8088 +# ============================================================================= +set -euo pipefail + +HERE="$(cd "$(dirname "$0")" && pwd)" +ROBOT="${ROBOT:-unitree@192.168.123.164}" +REMOTE_DIR='~/DFX_inspire_service' +PORT="${PORT:-8088}" +IMG="${IMG:-inspire-hand:latest}" +CMD="${1:-start}" + +say() { printf '\033[36m>>\033[0m %s\n' "$*"; } +ok() { printf '\033[32m>>\033[0m %s\n' "$*"; } +die() { printf '\033[31m>>\033[0m %s\n' "$*" >&2; exit 1; } + +# The robot is the only place this can run: it owns the hands (/dev/ttyUSB*), the +# DDS interface to the arm, and Docker. aarch64 is the reliable tell — the +# workstation is x86_64 and the image is built for arm64. +is_robot() { [ "$(uname -m)" = "aarch64" ] && command -v docker >/dev/null 2>&1; } + +remote() { + say "not the robot — deploying to $ROBOT and running there" + command -v rsync >/dev/null || die "rsync not installed" + # build/ is excluded: the image compiles its own binaries, and the host copy is + # 55MB of artifacts that only the (optional) native path uses. + rsync -az --delete --exclude='build/' --exclude='docker/.ctx/' \ + -e "ssh -o ConnectTimeout=8" "$HERE/" "$ROBOT:$REMOTE_DIR/" + # shellcheck disable=SC2029 + ssh -o ConnectTimeout=8 -t "$ROBOT" "cd $REMOTE_DIR && ./start.sh $CMD" + exit $? +} + +have_image() { docker image inspect "$IMG" >/dev/null 2>&1; } + +# run.sh tails the container log, which spills the dashboard's HTTP lines over this +# script's own output. Keep it unless something actually fails. +run_quiet() { + local log="/tmp/hand_start.$$.log" + if ! "$HERE/docker/run.sh" >"$log" 2>&1; then + cat "$log"; rm -f "$log"; die "docker run failed" + fi + rm -f "$log" +} + +build() { + say "building $IMG (compiles the binaries + python DDS in-image; a few minutes)" + "$HERE/docker/build_image.sh" +} + +wait_up() { + # Poll the dashboard rather than trusting `docker run` — the container can be up + # while the service inside is still opening the serial ports, and reporting a URL + # that 404s is worse than waiting three more seconds for it. + say "waiting for the dashboard..." + for _ in $(seq 1 40); do + if curl -fsS "http://127.0.0.1:$PORT/api/status" >/dev/null 2>&1; then + return 0 + fi + sleep 1 + done + return 1 +} + +report() { + local s + s="$(curl -fsS "http://127.0.0.1:$PORT/api/status" 2>/dev/null || true)" + [ -n "$s" ] || { echo " dashboard not answering on :$PORT"; return 1; } + python3 - "$s" <<'PY' 2>/dev/null || echo " $s" +import json, sys +d = json.loads(sys.argv[1]) +print(" service=%s bridge=%s iface=%s ports=%s" + % (d.get("service"), d.get("bridge"), d.get("iface"), ", ".join(d.get("ports") or []) or "none")) +if d.get("mismatch"): + print(" WARNING: port drift — the service opened different ttys than are live now") +PY + local r + r="$(curl -fsS "http://127.0.0.1:$PORT/api/rec/status" 2>/dev/null || true)" + case "$r" in + *'"ready":true'*) echo " recorder: ready (record/replay panel enabled)" ;; + *'"ready":false'*) echo " recorder: NOT found — rebuild with ./start.sh rebuild" ;; + esac + # Skip docker's own bridge addresses (172.17-19.x) — nothing outside the robot can + # reach those, and printing them as if they were the dashboard URL wastes a click. + for ip in $(hostname -I 2>/dev/null); do + case "$ip" in 172.1[789].*|127.*) continue ;; esac + echo " http://$ip:$PORT" + done +} + +is_robot || remote + +case "$CMD" in + start|restart) + have_image || build + run_quiet + wait_up || die "dashboard did not come up — ./start.sh logs" + ok "up" + report + ;; + rebuild) + build + run_quiet + wait_up || die "dashboard did not come up — ./start.sh logs" + ok "rebuilt and up" + report + ;; + stop) + "$HERE/docker/stop.sh" + ;; + status) + docker ps --filter name=inspire-hand --format ' {{.Names}} · {{.Status}} · {{.Image}}' | grep . \ + || echo " inspire-hand is not running" + report || true + ;; + logs) + docker logs -f --tail 60 inspire-hand + ;; + *) + sed -n '2,26p' "$0" | sed 's/^# \{0,1\}//' + exit 1 + ;; +esac diff --git a/udev/99-inspire-hands.rules b/udev/99-inspire-hands.rules new file mode 100644 index 0000000..2b2e598 --- /dev/null +++ b/udev/99-inspire-hands.rules @@ -0,0 +1,12 @@ +# Grant non-root (0666) access to the Inspire hand USB-RS485 adapters. +# +# Both adapters are QinHeng CH340 (1a86:7523) and report the SAME USB serial, so +# they can't be told apart by ID. We no longer pin per-port symlinks (they broke +# whenever the adapters moved to a hub / different USB ports). Instead we grant +# 0666 to ANY CH340 tty and let inspire_g1 open /dev/ttyUSB0 + /dev/ttyUSB1 +# directly (the only ttyUSB on this box are the two hands). Port-independent — +# works no matter which USB/hub port they land on. +# +# If right/left come out swapped, swap serial1/serial2 in inspire_g1.cpp. + +SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE="0666" diff --git a/vendor/unitree_sdk2_python/.gitignore b/vendor/unitree_sdk2_python/.gitignore new file mode 100644 index 0000000..001de9a --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/LICENSE b/vendor/unitree_sdk2_python/LICENSE new file mode 100644 index 0000000..42d2e64 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/README zh.md b/vendor/unitree_sdk2_python/README zh.md new file mode 100644 index 0000000..a7e6a8c --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/README.md b/vendor/unitree_sdk2_python/README.md new file mode 100644 index 0000000..fa4df00 --- /dev/null +++ b/vendor/unitree_sdk2_python/README.md @@ -0,0 +1,113 @@ +# unitree_sdk2_python +Python interface for unitree sdk2 + +# Installation +## Dependencies +- Python >= 3.8 +- cyclonedds == 0.10.2 +- numpy +- opencv-python + +### 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 diff --git a/vendor/unitree_sdk2_python/example/a2/audio/a2_audio_client_example.py b/vendor/unitree_sdk2_python/example/a2/audio/a2_audio_client_example.py new file mode 100644 index 0000000..e8a6b97 --- /dev/null +++ b/vendor/unitree_sdk2_python/example/a2/audio/a2_audio_client_example.py @@ -0,0 +1,39 @@ +import sys +import time + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.a2.audio.audio_client import AudioClient + + +if __name__ == "__main__": + if len(sys.argv) < 2: + print(f"Usage: {sys.argv[0]} networkInterface") + sys.exit(-1) + + ChannelFactoryInitialize(0, sys.argv[1]) + + audio_client = AudioClient() + audio_client.SetTimeout(10.0) + audio_client.Init() + + ret = audio_client.GetVolume() + print("debug GetVolume: ", ret) + + audio_client.SetVolume(85) + + ret = audio_client.GetVolume() + print("debug GetVolume: ", ret) + + 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/vendor/unitree_sdk2_python/example/a2/audio/a2_audio_client_play_wav.py b/vendor/unitree_sdk2_python/example/a2/audio/a2_audio_client_play_wav.py new file mode 100644 index 0000000..f514c17 --- /dev/null +++ b/vendor/unitree_sdk2_python/example/a2/audio/a2_audio_client_play_wav.py @@ -0,0 +1,38 @@ +import sys +from pathlib import Path + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.a2.audio.audio_client import AudioClient +from wav import read_wav, play_pcm_stream + + +def main(): + if len(sys.argv) < 3: + print(f"Usage: {sys.argv[0]} ") + sys.exit(1) + + net_interface = sys.argv[1] + wav_path = sys.argv[2] + + ChannelFactoryInitialize(0, net_interface) + audio_client = AudioClient() + audio_client.SetTimeout(10.0) + audio_client.Init() + + pcm_list, sample_rate, num_channels, is_ok = read_wav(wav_path) + print(f"[DEBUG] Read success: {is_ok}") + print(f"[DEBUG] Sample rate: {sample_rate} Hz") + print(f"[DEBUG] Channels: {num_channels}") + print(f"[DEBUG] PCM byte length: {len(pcm_list)}") + + if not is_ok or sample_rate != 16000 or num_channels != 1: + print("[ERROR] Failed to read WAV file or unsupported format (must be 16kHz mono)") + return + + play_pcm_stream(audio_client, pcm_list, "example") + audio_client.PlayStop("example") + + +if __name__ == "__main__": + main() + diff --git a/vendor/unitree_sdk2_python/example/a2/audio/test.wav b/vendor/unitree_sdk2_python/example/a2/audio/test.wav new file mode 100644 index 0000000000000000000000000000000000000000..d0bd0bbca59d5cf6abe4b4aad75bda31b5e381fc GIT binary patch literal 132246 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 literal 0 HcmV?d00001 diff --git a/vendor/unitree_sdk2_python/example/a2/audio/wav.py b/vendor/unitree_sdk2_python/example/a2/audio/wav.py new file mode 100644 index 0000000..015fe8f --- /dev/null +++ b/vendor/unitree_sdk2_python/example/a2/audio/wav.py @@ -0,0 +1,166 @@ +import struct +import time + +def read_wav(filename): + try: + with open(filename, 'rb') as f: + def read(fmt): + return struct.unpack(fmt, f.read(struct.calcsize(fmt))) + + # === Chunk Header === + chunk_id, = read(' 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/vendor/unitree_sdk2_python/example/b2/camera/capture_image.py b/vendor/unitree_sdk2_python/example/b2/camera/capture_image.py new file mode 100644 index 0000000..dcfaad6 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/b2/high_level/b2_sport_client.py b/vendor/unitree_sdk2_python/example/b2/high_level/b2_sport_client.py new file mode 100644 index 0000000..25aafe2 --- /dev/null +++ b/vendor/unitree_sdk2_python/example/b2/high_level/b2_sport_client.py @@ -0,0 +1,106 @@ +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="BalanceStand", id=1), + TestOption(name="StopMove", id=2), + TestOption(name="StandUp", id=3), + TestOption(name="StandDown", id=4), + TestOption(name="RecoveryStand", id=5), + TestOption(name="Move", id=6), + TestOption(name="FreeWalk", id=7), + TestOption(name="ClassicWalk", 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__": + + 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.\n" + "NOTE: Some interfaces are not demonstrated in this example for safety reasons. " + "If you need to use them, please contact technical support: https://serviceconsole.unitree.com/index.html#/") + 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: + print(f"ret:{sport_client.Damp()}") + elif test_option.id == 1: + print(f"ret:{sport_client.BalanceStand()}") + elif test_option.id == 2: + print(f"ret:{sport_client.StopMove()}") + elif test_option.id == 3: + print(f"ret:{sport_client.StandUp()}") + elif test_option.id == 4: + print(f"ret:{sport_client.StandDown()}") + elif test_option.id == 5: + print(f"ret:{sport_client.RecoveryStand()}") + elif test_option.id == 6: + print(f"ret:{sport_client.Move(0.5,0.0,0.0)}") + elif test_option.id == 7: + print(f"ret:{sport_client.FreeWalk()}") + elif test_option.id == 8: + print(f"ret:{sport_client.ClassicWalk(True)}") + print(f"ret:{sport_client.Move(0.1,0.0,0.0)}") + time.sleep(2) + print(f"ret:{sport_client.ClassicWalk(False)}") + print(f"ret:{sport_client.Move(-0.3,0.0,0.0)}") + time.sleep(2) + + time.sleep(1) diff --git a/vendor/unitree_sdk2_python/example/b2/low_level/b2_stand_example.py b/vendor/unitree_sdk2_python/example/b2/low_level/b2_stand_example.py new file mode 100644 index 0000000..a091630 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/b2/low_level/unitree_legged_const.py b/vendor/unitree_sdk2_python/example/b2/low_level/unitree_legged_const.py new file mode 100644 index 0000000..153a051 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/b2w/camera/camera_opencv.py b/vendor/unitree_sdk2_python/example/b2w/camera/camera_opencv.py new file mode 100644 index 0000000..3fafcef --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/b2w/camera/capture_image.py b/vendor/unitree_sdk2_python/example/b2w/camera/capture_image.py new file mode 100644 index 0000000..dcfaad6 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/b2w/high_level/b2w_sport_client.py b/vendor/unitree_sdk2_python/example/b2w/high_level/b2w_sport_client.py new file mode 100644 index 0000000..e324f75 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/b2w/low_level/b2w_stand_example.py b/vendor/unitree_sdk2_python/example/b2w/low_level/b2w_stand_example.py new file mode 100644 index 0000000..b535059 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/b2w/low_level/unitree_legged_const.py b/vendor/unitree_sdk2_python/example/b2w/low_level/unitree_legged_const.py new file mode 100644 index 0000000..7943e31 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/g1/audio/g1_audio_client_example.py b/vendor/unitree_sdk2_python/example/g1/audio/g1_audio_client_example.py new file mode 100644 index 0000000..6e0cc25 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/g1/audio/g1_audio_client_play_wav.py b/vendor/unitree_sdk2_python/example/g1/audio/g1_audio_client_play_wav.py new file mode 100644 index 0000000..3f3b6de --- /dev/null +++ b/vendor/unitree_sdk2_python/example/g1/audio/g1_audio_client_play_wav.py @@ -0,0 +1,35 @@ +import sys +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.g1.audio.g1_audio_client import AudioClient +from wav import read_wav, play_pcm_stream + +def main(): + if len(sys.argv) < 3: + print(f"Usage: python3 {sys.argv[0]} ") + sys.exit(1) + + net_interface = sys.argv[1] + wav_path = sys.argv[2] + + ChannelFactoryInitialize(0, net_interface) + audioClient = AudioClient() + audioClient.SetTimeout(10.0) + audioClient.Init() + + pcm_list, sample_rate, num_channels, is_ok = read_wav(wav_path) + print(f"[DEBUG] Read success: {is_ok}") + print(f"[DEBUG] Sample rate: {sample_rate} Hz") + print(f"[DEBUG] Channels: {num_channels}") + print(f"[DEBUG] PCM byte length: {len(pcm_list)}") + + if not is_ok or sample_rate != 16000 or num_channels != 1: + print("[ERROR] Failed to read WAV file or unsupported format (must be 16kHz mono)") + return + + play_pcm_stream(audioClient, pcm_list, "example") + + audioClient.PlayStop("example") + +if __name__ == "__main__": + main() + diff --git a/vendor/unitree_sdk2_python/example/g1/audio/test.wav b/vendor/unitree_sdk2_python/example/g1/audio/test.wav new file mode 100644 index 0000000000000000000000000000000000000000..d0bd0bbca59d5cf6abe4b4aad75bda31b5e381fc GIT binary patch literal 132246 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 literal 0 HcmV?d00001 diff --git a/vendor/unitree_sdk2_python/example/g1/audio/wav.py b/vendor/unitree_sdk2_python/example/g1/audio/wav.py new file mode 100644 index 0000000..015fe8f --- /dev/null +++ b/vendor/unitree_sdk2_python/example/g1/audio/wav.py @@ -0,0 +1,166 @@ +import struct +import time + +def read_wav(filename): + try: + with open(filename, 'rb') as f: + def read(fmt): + return struct.unpack(fmt, f.read(struct.calcsize(fmt))) + + # === Chunk Header === + chunk_id, = read('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/vendor/unitree_sdk2_python/example/g1/high_level/g1_arm7_sdk_dds_example.py b/vendor/unitree_sdk2_python/example/g1/high_level/g1_arm7_sdk_dds_example.py new file mode 100644 index 0000000..6ced14f --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/g1/high_level/g1_arm_action_example.py b/vendor/unitree_sdk2_python/example/g1/high_level/g1_arm_action_example.py new file mode 100644 index 0000000..7b0c289 --- /dev/null +++ b/vendor/unitree_sdk2_python/example/g1/high_level/g1_arm_action_example.py @@ -0,0 +1,141 @@ +import time +import sys +from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize +from unitree_sdk2py.g1.arm.g1_arm_action_client import G1ArmActionClient +from unitree_sdk2py.g1.arm.g1_arm_action_client import action_map +from dataclasses import dataclass + +@dataclass +class TestOption: + name: str + id: int + +option_list = [ + TestOption(name="release arm", id=0), + TestOption(name="shake hand", id=1), + TestOption(name="high five", id=2), + TestOption(name="hug", id=3), + TestOption(name="high wave", id=4), + TestOption(name="clap", id=5), + TestOption(name="face wave", id=6), + TestOption(name="left kiss", id=7), + TestOption(name="heart", id=8), + TestOption(name="right heart", id=9), + TestOption(name="hands up", id=10), + TestOption(name="x-ray", id=11), + TestOption(name="right hand up", id=12), + TestOption(name="reject", id=13), + TestOption(name="right kiss", id=14), + TestOption(name="two-hand kiss", id=15), + +] + +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 + + armAction_client = G1ArmActionClient() + armAction_client.SetTimeout(10.0) + armAction_client.Init() + + # actionList = armAction_client.GetActionList() + # print("actionList\n",actionList) + + 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: + armAction_client.ExecuteAction(action_map.get("release arm")) + elif test_option.id == 1: + armAction_client.ExecuteAction(action_map.get("shake hand")) + time.sleep(2) + armAction_client.ExecuteAction(action_map.get("release arm")) + elif test_option.id == 2: + armAction_client.ExecuteAction(action_map.get("high five")) + time.sleep(2) + armAction_client.ExecuteAction(action_map.get("release arm")) + elif test_option.id == 3: + armAction_client.ExecuteAction(action_map.get("hug")) + time.sleep(2) + armAction_client.ExecuteAction(action_map.get("release arm")) + elif test_option.id == 4: + armAction_client.ExecuteAction(action_map.get("high wave")) + elif test_option.id == 5: + armAction_client.ExecuteAction(action_map.get("clap")) + elif test_option.id == 6: + armAction_client.ExecuteAction(action_map.get("face wave")) + elif test_option.id == 7: + armAction_client.ExecuteAction(action_map.get("left kiss")) + elif test_option.id == 8: + armAction_client.ExecuteAction(action_map.get("heart")) + time.sleep(2) + armAction_client.ExecuteAction(action_map.get("release arm")) + elif test_option.id == 9: + armAction_client.ExecuteAction(action_map.get("right heart")) + time.sleep(2) + armAction_client.ExecuteAction(action_map.get("release arm")) + elif test_option.id == 10: + armAction_client.ExecuteAction(action_map.get("hands up")) + time.sleep(2) + armAction_client.ExecuteAction(action_map.get("release arm")) + elif test_option.id == 11: + armAction_client.ExecuteAction(action_map.get("x-ray")) + time.sleep(2) + armAction_client.ExecuteAction(action_map.get("release arm")) + elif test_option.id == 12: + armAction_client.ExecuteAction(action_map.get("right hand up")) + time.sleep(2) + armAction_client.ExecuteAction(action_map.get("release arm")) + elif test_option.id == 13: + armAction_client.ExecuteAction(action_map.get("reject")) + time.sleep(2) + armAction_client.ExecuteAction(action_map.get("release arm")) + elif test_option.id == 14: + armAction_client.ExecuteAction(action_map.get("right kiss")) + elif test_option.id == 15: + armAction_client.ExecuteAction(action_map.get("two-hand kiss")) + + time.sleep(1) \ No newline at end of file diff --git a/vendor/unitree_sdk2_python/example/g1/high_level/g1_loco_client_example.py b/vendor/unitree_sdk2_python/example/g1/high_level/g1_loco_client_example.py new file mode 100644 index 0000000..55ee059 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/g1/low_level/g1_low_level_example.py b/vendor/unitree_sdk2_python/example/g1/low_level/g1_low_level_example.py new file mode 100644 index 0000000..382e863 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/g1/readme.md b/vendor/unitree_sdk2_python/example/g1/readme.md new file mode 100644 index 0000000..9fb7900 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/go2/front_camera/camera_opencv.py b/vendor/unitree_sdk2_python/example/go2/front_camera/camera_opencv.py new file mode 100644 index 0000000..9d98839 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/go2/front_camera/capture_image.py b/vendor/unitree_sdk2_python/example/go2/front_camera/capture_image.py new file mode 100644 index 0000000..1e85643 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/go2/high_level/go2_sport_client.py b/vendor/unitree_sdk2_python/example/go2/high_level/go2_sport_client.py new file mode 100644 index 0000000..23873e6 --- /dev/null +++ b/vendor/unitree_sdk2_python/example/go2/high_level/go2_sport_client.py @@ -0,0 +1,155 @@ +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="hand stand", id=7), + TestOption(name="balanced stand", id=9), + 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 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__": + + + 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 = 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: + ret = sport_client.Move(0.3,0,0) + print("ret: ",ret) + 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.HandStand(True) + time.sleep(4) + sport_client.HandStand(False) + 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() + 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 == 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/vendor/unitree_sdk2_python/example/go2/high_level/go2_utlidar_switch.py b/vendor/unitree_sdk2_python/example/go2/high_level/go2_utlidar_switch.py new file mode 100644 index 0000000..09e0ce9 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/go2/low_level/go2_stand_example.py b/vendor/unitree_sdk2_python/example/go2/low_level/go2_stand_example.py new file mode 100644 index 0000000..f2241ce --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/go2/low_level/unitree_legged_const.py b/vendor/unitree_sdk2_python/example/go2/low_level/unitree_legged_const.py new file mode 100644 index 0000000..153a051 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/go2w/high_level/go2w_sport_client.py b/vendor/unitree_sdk2_python/example/go2w/high_level/go2w_sport_client.py new file mode 100644 index 0000000..824a882 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/go2w/low_level/go2w_stand_example.py b/vendor/unitree_sdk2_python/example/go2w/low_level/go2w_stand_example.py new file mode 100644 index 0000000..e3a54de --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/go2w/low_level/unitree_legged_const.py b/vendor/unitree_sdk2_python/example/go2w/low_level/unitree_legged_const.py new file mode 100644 index 0000000..7943e31 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/h1/high_level/h1_loco_client_example.py b/vendor/unitree_sdk2_python/example/h1/high_level/h1_loco_client_example.py new file mode 100644 index 0000000..4432d8a --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/h1/low_level/h1_low_level_example.py b/vendor/unitree_sdk2_python/example/h1/low_level/h1_low_level_example.py new file mode 100644 index 0000000..51a9111 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/h1/low_level/unitree_legged_const.py b/vendor/unitree_sdk2_python/example/h1/low_level/unitree_legged_const.py new file mode 100644 index 0000000..c9112ea --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/h1_2/low_level/h1_2_low_level_example.py b/vendor/unitree_sdk2_python/example/h1_2/low_level/h1_2_low_level_example.py new file mode 100644 index 0000000..0959b32 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/h2/high_level/h2_arm_sdk_dds_example.py b/vendor/unitree_sdk2_python/example/h2/high_level/h2_arm_sdk_dds_example.py new file mode 100644 index 0000000..819fd07 --- /dev/null +++ b/vendor/unitree_sdk2_python/example/h2/high_level/h2_arm_sdk_dds_example.py @@ -0,0 +1,198 @@ +import sys +import time + +import numpy as np + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize, ChannelPublisher, ChannelSubscriber +from unitree_sdk2py.idl.default import unitree_hg_msg_dds__LowCmd_ +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowCmd_, LowState_ +from unitree_sdk2py.utils.crc import CRC +from unitree_sdk2py.utils.thread import RecurrentThread + + +SMALL_SHOULDER_ROLL = np.deg2rad(20.0) +SMALL_ELBOW = np.deg2rad(30.0) + + +class H2JointIndex: + # 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 + + # Waist + WaistYaw = 12 + WaistRoll = 13 + WaistPitch = 14 + + # Left arm + LeftShoulderPitch = 15 + LeftShoulderRoll = 16 + LeftShoulderYaw = 17 + LeftElbow = 18 + LeftWristRoll = 19 + LeftWristPitch = 20 + LeftWristYaw = 21 + + # Right arm + RightShoulderPitch = 22 + RightShoulderRoll = 23 + RightShoulderYaw = 24 + RightElbow = 25 + RightWristRoll = 26 + RightWristPitch = 27 + RightWristYaw = 28 + + # Head + HeadYaw = 29 + HeadPitch = 30 + + kNotUsedJoint = 31 # NOTE: Weight + + +class Custom: + def __init__(self): + self.time_ = 0.0 + self.control_dt_ = 0.02 + self.duration_ = 3.0 + self.kp = 80.0 + self.kd = 1.5 + 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, SMALL_SHOULDER_ROLL, 0.0, SMALL_ELBOW, 0.0, 0.0, 0.0, + 0.0, -SMALL_SHOULDER_ROLL, 0.0, SMALL_ELBOW, 0.0, 0.0, 0.0, + ] + + self.arm_joints = [ + H2JointIndex.LeftShoulderPitch, H2JointIndex.LeftShoulderRoll, + H2JointIndex.LeftShoulderYaw, H2JointIndex.LeftElbow, + H2JointIndex.LeftWristRoll, H2JointIndex.LeftWristPitch, + H2JointIndex.LeftWristYaw, + H2JointIndex.RightShoulderPitch, H2JointIndex.RightShoulderRoll, + H2JointIndex.RightShoulderYaw, H2JointIndex.RightElbow, + H2JointIndex.RightWristRoll, H2JointIndex.RightWristPitch, + H2JointIndex.RightWristYaw, + ] + + 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 not self.first_update_low_state: + time.sleep(1) + + self.lowCmdWriteThreadPtr.Start() + + def LowStateHandler(self, msg: LowState_): + self.low_state = msg + + if not self.first_update_low_state: + self.first_update_low_state = True + + def LowCmdWrite(self): + if self.low_state is None: + return + + self.time_ += self.control_dt_ + + if self.time_ < self.duration_: + print("[Stage 1]: set arms to zero posture.") + # [Stage 1]: set arms to zero posture + self.low_cmd.motor_cmd[H2JointIndex.kNotUsedJoint].q = 1.0 + + 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.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.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: + print("[Stage 2]: lift arms up.") + # [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.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.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: + print("[Stage 3]: set arms back to zero posture.") + # [Stage 3]: set arms 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.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.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: + print("[Stage 4]: release arm_sdk.") + # [Stage 4]: release arm_sdk + ratio = np.clip((self.time_ - self.duration_ * 6) / self.duration_, 0.0, 1.0) + self.low_cmd.motor_cmd[H2JointIndex.kNotUsedJoint].q = 1.0 - ratio + + 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) diff --git a/vendor/unitree_sdk2_python/example/h2/high_level/h2_loco_client_example.py b/vendor/unitree_sdk2_python/example/h2/high_level/h2_loco_client_example.py new file mode 100644 index 0000000..2e8a7ee --- /dev/null +++ b/vendor/unitree_sdk2_python/example/h2/high_level/h2_loco_client_example.py @@ -0,0 +1,111 @@ +import sys +import time +from dataclasses import dataclass + +from unitree_sdk2py.core.channel import ChannelFactoryInitialize +from unitree_sdk2py.h2.loco.h2_loco_client import LocoClient + + +@dataclass +class TestOption: + name: str + id: int + + +option_list = [ + TestOption(name="damp", id=0), + TestOption(name="start", id=1), + TestOption(name="stand_up", id=2), + TestOption(name="zero_torque", id=3), + TestOption(name="stop_move", id=4), + TestOption(name="get_fsm_id", id=5), + TestOption(name="get_fsm_mode", id=6), + TestOption(name="set_fsm_id", id=7), + TestOption(name="set_velocity", id=8), + TestOption(name="move", id=9), + TestOption(name="switch_move_mode", id=10), + TestOption(name="set_speed_mode", id=11), +] + + +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() + + 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.Start() + elif test_option.id == 2: + sport_client.StandUp() + elif test_option.id == 3: + sport_client.ZeroTorque() + elif test_option.id == 4: + sport_client.StopMove() + elif test_option.id == 5: + code, fsm_id = sport_client.GetFsmId() + print("GetFsmId:", code, fsm_id) + elif test_option.id == 6: + code, fsm_mode = sport_client.GetFsmMode() + print("GetFsmMode:", code, fsm_mode) + elif test_option.id == 7: + sport_client.SetFsmId(4) + elif test_option.id == 8: + sport_client.SetVelocity(0.0, 0.0, 1.0, 4.0) + elif test_option.id == 9: + sport_client.Move(0.0, 0.0, 2) + elif test_option.id == 10: + sport_client.SwitchMoveMode(True) + elif test_option.id == 11: + sport_client.SetSpeedMode(1) + + time.sleep(1) \ No newline at end of file diff --git a/vendor/unitree_sdk2_python/example/h2/low_level/h2_ankle_swing_example.py b/vendor/unitree_sdk2_python/example/h2/low_level/h2_ankle_swing_example.py new file mode 100644 index 0000000..30dd87d --- /dev/null +++ b/vendor/unitree_sdk2_python/example/h2/low_level/h2_ankle_swing_example.py @@ -0,0 +1,206 @@ +import sys +import time + +import numpy as np + +from unitree_sdk2py.comm.motion_switcher.motion_switcher_client import MotionSwitcherClient +from unitree_sdk2py.core.channel import ChannelFactoryInitialize, ChannelPublisher, ChannelSubscriber +from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowCmd_, LowState_ +from unitree_sdk2py.utils.crc import CRC +from unitree_sdk2py.utils.thread import RecurrentThread + + +H2_NUM_MOTOR = 31 +HG_CMD_TOPIC = "rt/lowcmd" +HG_STATE_TOPIC = "rt/lowstate" + +Kp = [ + 200, 200, 200, 200, 200, 200, # legs + 200, 200, 200, 200, 200, 200, # legs + 300, 300, 300, # waist + 100, 100, 100, 100, 50, 50, 50, # arms + 100, 100, 100, 100, 50, 50, 50, # arms + 50, 10 # head +] + +Kd = [ + 3, 3, 3, 3, 3, 3, # legs + 3, 3, 3, 3, 3, 3, # legs + 5, 5, 5, # waist + 2, 2, 2, 2, 2, 2, 2, # arms + 2, 2, 2, 2, 2, 2, 2, # arms + 2, 0.1 # head +] + +joint_idx_in_idl = [ + 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, + 29, 30 +] + + +class Mode: + PR = 0 + AB = 1 + + +class H2JointIndex: + LeftHipPitch = 0 + LeftHipRoll = 1 + LeftHipYaw = 2 + LeftKnee = 3 + LeftAnkleRoll = 4 + LeftAnkleRollRaw = 4 + LeftAnklePitch = 5 + LeftAnklePitchRaw = 5 + RightHipPitch = 6 + RightHipRoll = 7 + RightHipYaw = 8 + RightKnee = 9 + RightAnkleRoll = 10 + RightAnkleRollRaw = 10 + RightAnklePitch = 11 + RightAnklePitchRaw = 11 + WaistYaw = 12 + WaistRoll = 13 + WaistA = 13 + WaistPitch = 14 + WaistB = 14 + LeftShoulderPitch = 15 + LeftShoulderRoll = 16 + LeftShoulderYaw = 17 + LeftElbow = 18 + LeftWristRoll = 19 + LeftWristpitch = 20 + LeftWristyaw = 21 + RightShoulderPitch = 22 + RightShoulderRoll = 23 + RightShoulderYaw = 24 + RightElbow = 25 + RightWristRoll = 26 + RightWristpitch = 27 + RightWristyaw = 28 + HEAD_PITCH = 29 + HEAD_YAW = 30 + + +class Custom: + def __init__(self): + self.time_ = 0.0 + self.control_dt_ = 0.002 + self.duration_ = 3.0 + self.mode_pr_ = Mode.PR + self.mode_machine_ = 0 + self.update_mode_machine_ = False + + self.low_cmd = LowCmd_() + self.low_state = None + self.crc = CRC() + + def Init(self): + # try to shutdown motion control-related service + self.msc = MotionSwitcherClient() + self.msc.SetTimeout(5.0) + self.msc.Init() + + status, result = self.msc.CheckMode() + while result.get("name"): + self.msc.ReleaseMode() + status, result = self.msc.CheckMode() + time.sleep(1) + + self.lowcmd_publisher_ = ChannelPublisher(HG_CMD_TOPIC, LowCmd_) + self.lowcmd_publisher_.Init() + + self.lowstate_subscriber_ = ChannelSubscriber(HG_STATE_TOPIC, LowState_) + self.lowstate_subscriber_.Init(self.LowStateHandler, 10) + + def Start(self): + self.control_thread_ = RecurrentThread( + interval=self.control_dt_, target=self.LowCmdWrite, name="control" + ) + while not self.update_mode_machine_: + time.sleep(0.1) + self.control_thread_.Start() + + def LowStateHandler(self, msg: LowState_): + self.low_state = msg + if not self.update_mode_machine_: + self.mode_machine_ = self.low_state.mode_machine + self.update_mode_machine_ = True + + def LowCmdWrite(self): + if self.low_state is None: + return + + self.time_ += self.control_dt_ + + # base fill for all joints we care about + self.low_cmd.mode_pr = self.mode_pr_ + self.low_cmd.mode_machine = self.mode_machine_ + + for i in range(H2_NUM_MOTOR): + slot = joint_idx_in_idl[i] + self.low_cmd.motor_cmd[slot].mode = 1 + self.low_cmd.motor_cmd[slot].tau = 0.0 + self.low_cmd.motor_cmd[slot].dq = 0.0 + self.low_cmd.motor_cmd[slot].kp = Kp[i] + self.low_cmd.motor_cmd[slot].kd = Kd[i] + + if self.time_ < self.duration_: + ratio = float(np.clip(self.time_ / self.duration_, 0.0, 1.0)) + for i in range(H2_NUM_MOTOR): + slot = joint_idx_in_idl[i] + self.low_cmd.motor_cmd[slot].q = (1.0 - ratio) * self.low_state.motor_state[slot].q + elif self.time_ < self.duration_ * 2.0: + self.mode_pr_ = Mode.PR + self.low_cmd.mode_pr = self.mode_pr_ + max_P = np.pi * 30.0 / 180.0 + max_R = np.pi * 30.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.motor_cmd[joint_idx_in_idl[H2JointIndex.LeftAnklePitch]].q = float(L_P_des) + self.low_cmd.motor_cmd[joint_idx_in_idl[H2JointIndex.LeftAnkleRoll]].q = float(L_R_des) + self.low_cmd.motor_cmd[joint_idx_in_idl[H2JointIndex.RightAnklePitch]].q = float(R_P_des) + self.low_cmd.motor_cmd[joint_idx_in_idl[H2JointIndex.RightAnkleRoll]].q = float(R_R_des) + else: + self.mode_pr_ = Mode.AB + self.low_cmd.mode_pr = self.mode_pr_ + max_A = np.pi * 30.0 / 180.0 + max_B = np.pi * 10.0 / 180.0 + t = self.time_ - self.duration_ * 2.0 + L_A_des = +max_A * np.sin(np.pi * t) + L_B_des = +max_B * np.sin(np.pi * t + np.pi) + R_A_des = -max_A * np.sin(np.pi * t) + R_B_des = -max_B * np.sin(np.pi * t + np.pi) + self.low_cmd.motor_cmd[joint_idx_in_idl[H2JointIndex.LeftAnklePitchRaw]].q = float(L_A_des) + self.low_cmd.motor_cmd[joint_idx_in_idl[H2JointIndex.LeftAnkleRollRaw]].q = float(L_B_des) + self.low_cmd.motor_cmd[joint_idx_in_idl[H2JointIndex.RightAnklePitchRaw]].q = float(R_A_des) + self.low_cmd.motor_cmd[joint_idx_in_idl[H2JointIndex.RightAnkleRollRaw]].q = float(R_B_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) + diff --git a/vendor/unitree_sdk2_python/example/helloworld/publisher.py b/vendor/unitree_sdk2_python/example/helloworld/publisher.py new file mode 100644 index 0000000..cfe4f1f --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/helloworld/subscriber.py b/vendor/unitree_sdk2_python/example/helloworld/subscriber.py new file mode 100644 index 0000000..a7d0b63 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/helloworld/user_data.py b/vendor/unitree_sdk2_python/example/helloworld/user_data.py new file mode 100644 index 0000000..9696ce3 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/motionSwitcher/motion_switcher_example.py b/vendor/unitree_sdk2_python/example/motionSwitcher/motion_switcher_example.py new file mode 100644 index 0000000..4f39f72 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_move.py b/vendor/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_move.py new file mode 100644 index 0000000..bdbd833 --- /dev/null +++ b/vendor/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_move.py @@ -0,0 +1,38 @@ +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) + time.sleep(1.0) + client.MoveToAbsolutePosition(0.0, 0.0, 0.0) + client.MoveToIncrementPosition(0.0, 0.0, 0.0) + + except KeyboardInterrupt: + client.Move(0.0, 0.0, 0.0) + client.UseRemoteCommandFromApi(False) + print("exit!!") + \ No newline at end of file diff --git a/vendor/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_switch.py b/vendor/unitree_sdk2_python/example/obstacles_avoid/obstacles_avoid_switch.py new file mode 100644 index 0000000..eb345c0 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/vui_client/vui_client_example.py b/vendor/unitree_sdk2_python/example/vui_client/vui_client_example.py new file mode 100644 index 0000000..a5e6932 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/example/wireless_controller/wireless_controller.py b/vendor/unitree_sdk2_python/example/wireless_controller/wireless_controller.py new file mode 100644 index 0000000..995487f --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/pyproject.toml b/vendor/unitree_sdk2_python/pyproject.toml new file mode 100644 index 0000000..07de284 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/setup.py b/vendor/unitree_sdk2_python/setup.py new file mode 100644 index 0000000..54d8cac --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/__init__.py new file mode 100644 index 0000000..8d180ee --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/__init__.py @@ -0,0 +1,15 @@ +from . import idl, utils, core, rpc, comm, go2, b2, g1, h1, h2, a2 + +__all__ = [ + "idl", + "utils", + "core", + "rpc", + "comm", + "go2", + "b2", + "g1", + "h1", + "h2", + "a2", +] diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/a2/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/a2/__init__.py new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/a2/__init__.py @@ -0,0 +1 @@ + diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/a2/audio/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/a2/audio/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/a2/audio/audio_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/a2/audio/audio_api.py new file mode 100644 index 0000000..da665ab --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/a2/audio/audio_api.py @@ -0,0 +1,26 @@ +""" +" 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 +""" +AUDIO_ERR_COMM = 100 + diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/a2/audio/audio_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/a2/audio/audio_client.py new file mode 100644 index 0000000..1698403 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/a2/audio/audio_client.py @@ -0,0 +1,74 @@ +import json +from typing import Optional, Tuple + +from ...rpc.client import Client +from .audio_api import * + + +""" +" class AudioClient +""" +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) -> int: + p = {} + p["index"] = self.tts_index + p["text"] = text + p["speaker_id"] = speaker_id + self.tts_index += 1 + 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) -> int: + p = {} + p["volume"] = 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) -> 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 + + def PlayStream(self, app_name: str, stream_id: str, pcm_data: bytes): + param = json.dumps({"app_name": app_name, "stream_id": stream_id}) + pcm_list = list(pcm_data) + return self._CallRequestWithParamAndBin(ROBOT_API_ID_AUDIO_START_PLAY, param, pcm_list) + + def PlayStop(self, app_name: str) -> int: + parameter = json.dumps({"app_name": app_name}) + code, data = self._Call(ROBOT_API_ID_AUDIO_STOP_PLAY, parameter) + return 0 + diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/a2/sport/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/a2/sport/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/a2/sport/sport_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/a2/sport/sport_api.py new file mode 100644 index 0000000..0210e42 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/a2/sport/sport_api.py @@ -0,0 +1,41 @@ +""" +" service name +""" +ROBOT_SPORT_SERVICE_NAME = "sport" + +""" +" service api version +""" +ROBOT_SPORT_API_VERSION = "1.0.0.1" + +""" +" api id +""" +ROBOT_SPORT_API_ID_DAMP = 1001 +ROBOT_SPORT_API_ID_BALANCESTAND = 1002 +ROBOT_SPORT_API_ID_STOPMOVE = 1003 +ROBOT_SPORT_API_ID_STANDUP = 1004 +ROBOT_SPORT_API_ID_STANDDOWN = 1005 +ROBOT_SPORT_API_ID_RECOVERYSTAND = 1006 +ROBOT_SPORT_API_ID_EULER = 1007 +ROBOT_SPORT_API_ID_MOVE = 1008 +ROBOT_SPORT_API_ID_BODYPOSITION = 1009 +ROBOT_SPORT_API_ID_SWITCHGAIT = 1011 +ROBOT_SPORT_API_ID_BODYHEIGHT = 1013 +ROBOT_SPORT_API_ID_SPEEDLEVEL = 1015 +ROBOT_SPORT_API_ID_LEFTSIDEGAIT = 1016 +ROBOT_SPORT_API_ID_RIGHTSIDEGAIT = 1017 +ROBOT_SPORT_API_ID_HANDSTAND = 1018 +ROBOT_SPORT_API_ID_BIPEDSTAND = 1019 +ROBOT_SPORT_API_ID_FRONTFLIP = 1020 +ROBOT_SPORT_API_ID_BACKFLIP = 1021 +ROBOT_SPORT_API_ID_SETAUTORECOVERY = 1040 +ROBOT_SPORT_API_ID_GETSTATE = 1034 + +""" +" error code +""" +# server side +SPORT_ERR_SERVER_OVERTIME = 4201 +SPORT_ERR_SERVER_NOT_INIT = 4205 + diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/a2/sport/sport_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/a2/sport/sport_client.py new file mode 100644 index 0000000..d8d1e7f --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/a2/sport/sport_client.py @@ -0,0 +1,182 @@ +import json +from typing import Dict + +from ...rpc.client import Client +from .sport_api import * + + +""" +" class SportClient +""" +class SportClient(Client): + def __init__(self): + super().__init__(ROBOT_SPORT_SERVICE_NAME, False) + + def Init(self): + # set api version + self._SetApiVerson(ROBOT_SPORT_API_VERSION) + + # regist api + self._RegistApi(ROBOT_SPORT_API_ID_DAMP, 0) + self._RegistApi(ROBOT_SPORT_API_ID_BALANCESTAND, 0) + self._RegistApi(ROBOT_SPORT_API_ID_STOPMOVE, 0) + self._RegistApi(ROBOT_SPORT_API_ID_STANDUP, 0) + self._RegistApi(ROBOT_SPORT_API_ID_STANDDOWN, 0) + self._RegistApi(ROBOT_SPORT_API_ID_RECOVERYSTAND, 0) + self._RegistApi(ROBOT_SPORT_API_ID_EULER, 0) + self._RegistApi(ROBOT_SPORT_API_ID_MOVE, 0) + self._RegistApi(ROBOT_SPORT_API_ID_SWITCHGAIT, 0) + self._RegistApi(ROBOT_SPORT_API_ID_BODYHEIGHT, 0) + self._RegistApi(ROBOT_SPORT_API_ID_SPEEDLEVEL, 0) + self._RegistApi(ROBOT_SPORT_API_ID_SETAUTORECOVERY, 0) + + self._RegistApi(ROBOT_SPORT_API_ID_BODYPOSITION, 0) + self._RegistApi(ROBOT_SPORT_API_ID_LEFTSIDEGAIT, 0) + self._RegistApi(ROBOT_SPORT_API_ID_RIGHTSIDEGAIT, 0) + self._RegistApi(ROBOT_SPORT_API_ID_HANDSTAND, 0) + self._RegistApi(ROBOT_SPORT_API_ID_BIPEDSTAND, 0) + self._RegistApi(ROBOT_SPORT_API_ID_FRONTFLIP, 0) + self._RegistApi(ROBOT_SPORT_API_ID_BACKFLIP, 0) + + self._RegistApi(ROBOT_SPORT_API_ID_GETSTATE, 0) + + def Damp(self) -> int: + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_DAMP, parameter) + return code + + def BalanceStand(self) -> int: + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_BALANCESTAND, parameter) + return code + + def StopMove(self) -> int: + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_STOPMOVE, parameter) + return code + + def StandUp(self) -> int: + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_STANDUP, parameter) + return code + + def StandDown(self) -> int: + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_STANDDOWN, parameter) + return code + + def RecoveryStand(self) -> int: + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_RECOVERYSTAND, parameter) + return code + + def Euler(self, roll: float, pitch: float, yaw: float) -> int: + p = {} + p["x"] = roll + p["y"] = pitch + p["z"] = yaw + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_EULER, parameter) + return code + + def Move(self, vx: float, vy: float, vyaw: float) -> int: + p = {} + p["x"] = vx + p["y"] = vy + p["z"] = vyaw + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_MOVE, parameter) + return code + + def SwitchGait(self, gait_type: int) -> int: + p = {} + p["data"] = gait_type + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_SWITCHGAIT, parameter) + return code + + def BodyHeight(self, height: float) -> int: + p = {} + p["data"] = height + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_BODYHEIGHT, parameter) + return code + + def SpeedLevel(self, level: int) -> int: + p = {} + p["data"] = level + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_SPEEDLEVEL, parameter) + return code + + def BodyPosition(self, x: float, y: float, z: float, yaw: float) -> int: + p = {} + p["x"] = x + p["y"] = y + p["z"] = z + p["yaw"] = yaw + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_BODYPOSITION, parameter) + return code + + def LeftSideGait(self, enter: int) -> int: + p = {} + p["data"] = enter + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_LEFTSIDEGAIT, parameter) + return code + + def RightSideGait(self, enter: int) -> int: + p = {} + p["data"] = enter + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_RIGHTSIDEGAIT, parameter) + return code + + def HandStand(self, enter: int) -> int: + p = {} + p["data"] = enter + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_HANDSTAND, parameter) + return code + + def BipedStand(self, enter: int) -> int: + p = {} + p["data"] = enter + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_BIPEDSTAND, parameter) + return code + + def FrontFlip(self) -> int: + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_FRONTFLIP, parameter) + return code + + def BackFlip(self) -> int: + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_BACKFLIP, parameter) + return code + + def SetAutoRecovery(self, switch_on: int) -> int: + p = {} + p["data"] = switch_on + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_SETAUTORECOVERY, parameter) + return code + + def GetState(self, state_map: Dict[str, str]) -> int: + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_GETSTATE, parameter) + if data: + state_map.update(json.loads(data)) + return code + diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/b2/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/b2/back_video/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/back_video/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_api.py new file mode 100644 index 0000000..16f566b --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/back_video/back_video_client.py new file mode 100644 index 0000000..e19c97f --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/b2/front_video/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/front_video/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_api.py new file mode 100644 index 0000000..6f4717a --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/front_video/front_video_client.py new file mode 100644 index 0000000..5ba065a --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_api.py new file mode 100644 index 0000000..4a8f1ca --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_api.py @@ -0,0 +1,28 @@ +""" +" service name +""" +ROBOT_STATE_SERVICE_NAME = "robot_state" + + +""" +" service api version +""" +ROBOT_STATE_API_VERSION = "1.0.0.2" + + +""" +" api id +""" +ROBOT_STATE_API_ID_SERVICE_SWITCH = 1001 +ROBOT_STATE_API_ID_REPORT_FREQ = 1002 +ROBOT_STATE_API_ID_SERVICE_LIST = 1003 +ROBOT_STATE_API_ID_LOWPOWER_SWITCH = 1004 +ROBOT_STATE_API_ID_LOWPOWER_STATUS = 1005 +ROBOT_STATE_API_ID_GET_PKG_VERSION = 1006 + + +""" +" error code +""" +ROBOT_STATE_ERR_SERVICE_SWITCH = 5201 +ROBOT_STATE_ERR_SERVICE_PROTECTED = 5202 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_client.py new file mode 100644 index 0000000..0dc58b5 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/robot_state/robot_state_client.py @@ -0,0 +1,119 @@ +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) + self._RegistApi(ROBOT_STATE_API_ID_LOWPOWER_SWITCH, 0) + self._RegistApi(ROBOT_STATE_API_ID_LOWPOWER_STATUS, 0) + self._RegistApi(ROBOT_STATE_API_ID_GET_PKG_VERSION, 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 + + def LowPowerSwitch(self, swit: int): + p = {} + p["switch"] = swit + parameter = json.dumps(p) + + code, data = self._Call(ROBOT_STATE_API_ID_LOWPOWER_SWITCH, parameter) + return code + + def LowPowerStatus(self): + p = {} + parameter = json.dumps(p) + + code, data = self._Call(ROBOT_STATE_API_ID_LOWPOWER_STATUS, parameter) + + if code != 0: + return code, None + + d = json.loads(data) + return code, d["status"] + + def GetPkgVersion(self): + p = {} + parameter = json.dumps(p) + + code, data = self._Call(ROBOT_STATE_API_ID_GET_PKG_VERSION, parameter) + + if code != 0: + return code, None, None + + d = json.loads(data) + return code, d["packageVersion"], d["moduleVersionMap"] diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/b2/sport/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/sport/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_api.py new file mode 100644 index 0000000..7b25e7b --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_api.py @@ -0,0 +1,45 @@ +""" +" service name +""" +SPORT_SERVICE_NAME = "sport" + + +""" +" service api version +""" +SPORT_API_VERSION = "1.0.0.1" + + +""" +" api id +""" +ROBOT_SPORT_API_ID_DAMP = 1001 +ROBOT_SPORT_API_ID_BALANCESTAND = 1002 +ROBOT_SPORT_API_ID_STOPMOVE = 1003 +ROBOT_SPORT_API_ID_STANDUP = 1004 +ROBOT_SPORT_API_ID_STANDDOWN = 1005 +ROBOT_SPORT_API_ID_RECOVERYSTAND = 1006 +ROBOT_SPORT_API_ID_MOVE = 1008 +ROBOT_SPORT_API_ID_SWITCHGAIT = 1011 +ROBOT_SPORT_API_ID_BODYHEIGHT = 1013 +ROBOT_SPORT_API_ID_SPEEDLEVEL = 1015 +ROBOT_SPORT_API_ID_TRAJECTORYFOLLOW = 1018 +ROBOT_SPORT_API_ID_CONTINUOUSGAIT = 1019 +ROBOT_SPORT_API_ID_MOVETOPOS = 1036 +ROBOT_SPORT_API_ID_SWITCHMOVEMODE = 1038 +ROBOT_SPORT_API_ID_VISIONWALK = 1101 +ROBOT_SPORT_API_ID_HANDSTAND = 1039 +ROBOT_SPORT_API_ID_AUTORECOVERY_SET = 1040 +ROBOT_SPORT_API_ID_FREEWALK = 1045 +ROBOT_SPORT_API_ID_CLASSICWALK = 1049 +ROBOT_SPORT_API_ID_FASTWALK = 1050 +ROBOT_SPORT_API_ID_FREEEULER = 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/vendor/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_client.py new file mode 100644 index 0000000..1438640 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/sport/sport_client.py @@ -0,0 +1,219 @@ +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(ROBOT_SPORT_API_ID_DAMP, 0) + self._RegistApi(ROBOT_SPORT_API_ID_BALANCESTAND, 0) + self._RegistApi(ROBOT_SPORT_API_ID_STOPMOVE, 0) + self._RegistApi(ROBOT_SPORT_API_ID_STANDUP, 0) + self._RegistApi(ROBOT_SPORT_API_ID_STANDDOWN, 0) + self._RegistApi(ROBOT_SPORT_API_ID_RECOVERYSTAND, 0) + self._RegistApi(ROBOT_SPORT_API_ID_MOVE, 0) + self._RegistApi(ROBOT_SPORT_API_ID_SWITCHGAIT, 0) + self._RegistApi(ROBOT_SPORT_API_ID_BODYHEIGHT, 0) + self._RegistApi(ROBOT_SPORT_API_ID_SPEEDLEVEL, 0) + self._RegistApi(ROBOT_SPORT_API_ID_TRAJECTORYFOLLOW, 0) + self._RegistApi(ROBOT_SPORT_API_ID_CONTINUOUSGAIT, 0) + self._RegistApi(ROBOT_SPORT_API_ID_MOVETOPOS, 0) + self._RegistApi(ROBOT_SPORT_API_ID_SWITCHMOVEMODE, 0) + self._RegistApi(ROBOT_SPORT_API_ID_VISIONWALK, 0) + self._RegistApi(ROBOT_SPORT_API_ID_HANDSTAND, 0) + self._RegistApi(ROBOT_SPORT_API_ID_AUTORECOVERY_SET, 0) + self._RegistApi(ROBOT_SPORT_API_ID_FREEWALK, 0) + self._RegistApi(ROBOT_SPORT_API_ID_CLASSICWALK, 0) + self._RegistApi(ROBOT_SPORT_API_ID_FASTWALK, 0) + self._RegistApi(ROBOT_SPORT_API_ID_FREEEULER, 0) + + def Damp(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_DAMP, parameter) + return code + + def BalanceStand(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_BALANCESTAND, parameter) + return code + + def StopMove(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_STOPMOVE, parameter) + return code + + def StandUp(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_STANDUP, parameter) + return code + + def StandDown(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_STANDDOWN, parameter) + return code + + def RecoveryStand(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_RECOVERYSTAND, parameter) + return code + + 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(ROBOT_SPORT_API_ID_MOVE, parameter) + return code + + def SwitchGait(self, t: int): + p = {} + p["data"] = t + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_SWITCHGAIT, parameter) + return code + + def BodyHeight(self, height: float): + p = {} + p["data"] = height + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_BODYHEIGHT, parameter) + return code + + def SpeedLevel(self, level: int): + p = {} + p["data"] = level + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_SPEEDLEVEL, parameter) + return code + + 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(ROBOT_SPORT_API_ID_TRAJECTORYFOLLOW, parameter) + return code + + def ContinuousGait(self, flag: int): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_CONTINUOUSGAIT, parameter) + return code + + 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(ROBOT_SPORT_API_ID_MOVETOPOS, parameter) + return code + + def SwitchMoveMode(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_SWITCHMOVEMODE, parameter) + return code + + def VisionWalk(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_VISIONWALK, parameter) + return code + + def HandStand(self, flag: int): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_HANDSTAND, parameter) + return code + + def AutoRecoverySet(self, flag: int): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_AUTORECOVERY_SET, parameter) + return code + + def FreeWalk(self): + p = {} + p["data"] = True ## default + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_FREEWALK, parameter) + return code + + def ClassicWalk(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_CLASSICWALK, parameter) + return code + + def FastWalk(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_FASTWALK, parameter) + return code + + def FreeEuler(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(ROBOT_SPORT_API_ID_FREEEULER, parameter) + return code diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/b2/vui/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/vui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_api.py new file mode 100644 index 0000000..d5dcd34 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/b2/vui/vui_client.py new file mode 100644 index 0000000..234f285 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/comm/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/comm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_api.py new file mode 100644 index 0000000..ddb9a42 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/comm/motion_switcher/motion_switcher_client.py new file mode 100644 index 0000000..1ad411a --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/core/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/core/channel.py b/vendor/unitree_sdk2_python/unitree_sdk2py/core/channel.py new file mode 100644 index 0000000..64577f9 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/core/channel.py @@ -0,0 +1,301 @@ +import time +from typing import Any, Callable +import threading +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 + + __initialized = False + __init_lock = threading.Lock() + + def __init__(self): + super().__init__() + + def Init(self, id: int, networkInterface: str = None, qos: Qos = None): + if self.__class__.__initialized: + return True + + with self.__class__.__init_lock: + if self.__class__.__initialized: + return True + + config = None + # choose config + if networkInterface is None: + config = ChannelConfigAutoDetermine + else: + config = ChannelConfigHasInterface.replace('$__IF_NAME__$', networkInterface) + + try: + self.__class__.__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.__class__.__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.__class__.__qos = qos + self.__class__.__initialized = True + return True + + def CreateChannel(self, name: str, type: Any): + return Channel(self.__class__.__participant, name, type, self.__class__.__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: int = 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/vendor/unitree_sdk2_python/unitree_sdk2py/core/channel_config.py b/vendor/unitree_sdk2_python/unitree_sdk2py/core/channel_config.py new file mode 100644 index 0000000..19a67a4 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/core/channel_config.py @@ -0,0 +1,25 @@ +ChannelConfigHasInterface = ''' + + + + + + + + + config + /tmp/cdds.LOG + + + ''' + +ChannelConfigAutoDetermine = ''' + + + + + + + + + ''' diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/core/channel_name.py b/vendor/unitree_sdk2_python/unitree_sdk2py/core/channel_name.py new file mode 100644 index 0000000..722e408 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/g1/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/g1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/g1/arm/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/g1/arm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/g1/arm/g1_arm_action_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/g1/arm/g1_arm_action_api.py new file mode 100644 index 0000000..f3e9829 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/g1/arm/g1_arm_action_api.py @@ -0,0 +1,19 @@ +""" +" service name +""" +ARM_ACTION_SERVICE_NAME = "arm" + +""" +" service api version +""" +ARM_ACTION_API_VERSION = "1.0.0.14" + +""" +" api id +""" +ROBOT_API_ID_ARM_ACTION_EXECUTE_ACTION = 7106 +ROBOT_API_ID_ARM_ACTION_GET_ACTION_LIST = 7107 + +""" +" error code +""" \ No newline at end of file diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/g1/arm/g1_arm_action_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/g1/arm/g1_arm_action_client.py new file mode 100644 index 0000000..615d276 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/g1/arm/g1_arm_action_client.py @@ -0,0 +1,56 @@ +import json + +from ...rpc.client import Client +from .g1_arm_action_api import * + +action_map = { + "release arm": 99, + "two-hand kiss": 11, + "left kiss": 12, + "right kiss": 13, + "hands up": 15, + "clap": 17, + "high five": 18, + "hug": 19, + "heart": 20, + "right heart": 21, + "reject": 22, + "right hand up": 23, + "x-ray": 24, + "face wave": 25, + "high wave": 26, + "shake hand": 27, +} + + +""" +" class SportClient +""" +class G1ArmActionClient(Client): + def __init__(self): + super().__init__(ARM_ACTION_SERVICE_NAME, False) + + def Init(self): + # set api version + self._SetApiVerson(ARM_ACTION_API_VERSION) + + # regist api + self._RegistApi(ROBOT_API_ID_ARM_ACTION_EXECUTE_ACTION, 0) + self._RegistApi(ROBOT_API_ID_ARM_ACTION_GET_ACTION_LIST, 0) + + ## API Call ## + def ExecuteAction(self, action_id: int): + p = {} + p["data"] = action_id + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_ARM_ACTION_EXECUTE_ACTION, parameter) + return code + + def GetActionList(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_ARM_ACTION_GET_ACTION_LIST, parameter) + if code == 0: + return code, json.loads(data) + else: + return code, None \ No newline at end of file diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/g1/audio/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/g1/audio/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_api.py new file mode 100644 index 0000000..9aba780 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_client.py new file mode 100644 index 0000000..71d9e88 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/g1/audio/g1_audio_client.py @@ -0,0 +1,71 @@ +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 + 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 + + def PlayStream(self, app_name: str, stream_id: str, pcm_data: bytes): + param = json.dumps({"app_name": app_name, "stream_id": stream_id}) + pcm_list = list(pcm_data) + return self._CallRequestWithParamAndBin(ROBOT_API_ID_AUDIO_START_PLAY, param, pcm_list) + + def PlayStop(self, app_name: str): + parameter = json.dumps({"app_name": app_name}) + self._Call(ROBOT_API_ID_AUDIO_STOP_PLAY, parameter) + return 0 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/g1/loco/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/g1/loco/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_api.py new file mode 100644 index 0000000..2fdca7e --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_api.py @@ -0,0 +1,32 @@ +""" +" service name +""" +LOCO_SERVICE_NAME = "sport" + + +""" +" 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/vendor/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/g1/loco/g1_loco_client.py new file mode 100644 index 0000000..15d751d --- /dev/null +++ b/vendor/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(500) + + 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/vendor/unitree_sdk2_python/unitree_sdk2py/go2/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_api.py new file mode 100644 index 0000000..ad9dc06 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_client.py new file mode 100644 index 0000000..c75831b --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/obstacles_avoid/obstacles_avoid_client.py @@ -0,0 +1,80 @@ +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 + + def MoveToAbsolutePosition(self, vx: float, vy: float, vyaw: float): + p = {} + p["x"] = vx + p["y"] = vy + p["yaw"] = vyaw + p["mode"] = 2 + parameter = json.dumps(p) + code = self._CallNoReply(OBSTACLES_AVOID_API_ID_MOVE, parameter) + return code + + def MoveToIncrementPosition(self, vx: float, vy: float, vyaw: float): + p = {} + p["x"] = vx + p["y"] = vy + p["yaw"] = vyaw + p["mode"] = 1 + parameter = json.dumps(p) + code = self._CallNoReply(OBSTACLES_AVOID_API_ID_MOVE, parameter) + return code \ No newline at end of file diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_api.py new file mode 100644 index 0000000..fde54df --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/robot_state/robot_state_client.py new file mode 100644 index 0000000..097a891 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/go2/sport/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/sport/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_api.py new file mode 100644 index 0000000..2cc3cee --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_api.py @@ -0,0 +1,63 @@ +""" +" 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_SPEEDLEVEL = 1015 +SPORT_API_ID_HELLO = 1016 +SPORT_API_ID_STRETCH = 1017 +SPORT_API_ID_CONTENT = 1020 +SPORT_API_ID_DANCE1 = 1022 +SPORT_API_ID_DANCE2 = 1023 +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_HEART = 1036 +SPORT_API_ID_STATICWALK = 1061 +SPORT_API_ID_TROTRUN = 1062 +SPORT_API_ID_ECONOMICGAIT = 1063 +SPORT_API_ID_LEFTFLIP = 2041 +SPORT_API_ID_BACKFLIP = 2043 +SPORT_API_ID_HANDSTAND = 2044 +SPORT_API_ID_FREEWALK = 2045 +SPORT_API_ID_FREEBOUND = 2046 +SPORT_API_ID_FREEJUMP = 2047 +SPORT_API_ID_FREEAVOID = 2048 +SPORT_API_ID_CLASSICWALK = 2049 +SPORT_API_ID_WALKUPRIGHT = 2050 +SPORT_API_ID_CROSSSTEP = 2051 +SPORT_API_ID_AUTORECOVERY_SET = 2054 +SPORT_API_ID_AUTORECOVERY_GET = 2055 +SPORT_API_ID_SWITCHAVOIDMODE = 2058 + +""" +" 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/vendor/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_client.py new file mode 100644 index 0000000..2675ab2 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/sport/sport_client.py @@ -0,0 +1,363 @@ +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) # Damp + self._RegistApi(SPORT_API_ID_BALANCESTAND, 0) # BalanceStand + self._RegistApi(SPORT_API_ID_STOPMOVE, 0) # StopMove + self._RegistApi(SPORT_API_ID_STANDUP, 0) # StandUp + self._RegistApi(SPORT_API_ID_STANDDOWN, 0) # StandDown + self._RegistApi(SPORT_API_ID_RECOVERYSTAND, 0) # RecoveryStand + self._RegistApi(SPORT_API_ID_EULER, 0) # Euler + self._RegistApi(SPORT_API_ID_MOVE, 0) # Move + self._RegistApi(SPORT_API_ID_SIT, 0) # Sit + self._RegistApi(SPORT_API_ID_RISESIT, 0) # RiseSit + self._RegistApi(SPORT_API_ID_SPEEDLEVEL, 0) # SpeedLevel + self._RegistApi(SPORT_API_ID_HELLO, 0) # Hello + self._RegistApi(SPORT_API_ID_STRETCH, 0) # Stretch + self._RegistApi(SPORT_API_ID_CONTENT, 0) # Content + self._RegistApi(SPORT_API_ID_DANCE1, 0) # Dance1 + self._RegistApi(SPORT_API_ID_DANCE2, 0) # Dance2 + self._RegistApi(SPORT_API_ID_SWITCHJOYSTICK, 0) # SwitchJoystick + self._RegistApi(SPORT_API_ID_POSE, 0) # Pose + self._RegistApi(SPORT_API_ID_SCRAPE, 0) # Scrape + self._RegistApi(SPORT_API_ID_FRONTFLIP, 0) # FrontFlip + self._RegistApi(SPORT_API_ID_FRONTJUMP, 0) # FrontJump + self._RegistApi(SPORT_API_ID_FRONTPOUNCE, 0) # FrontPounce + self._RegistApi(SPORT_API_ID_HEART, 0) # Heart + self._RegistApi(SPORT_API_ID_STATICWALK, 0) # StaticWalk + self._RegistApi(SPORT_API_ID_TROTRUN, 0) # TrotRun + self._RegistApi(SPORT_API_ID_ECONOMICGAIT, 0) # EconomicGait + self._RegistApi(SPORT_API_ID_LEFTFLIP, 0) # LeftFlip + self._RegistApi(SPORT_API_ID_BACKFLIP, 0) # BackFlip + self._RegistApi(SPORT_API_ID_HANDSTAND, 0) # HandStand + self._RegistApi(SPORT_API_ID_FREEWALK, 0) # FreeWalk + self._RegistApi(SPORT_API_ID_FREEBOUND, 0) # FreeBound + self._RegistApi(SPORT_API_ID_FREEJUMP, 0) # FreeJump + self._RegistApi(SPORT_API_ID_FREEAVOID, 0) # FreeAvoid + self._RegistApi(SPORT_API_ID_CLASSICWALK, 0) # ClassicWalk + self._RegistApi(SPORT_API_ID_WALKUPRIGHT, 0) # WalkUpright + self._RegistApi(SPORT_API_ID_CROSSSTEP, 0) # CrossStep + self._RegistApi(SPORT_API_ID_AUTORECOVERY_SET, 0) # AutoRecoverySet + self._RegistApi(SPORT_API_ID_AUTORECOVERY_GET, 0) # AutoRecoveryGet + self._RegistApi(SPORT_API_ID_SWITCHAVOIDMODE, 0) # SwitchAvoidMode + + # 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 + + # 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 + + # 1020 + def Content(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_CONTENT, 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 + + # 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 + + # 1036 + def Heart(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_HEART, parameter) + return code + + # 2041 + def LeftFlip(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_LEFTFLIP, parameter) + return code + + # 2043 + def BackFlip(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_BACKFLIP, parameter) + return code + + # 2045 + def FreeWalk(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_FREEWALK, parameter) + return code + + # 2046 + def FreeBound(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_FREEBOUND, parameter) + return code + + # 2047 + def FreeJump(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_FREEJUMP, parameter) + return code + + # 2048 + def FreeAvoid(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_FREEAVOID, parameter) + return code + + # 2050 + def WalkUpright(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_WALKUPRIGHT, parameter) + return code + + # 2051 + def CrossStep(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_CROSSSTEP, parameter) + return code + + # 1061 + def StaticWalk(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_STATICWALK, parameter) + return code + + # 1062 + def TrotRun(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_TROTRUN, parameter) + return code + + # 2044 + def HandStand(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_HANDSTAND, parameter) + return code + # 2049 + def ClassicWalk(self, flag: bool): + p = {} + p["data"] = flag + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_CLASSICWALK, parameter) + return code + + # 2054 + def AutoRecoverySet(self, enabled: bool): + p = {} + p["data"] = enabled + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_AUTORECOVERY_SET, parameter) + return code + + # 2055 + def AutoRecoveryGet(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_AUTORECOVERY_GET, parameter) + if code == 0: + d = json.loads(data) + return code, d["data"] + else: + return code, None + + # 2058 + def SwitchAvoidMode(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(SPORT_API_ID_SWITCHAVOIDMODE, parameter) + return code diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/go2/video/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/video/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/go2/video/video_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/video/video_api.py new file mode 100644 index 0000000..a4cb1b6 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/go2/video/video_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/video/video_client.py new file mode 100644 index 0000000..79e1fb5 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/go2/vui/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/vui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_api.py new file mode 100644 index 0000000..d5dcd34 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/go2/vui/vui_client.py new file mode 100644 index 0000000..234f285 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/h1/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/h1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/h1/loco/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/h1/loco/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/h1/loco/h1_loco_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/h1/loco/h1_loco_api.py new file mode 100644 index 0000000..bd8ddbe --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/h1/loco/h1_loco_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/h1/loco/h1_loco_client.py new file mode 100644 index 0000000..3bc01e3 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/h2/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/h2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/h2/loco/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/h2/loco/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/h2/loco/h2_loco_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/h2/loco/h2_loco_api.py new file mode 100644 index 0000000..cdbdb85 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/h2/loco/h2_loco_api.py @@ -0,0 +1,40 @@ +""" +service name +""" +LOCO_SERVICE_NAME = "sport" + +""" +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_GET_ARM_SDK_STATUS = 7007 +ROBOT_API_ID_LOCO_GET_AVAILABLE_FSM_IDS = 7008 + +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 +ROBOT_API_ID_LOCO_SET_SPEED_MODE = 7107 +ROBOT_API_ID_LOCO_SET_PUNCH_API = 7108 +ROBOT_API_ID_LOCO_SET_ARM_SDK_STATUS = 7109 + +""" +error code +""" +LOCO_ERR_LOCOSTATE_NOT_AVAILABLE = 7301 +LOCO_ERR_SCHEDULER_STATE_NOT_AVAILABLE = 7301 +LOCO_ERR_INVALID_FSM_ID = 7302 +LOCO_ERR_INVALID_TASK_ID = 7303 + diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/h2/loco/h2_loco_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/h2/loco/h2_loco_client.py new file mode 100644 index 0000000..4e2a418 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/h2/loco/h2_loco_client.py @@ -0,0 +1,249 @@ +import json + +from ...rpc.client import Client +from .h2_loco_api import * + +""" +" class SportClient +""" +class LocoClient(Client): + def __init__(self): + super().__init__(LOCO_SERVICE_NAME, False) + self.continous_move_ = 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_GET_ARM_SDK_STATUS, 0) + self._RegistApi(ROBOT_API_ID_LOCO_GET_AVAILABLE_FSM_IDS, 0) + + 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) + self._RegistApi(ROBOT_API_ID_LOCO_SET_SPEED_MODE, 0) + self._RegistApi(ROBOT_API_ID_LOCO_SET_PUNCH_API, 0) + self._RegistApi(ROBOT_API_ID_LOCO_SET_ARM_SDK_STATUS, 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 + + # 7103 + def SetSwingHeight(self, swing_height: float): + p = {} + p["data"] = swing_height + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_SET_SWING_HEIGHT, 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 + + # 7107 + def SetSpeedMode(self, speed_mode: int): + p = {} + p["data"] = speed_mode + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_SET_SPEED_MODE, parameter) + return code + + # 7108 + def SetPunchApi(self, punch_api): + p = {} + p["data"] = punch_api + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_SET_PUNCH_API, parameter) + return code + + # 7109 + def SetArmSdkStatus(self, arm_sdk_status: bool): + p = {} + p["data"] = arm_sdk_status + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_SET_ARM_SDK_STATUS, parameter) + return code + + def Damp(self): + self.SetFsmId(1) + + def Start(self): + self.SetFsmId(500) + + def Squat(self): + self.SetFsmId(2) + + def Sit(self): + self.SetFsmId(3) + + def StandUp(self): + self.SetFsmId(4) + + def ZeroTorque(self): + self.SetFsmId(0) + + def StopMove(self): + self.SetVelocity(0.0, 0.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 = None): + if continous_move is None: + continous_move = self.continous_move_ + duration = 864000.0 if continous_move else 1 + self.SetVelocity(vx, vy, vyaw, duration) + + def BalanceStand(self): + self.SetBalanceMode(0) + + def ContinuousGait(self, flag: bool): + self.SetBalanceMode(1 if flag else 0) + + def SwitchMoveMode(self, flag: bool): + self.continous_move_ = flag + return 0 + + 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) + + def EnableArmSDK(self): + self.SetArmSdkStatus(True) + + def DisableArmSDK(self): + self.SetArmSdkStatus(False) + + def GetFsmId(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_GET_FSM_ID, parameter) + if code != 0: + return code, None + js = json.loads(data) + return code, js.get("data") + + def GetFsmMode(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_GET_FSM_MODE, parameter) + if code != 0: + return code, None + js = json.loads(data) + return code, js.get("data") + + def GetBalanceMode(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_GET_BALANCE_MODE, parameter) + if code != 0: + return code, None + js = json.loads(data) + return code, js.get("data") + + def GetSwingHeight(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_GET_SWING_HEIGHT, parameter) + if code != 0: + return code, None + js = json.loads(data) + return code, js.get("data") + + def GetStandHeight(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_GET_STAND_HEIGHT, parameter) + if code != 0: + return code, None + js = json.loads(data) + return code, js.get("data") + + def GetPhase(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_GET_PHASE, parameter) + if code != 0: + return code, None + js = json.loads(data) + return code, js.get("data") + + def GetArmSdkStatus(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_GET_ARM_SDK_STATUS, parameter) + if code != 0: + return code, None + js = json.loads(data) + return code, js.get("data") + + def GetAvailableFsmIds(self): + p = {} + parameter = json.dumps(p) + code, data = self._Call(ROBOT_API_ID_LOCO_GET_AVAILABLE_FSM_IDS, parameter) + if code != 0: + return code, None, None + js = json.loads(data) + return code, js.get("ids", []), js.get("names", []) + diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/idl/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/__init__.py new file mode 100644 index 0000000..38d706e --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/__init__.py new file mode 100644 index 0000000..c0397fd --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/__init__.py new file mode 100644 index 0000000..b791398 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/dds_/_Time_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/dds_/_Time_.py new file mode 100644 index 0000000..970c671 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/dds_/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/builtin_interfaces/msg/dds_/__init__.py new file mode 100644 index 0000000..c3f9730 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/default.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/default.py new file mode 100644 index 0000000..89d567d --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/default.py @@ -0,0 +1,268 @@ +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_ + +""" +" 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]) + + +""" +" 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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/__init__.py new file mode 100644 index 0000000..ab19108 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/__init__.py new file mode 100644 index 0000000..7d10dec --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Point32_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Point32_.py new file mode 100644 index 0000000..34eefd4 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PointStamped_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PointStamped_.py new file mode 100644 index 0000000..19de6ae --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Point_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Point_.py new file mode 100644 index 0000000..efbc562 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Pose2D_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Pose2D_.py new file mode 100644 index 0000000..c223370 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseStamped_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseStamped_.py new file mode 100644 index 0000000..7864bb0 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseWithCovarianceStamped_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseWithCovarianceStamped_.py new file mode 100644 index 0000000..843d4c8 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseWithCovariance_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_PoseWithCovariance_.py new file mode 100644 index 0000000..bd75301 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Pose_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Pose_.py new file mode 100644 index 0000000..2ea78d1 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_QuaternionStamped_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_QuaternionStamped_.py new file mode 100644 index 0000000..ae2b360 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Quaternion_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Quaternion_.py new file mode 100644 index 0000000..0a401fb --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistStamped_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistStamped_.py new file mode 100644 index 0000000..97aa7c9 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistWithCovarianceStamped_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistWithCovarianceStamped_.py new file mode 100644 index 0000000..d322b7f --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistWithCovariance_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_TwistWithCovariance_.py new file mode 100644 index 0000000..7281bfc --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Twist_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Twist_.py new file mode 100644 index 0000000..5cd3b23 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Vector3_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/_Vector3_.py new file mode 100644 index 0000000..9cdeb0f --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/geometry_msgs/msg/dds_/__init__.py new file mode 100644 index 0000000..68c4fe2 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/__init__.py new file mode 100644 index 0000000..a2724c2 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/__init__.py new file mode 100644 index 0000000..d47683b --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_MapMetaData_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_MapMetaData_.py new file mode 100644 index 0000000..9228cd6 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_OccupancyGrid_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_OccupancyGrid_.py new file mode 100644 index 0000000..0274c58 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_Odometry_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/_Odometry_.py new file mode 100644 index 0000000..054bb5c --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/nav_msgs/msg/dds_/__init__.py new file mode 100644 index 0000000..a14e17a --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/__init__.py new file mode 100644 index 0000000..984e7e2 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/__init__.py new file mode 100644 index 0000000..62a2292 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/PointField_Constants/_PointField_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/PointField_Constants/_PointField_.py new file mode 100644 index 0000000..673aa84 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/PointField_Constants/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/PointField_Constants/__init__.py new file mode 100644 index 0000000..310fecd --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/_PointCloud2_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/_PointCloud2_.py new file mode 100644 index 0000000..ffdf713 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/_PointField_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/_PointField_.py new file mode 100644 index 0000000..e62dbf9 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/sensor_msgs/msg/dds_/__init__.py new file mode 100644 index 0000000..68e0141 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/__init__.py new file mode 100644 index 0000000..c17a705 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/__init__.py new file mode 100644 index 0000000..02c8fe3 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/_Header_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/_Header_.py new file mode 100644 index 0000000..bd3564f --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/_String_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/_String_.py new file mode 100644 index 0000000..052f122 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/std_msgs/msg/dds_/__init__.py new file mode 100644 index 0000000..f5282e5 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/__init__.py new file mode 100644 index 0000000..e6546ff --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/__init__.py new file mode 100644 index 0000000..bbf9fde --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestHeader_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestHeader_.py new file mode 100644 index 0000000..57d67a0 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestIdentity_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestIdentity_.py new file mode 100644 index 0000000..a891b54 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestLease_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestLease_.py new file mode 100644 index 0000000..32cef0f --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestPolicy_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_RequestPolicy_.py new file mode 100644 index 0000000..aa7ef2f --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_Request_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_Request_.py new file mode 100644 index 0000000..39c6b43 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_ResponseHeader_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_ResponseHeader_.py new file mode 100644 index 0000000..ef47340 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_ResponseStatus_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_ResponseStatus_.py new file mode 100644 index 0000000..92c0b1b --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_Response_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/_Response_.py new file mode 100644 index 0000000..c743651 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_api/msg/dds_/__init__.py new file mode 100644 index 0000000..1e4c9f8 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/__init__.py new file mode 100644 index 0000000..b553006 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/__init__.py new file mode 100644 index 0000000..f8cae9b --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_AudioData_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_AudioData_.py new file mode 100644 index 0000000..27913c9 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_BmsCmd_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_BmsCmd_.py new file mode 100644 index 0000000..5da647e --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_BmsState_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_BmsState_.py new file mode 100644 index 0000000..af635e8 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Error_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Error_.py new file mode 100644 index 0000000..c338c69 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Go2FrontVideoData_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Go2FrontVideoData_.py new file mode 100644 index 0000000..04d245b --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_HeightMap_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_HeightMap_.py new file mode 100644 index 0000000..48168da --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_IMUState_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_IMUState_.py new file mode 100644 index 0000000..e48908f --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_InterfaceConfig_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_InterfaceConfig_.py new file mode 100644 index 0000000..5efbd64 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LidarState_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LidarState_.py new file mode 100644 index 0000000..6861068 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LowCmd_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LowCmd_.py new file mode 100644 index 0000000..bcd4781 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LowState_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_LowState_.py new file mode 100644 index 0000000..79ecbe6 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorCmd_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorCmd_.py new file mode 100644 index 0000000..5a2dd85 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorCmds_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorCmds_.py new file mode 100644 index 0000000..11d3029 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorState_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorState_.py new file mode 100644 index 0000000..0787acb --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorStates_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_MotorStates_.py new file mode 100644 index 0000000..4b0245b --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_PathPoint_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_PathPoint_.py new file mode 100644 index 0000000..20a8c55 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Req_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Req_.py new file mode 100644 index 0000000..5bb39b3 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Res_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_Res_.py new file mode 100644 index 0000000..684a5a7 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_SportModeState_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_SportModeState_.py new file mode 100644 index 0000000..e0a26a1 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_TimeSpec_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_TimeSpec_.py new file mode 100644 index 0000000..7a580b6 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_UwbState_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_UwbState_.py new file mode 100644 index 0000000..d5f2588 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_UwbSwitch_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_UwbSwitch_.py new file mode 100644 index 0000000..b902ef8 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_WirelessController_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/_WirelessController_.py new file mode 100644 index 0000000..18919dd --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_go/msg/dds_/__init__.py new file mode 100644 index 0000000..fc24f6f --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/.idlpy_manifest b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/.idlpy_manifest new file mode 100644 index 0000000..fd4ea94 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/__init__.py new file mode 100644 index 0000000..5f55ee5 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/.idlpy_manifest b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/.idlpy_manifest new file mode 100644 index 0000000..3218e96 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/__init__.py new file mode 100644 index 0000000..02b132d --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/.idlpy_manifest b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/.idlpy_manifest new file mode 100644 index 0000000..6174f37 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_BmsCmd_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_BmsCmd_.py new file mode 100644 index 0000000..525d1a4 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_BmsState_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_BmsState_.py new file mode 100644 index 0000000..dd0c6bd --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_HandCmd_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_HandCmd_.py new file mode 100644 index 0000000..044afcb --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_HandState_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_HandState_.py new file mode 100644 index 0000000..351dc75 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_IMUState_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_IMUState_.py new file mode 100644 index 0000000..75d01a6 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_LowCmd_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_LowCmd_.py new file mode 100644 index 0000000..56b4b8b --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_LowState_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_LowState_.py new file mode 100644 index 0000000..8e95ac7 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_LowState_.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: 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_' + 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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MainBoardState_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MainBoardState_.py new file mode 100644 index 0000000..c280245 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MotorCmd_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MotorCmd_.py new file mode 100644 index 0000000..3dd8f9d --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MotorState_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_MotorState_.py new file mode 100644 index 0000000..839fccf --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_PressSensorState_.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/_PressSensorState_.py new file mode 100644 index 0000000..7dcf794 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/__init__.py new file mode 100644 index 0000000..87b865f --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/idl/unitree_hg/msg/dds_/__init__.py @@ -0,0 +1,19 @@ +""" + 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 ._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_", "LowCmd_", "LowState_", "MainBoardState_", "MotorCmd_", "MotorState_", "PressSensorState_", ] diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/client.py new file mode 100644 index 0000000..646be04 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/client.py @@ -0,0 +1,111 @@ +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 _CallRequestWithParamAndBin(self, apiId: int, requestParamter: str, + requestBinary: list): + ret, proirity, leaseId = self.__CheckApi(apiId) + if ret == 0: + return self._CallRequestWithParamAndBinBase(apiId, requestParamter, + requestBinary, proirity, + leaseId) + else: + return RPC_ERR_CLIENT_API_NOT_REG, None + + def _CallRequestWithParamAndBinNoReply(self, apiId: int, requestParamter: str, + requestBinary: list): + ret, proirity, leaseId = self.__CheckApi(apiId) + if ret == 0: + return self._CallRequestWithParamAndBinNoReplyBase(apiId, + requestParamter, + requestBinary, + 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/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/client_base.py b/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/client_base.py new file mode 100644 index 0000000..fae1db9 --- /dev/null +++ b/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/client_base.py @@ -0,0 +1,128 @@ +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 _CallRequestWithParamAndBinBase(self, apiId: int, requestParamter: str, + requestBinary: list, proirity: int = 0, + leaseId: int = 0): + header = self.__SetHeader(apiId, leaseId, proirity, False) + request = Request(header, requestParamter, requestBinary) + + 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 _CallRequestWithParamAndBinNoReplyBase(self, apiId: int, requestParamter: str, + requestBinary: list, proirity: int, + leaseId: int): + header = self.__SetHeader(apiId, leaseId, proirity, True) + request = Request(header, requestParamter, request_binary) + + 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/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/client_stub.py b/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/client_stub.py new file mode 100644 index 0000000..67c2c8f --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/internal.py b/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/internal.py new file mode 100644 index 0000000..875a78b --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/lease_client.py b/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/lease_client.py new file mode 100644 index 0000000..37f600f --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/lease_server.py b/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/lease_server.py new file mode 100644 index 0000000..d5b27d1 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/request_future.py b/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/request_future.py new file mode 100644 index 0000000..037ab10 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/server.py b/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/server.py new file mode 100644 index 0000000..4e389a8 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/server_base.py b/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/server_base.py new file mode 100644 index 0000000..f010561 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/server_stub.py b/vendor/unitree_sdk2_python/unitree_sdk2py/rpc/server_stub.py new file mode 100644 index 0000000..e67be1f --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/client/obstacles_avoid_client_example.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/client/obstacles_avoid_client_example.py new file mode 100644 index 0000000..7936fe7 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/client/robot_service_client_example.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/client/robot_service_client_example.py new file mode 100644 index 0000000..01467c0 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/client/sport_client_example.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/client/sport_client_example.py new file mode 100644 index 0000000..22a8de0 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/client/video_client_example.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/client/video_client_example.py new file mode 100644 index 0000000..7eec949 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/client/vui_client_example.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/client/vui_client_example.py new file mode 100644 index 0000000..6df7c09 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/crc/test_crc.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/crc/test_crc.py new file mode 100644 index 0000000..ceb1b4b --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/helloworld/helloworld.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/helloworld/helloworld.py new file mode 100644 index 0000000..7e93803 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/helloworld/publisher.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/helloworld/publisher.py new file mode 100644 index 0000000..36e8fb8 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/helloworld/subscriber.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/helloworld/subscriber.py new file mode 100644 index 0000000..38e4763 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/lowlevel_control.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/lowlevel_control.py new file mode 100644 index 0000000..8e94fe1 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/read_lowstate.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/read_lowstate.py new file mode 100644 index 0000000..d3f20ca --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/sub_lowstate.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/sub_lowstate.py new file mode 100644 index 0000000..9a94619 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/unitree_go2_const.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/lowlevel/unitree_go2_const.py new file mode 100644 index 0000000..153a051 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_api.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_api.py new file mode 100644 index 0000000..68778ca --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_client_example.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_client_example.py new file mode 100644 index 0000000..35c6ad4 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_server_example.py b/vendor/unitree_sdk2_python/unitree_sdk2py/test/rpc/test_server_example.py new file mode 100644 index 0000000..a929e01 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/utils/__init__.py b/vendor/unitree_sdk2_python/unitree_sdk2py/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vendor/unitree_sdk2_python/unitree_sdk2py/utils/bqueue.py b/vendor/unitree_sdk2_python/unitree_sdk2py/utils/bqueue.py new file mode 100644 index 0000000..a612bfb --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/utils/clib_lookup.py b/vendor/unitree_sdk2_python/unitree_sdk2py/utils/clib_lookup.py new file mode 100644 index 0000000..ca7073d --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/utils/crc.py b/vendor/unitree_sdk2_python/unitree_sdk2py/utils/crc.py new file mode 100644 index 0000000..c250706 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/utils/future.py b/vendor/unitree_sdk2_python/unitree_sdk2py/utils/future.py new file mode 100644 index 0000000..f539087 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/utils/hz_sample.py b/vendor/unitree_sdk2_python/unitree_sdk2py/utils/hz_sample.py new file mode 100644 index 0000000..962fe3c --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/utils/joystick.py b/vendor/unitree_sdk2_python/unitree_sdk2py/utils/joystick.py new file mode 100644 index 0000000..aeda672 --- /dev/null +++ b/vendor/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/vendor/unitree_sdk2_python/unitree_sdk2py/utils/lib/crc_aarch64.so b/vendor/unitree_sdk2_python/unitree_sdk2py/utils/lib/crc_aarch64.so new file mode 100644 index 0000000000000000000000000000000000000000..878df74577e4fc4c90ff2d011c2344633c9798da GIT binary patch literal 7928 zcmeHMU2Ggz6+XMOaenMN8&eUJRAgQ9P?~1E2@cprqH8Diy2x>1vk@vnn#}&#Ua~*# z?5wbxh;9H?mEgf4mC^@jD}+QT70N?sE2vtxLMW&_M6^_Sp+r!GrAES01$0I2<(zxZ zSx74K}p`a}EokY=IEsyr<7=*5HNUM#|bSH@FWEW$*7~2W0vcAx5+C9+Y z&~=67e!8Fagpw|`FZ|ifQ2#glkbXL(v;c&P-51f0ADuOBO~Ncr@OYi_a+Tv3%B*^} z+tG)CSA6;;Jh=YcPf!2#%>zefGDni%+R?T5b5klD%HLMJ$W8$vP(u?&t@|InZ|Yv> zyMZ3U@}M3y1NtBwmWZ@q0!hT9Bi(yC;1o%-Ur)ZlIO=28_Sw4l!{i_ zwH?>8z`8$q+{$E~>};;=W}W2mqxn)Xo3vB;td_NxSm_1Zn#mRIeD2{aq@DDq4qNGx zlf`vQ5n;YL4>2Bp*oVK*a>La3+QMqQ76Z6ktJimvf3ijV5}bnb=a3E{y$|5sWq>5|O9MCJ zH5>I;jli>jz>x_wK+4qZi2j8)!ea60fC{eXwD_cIIXE zobPo*?E;Q@Dis@v1>gw!U4nPwHKdo-7~5jC*X#9**YJu!HE}8hBky&`YHMd+Q@ME? zpMm0EG2q$17=gP73>f;;^WRK#LG`x))xJ+W@w7woEsHZR21C_E04^nnf5(8kVPW5i zrvM%bf${n3rRttnAn;Y{51b90sD`V1#zOGWtLydTuR?IX4qc~L*6K+;AJsmhGFLKB zfxX6OqX0nb;-&R^eG=&nq+daLPrY8BN9rPd9O+r4&mj%tMJ@1f0zwNP5A5yPr!e~5 z=h2q@Q6oKiXCFf>hLlrl)ww?6cr-u&abH&QS!P#PEaAeRl z`L9)&cHEp+_nHe(;8;5+BTd>k}%vXl}8L+HO z$(qfVQg+_TxFx4-*%b_=RG80a-E77jIW#=dUWpC>bC&Hm_M(+7y3QiZICddxWh#Zj zBAR$O3pL#)RXj7qpe$=5F?xL58lOH!hmpq~oE|-X|51eTAW}dpQ!ZI^b}>UolH>=) zc$906PqOjku>`1Cz=}^jFg7}6JuoqGVmxUjN5`hd(QU`mN_G0#{)F>Z=c3I=s9=^C z3$C3)>N=XvNv~LPvnI~JnX2USnS;3uC~nR!&w-g)ETW~Rt^=l%&D#WWZ$9sWsh-z# zvkTa#bc30lBnLN1L1m(T~^OyxLZyEd4qav4mV>jKW7=WY4$ zU8-ZL7L@w2)9(o~g74n~m?-!=)`ns)^{vqTY$$c3)S2RM7&}zkioMiJLYG;Q#w8zO zFLh~*aj}=WO6Vz+dmDZZn%X3C0XwV&q!giiSqCh;pLc$@Rt zOC2dx#u0sg{6)5x{CE0Ys*^pPy;6u`-$*1UbV&@E`s05TaXR3Y`IY^h{BGsf7hdRh z(RQcDUh3Rob!xmZ->9OR*a>|SQ3_SYm;dAPyI_^`FL@I?$?K2MmQ=)E{_ieRP}uI$ zq;VH}p>%fcwU_#O4jQFYiC?lO=YKIYh|-+dpL>&8k?rs|bA)+SwoA N0ekkh+n0u4X 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 literal 0 HcmV?d00001 diff --git a/web/__pycache__/hand_web.cpython-313.pyc b/web/__pycache__/hand_web.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..02c7c35672b04107e63bafd7cc5fa5fa08c3b76f GIT binary patch literal 106698 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

7|BxYS44O&)1IHS*= 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 literal 0 HcmV?d00001 diff --git a/web/hand_web.py b/web/hand_web.py new file mode 100644 index 0000000..837abbc --- /dev/null +++ b/web/hand_web.py @@ -0,0 +1,1648 @@ +#!/usr/bin/env python3 +""" +Inspire hand web dashboard: sliders + arm/hand combos + live status. + - hand poses -> C++ hand_bridge (127.0.0.1:7799) -> rt/inspire/cmd + - arm actions -> build/arm_action (Unitree G1ArmActionClient) + - status/restart/iface controls +Needs inspire_g1 + hand_bridge (./manage.sh dashboard starts both). +""" +import os, socket, json, argparse, subprocess, glob, re, time, pty, signal, threading, queue, shlex +from flask import Flask, request, jsonify, Response + +REPO = os.environ.get("HAND_REPO") or os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +BUILD = os.path.join(REPO, "build") +BRIDGE_HOST, BRIDGE_PORT = "127.0.0.1", 7799 +# Saved shapes/combos live in HAND_DATA, which run.sh bind-mounts from the host. They used +# to sit inside the image at /opt/hand, so every `docker rm -f` in run.sh silently destroyed +# the whole library. +DATA_HOME = os.environ.get("HAND_DATA") or REPO +os.makedirs(DATA_HOME, exist_ok=True) +POSES_FILE = os.path.join(DATA_HOME, "hand_poses.txt") +COMBOS_FILE = os.path.join(DATA_HOME, "hand_combos.json") +IFACE_FILE = os.path.join(DATA_HOME, ".dashboard_iface") +PORT = 8088 # set from --port; the recorder pulls shapes back off this port + +# Record/replay is driven by g1_record_replay.py as a CHILD PROCESS rather than +# reimplemented here. That script is ~1000 lines of arm gravity feed-forward, touch +# thresholding and closure remapping, and a second copy inside the dashboard would drift +# from the CLI the first time either one changed. RECORDER_DIR overrides the search. +REC_SCRIPT = "g1_record_replay.py" +# recorder/ inside this project comes first: the recorder ships WITH the dashboard so the +# two can never fall out of step, and a checkout is self-contained. The outside paths stay +# as fallbacks for a host that still keeps its own working copy. +REC_DIR = os.path.abspath(os.environ.get("RECORDER_DIR") or next( + (p for p in (os.path.join(REPO, "recorder"), + os.path.join(REPO, "..", "Manual_Recorder"), + os.path.expanduser("~/Manual_Recorder"), + os.path.expanduser("~/Robotics_workspace/yslootahtech/G1/G1_Lootah/" + "Manual_Recorder")) + if os.path.isfile(os.path.join(p, REC_SCRIPT))), "")) +# The recorder needs unitree_sdk2py, which on a workstation lives in the g1_env conda env — +# point RECORDER_PY at that interpreter if the default python3 cannot import it. +REC_PY = os.environ.get("RECORDER_PY") or "python3" +TAKES_DIR = os.path.join(REC_DIR, "DataG1") if REC_DIR else "" +JOINTS = ["pinky", "ring", "middle", "index", "thumb_bend", "thumb_rot"] +ARM_ACTIONS = {"shake hand": 27, "right hand up": 23, "hands up": 15, "high five": 18, + "hug": 19, "high wave": 26, "face wave": 25, "clap": 17, "heart": 20, + "reject": 22, "release arm": 99} +app = Flask(__name__) +app.config["MAX_CONTENT_LENGTH"] = 256 * 1024 * 1024 # take uploads; a 60Hz minute is ~4MB + + +def get_iface(): + try: + v = open(IFACE_FILE).read().strip() + return v or "eth0" + except Exception: + return os.environ.get("IFACE", "eth0") + + +def sh(args, timeout=20): + try: + r = subprocess.run(args, capture_output=True, timeout=timeout) + return r.returncode, (r.stdout + r.stderr).decode(errors="ignore") + except Exception as e: + return 1, str(e) + + +def running(name): + return sh(["pgrep", "-x", name], timeout=5)[0] == 0 + + +def iface_status(name): + _, out = sh(["ip", "-brief", "addr", "show", name], timeout=4) + m = re.search(r"(\d+\.\d+\.\d+\.\d+)", out) + return {"up": "UP" in out, "ip": m.group(1) if m else None} + + +def detected_ports(): + """Which tty did inspire_g1 open for each hand, per its startup log. + + Handles BOTH log formats. The old one put them on one line: + Inspire hands detected: right=/dev/ttyUSB1 left=/dev/ttyUSB0 + the current one splits them and adds the USB path, which the old single-line regex + could not match — so the dashboard showed a red "no ports" on a perfectly healthy + service: + Inspire hands identified by usb path order: + RIGHT = /dev/ttyUSB0 (usb 1-2.2.3, rs485 id 1) + LEFT = /dev/ttyUSB1 (usb 1-2.2.1.1, rs485 id 1) + """ + try: + txt = open("/tmp/inspire_g1.log").read() + except Exception: + return [] + m = re.search(r"right=(\S+)\s+left=(\S+)", txt) # old single-line form + if m: + return [m.group(1), m.group(2)] + r = re.findall(r"^\s*RIGHT\s*=\s*(\S+)", txt, re.M) # current two-line form + l = re.findall(r"^\s*LEFT\s*=\s*(\S+)", txt, re.M) + if r and l: + return [r[-1], l[-1]] + return [] + + +def send_pose(vals): + if len(vals) != 12: + return False, "need 12 values" + line = " ".join("%.3f" % float(v) for v in vals) + "\n" + try: + s = socket.create_connection((BRIDGE_HOST, BRIDGE_PORT), timeout=2) + s.sendall(line.encode()); r = s.recv(16); s.close() + return (b"ok" in r), r.decode(errors="ignore").strip() + except Exception as e: + return False, "bridge down: %s" % e + + +def read_state(want_force=False): + """Ask the bridge for state. 'S' returns 24 values (12 angles + 12 forces); 'R' returns + the legacy 12 angles. Falls back automatically so an un-rebuilt bridge still works.""" + def ask(cmd, n): + try: + s = socket.create_connection((BRIDGE_HOST, BRIDGE_PORT), timeout=2) + s.sendall(cmd); r = s.recv(512); s.close() + vals = [float(x) for x in r.decode().split()] + return vals if len(vals) == n else None + except Exception: + return None + if want_force: + v = ask(b"S\n", 24) + if v: + return v[:12], v[12:] + v = ask(b"R\n", 12) + return (v, None) if v else (None, None) + v = ask(b"S\n", 24) + if v: + return v[:12] + return ask(b"R\n", 12) + + +def run_arm(aid, timeout=15): + rc, out = sh([os.path.join(BUILD, "arm_action"), str(int(aid)), get_iface()], timeout=timeout) + return rc == 0, out[-200:] + + +def load_poses(): + m = {} + if os.path.exists(POSES_FILE): + for line in open(POSES_FILE): + p = line.split() + if len(p) == 13 and not line.startswith("#"): + try: m[p[0]] = [float(x) for x in p[1:]] + except ValueError: pass + return m + + +def write_poses(m): + with open(POSES_FILE, "w") as f: + f.write("# name \n" % " ".join(JOINTS)) + for k in sorted(m): + f.write(k + " " + " ".join("%.3f" % x for x in m[k]) + "\n") + + +def save_pose(name, vals): + m = load_poses(); m[name] = [float(v) for v in vals]; write_poses(m) + + +def load_combos(): + try: return json.load(open(COMBOS_FILE)) + except Exception: return {} + + +def write_combos(m): + json.dump(m, open(COMBOS_FILE, "w"), indent=2) + + +@app.route("/") +def index(): + return Response(HTML, mimetype="text/html") + + +@app.route("/api/status") +def api_status(): + ports, live = detected_ports(), sorted(glob.glob("/dev/ttyUSB*")) + mismatch = bool(ports) and any(p not in live for p in ports) + return jsonify(service=running("inspire_g1"), bridge=running("hand_bridge"), + ports=ports, live=live, mismatch=mismatch, iface=get_iface(), + eth0=iface_status("eth0"), wlan0=iface_status("wlan0")) + + +@app.route("/api/restart", methods=["POST"]) +def api_restart(): + env = dict(os.environ); env["IFACE"] = get_iface() + subprocess.run([os.path.join(REPO, "manage.sh"), "restart"], capture_output=True, timeout=25, env=env) + return jsonify(ok=running("inspire_g1")) + + +@app.route("/api/iface", methods=["POST"]) +def api_iface(): + name = request.get_json(force=True).get("iface", "eth0") + if name not in ("eth0", "wlan0"): + return jsonify(ok=False, msg="eth0 or wlan0") + open(IFACE_FILE, "w").write(name) + sh(["pkill", "-x", "hand_bridge"], timeout=5); time.sleep(1) + subprocess.Popen(["setsid", os.path.join(BUILD, "hand_bridge"), name, str(BRIDGE_PORT)], + stdout=open("/tmp/hand_bridge.log", "w"), stderr=subprocess.STDOUT, + stdin=subprocess.DEVNULL) + time.sleep(1) + return jsonify(ok=True, iface=name) + + +@app.route("/api/apply", methods=["POST"]) +def api_apply(): + ok, msg = send_pose(request.get_json(force=True).get("q", [])) + return jsonify(ok=ok, msg=msg) + + +@app.route("/api/release", methods=["POST"]) +def api_release(): + # NOT a release, despite the name. q<0 sends 0xFFFF, which the RH56 reads as "no change": + # the joint keeps its previous target and stays driven. There is no motor-release command, + # and the worm drive is non-backdrivable, so a finger can never be posed by hand. The UI + # button that promised this has been removed; the route stays so old callers don't 404. + ok, msg = send_pose([-1.0] * 12) + return jsonify(ok=ok, msg=msg, note="no-op: RH56 cannot release a finger") + + +ERR_BITS = [(0x01, "LOCKED-ROTOR"), (0x02, "OVER-TEMP"), (0x04, "OVER-CURRENT"), + (0x08, "ABNORMAL"), (0x10, "COMMS")] +STATUS_TXT = {0: "unclenching", 1: "grasping", 2: "reached pos", 3: "reached force", + 5: "current prot", 6: "locked rotor", 7: "fault"} + + +def read_diag(): + """Full per-finger diagnostics from the bridge 'D' command: 8 blocks of 12 + publish rate.""" + try: + s = socket.create_connection((BRIDGE_HOST, BRIDGE_PORT), timeout=2) + s.sendall(b"D\n") + buf = b"" + while len(buf) < 4096 and b"\n" not in buf: + c = s.recv(4096) + if not c: + break + buf += c + s.close() + v = [float(x) for x in buf.decode().split()] + if len(v) < 97: + return None + b = [v[i * 12:(i + 1) * 12] for i in range(8)] + return dict(q=b[0], f=b[1], cur=b[2], fset=b[3], temp=b[4], + err=[int(x) for x in b[5]], sta=[int(x) for x in b[6]], + lost=[int(x) for x in b[7]], rate=v[96]) + except Exception: + return None + + +@app.route("/api/diag") +def api_diag(): + """Everything the tracker needs in one call: angle, force, current, force-limit, temp, + ERROR bits, STATUS, lost counters and the measured publish rate.""" + d = read_diag() + if not d: + q, f = read_state(want_force=True) + return jsonify(ok=q is not None, q=q, f=f, full=False) + d["err_txt"] = [" ".join(n for b, n in ERR_BITS if int(e) & b) or "" for e in d["err"]] + d["sta_txt"] = [STATUS_TXT.get(int(x), str(int(x))) for x in d["sta"]] + d["ok"] = True + d["full"] = True + return jsonify(d) + + +@app.route("/api/follow", methods=["POST"]) +def api_follow(): + """Force-follow (admittance): press a finger and it moves under your hand. + + The RH56 cannot be back-driven, so a press alone moves it 0.000. This reads the press and + drives the motor that way, which makes the ANGLE genuinely change — so what gets recorded + is a real measured angle instead of a grams-to-degrees guess. It also removes the + calibration problem: presses vary 5x between takes (41-110g one run, 48-527g the next), and + no fixed scale survives that. Watching the finger move and stopping when it looks right is + a closed loop with you in it. + """ + d = request.get_json(force=True) + on = 1 if d.get("on") else 0 + return jsonify(**set_follow(on, d.get("gain", 0.0006), d.get("deadband", 60), + d.get("maxrate", 0.35), d.get("force", 400))) + + +FOLLOW_ON = False # tracked so a take can disarm it — both publish rt/inspire/cmd + + +def set_follow(on, gain=0.0006, deadband=60, maxrate=0.35, force=400): + global FOLLOW_ON + line = "F %d %s %s %s %s\n" % (1 if on else 0, gain, deadband, maxrate, force) + try: + s_ = socket.create_connection((BRIDGE_HOST, BRIDGE_PORT), timeout=2) + s_.sendall(line.encode()); r = s_.recv(64); s_.close() + FOLLOW_ON = bool(on) + return {"ok": True, "on": bool(on), "msg": r.decode(errors="ignore").strip()} + except Exception as e: + return {"ok": False, "msg": "bridge down: %s" % e} + + +@app.route("/api/track") +def api_track(): + """Angle AND force in one call, for the live joint tracker. + + Both are needed together: a finger you press shows force but NOT angle, because the RH56 + drive is non-backdrivable. Seeing the two columns side by side is what makes that obvious + instead of looking like a broken sensor. + """ + q, f = read_state(want_force=True) + return jsonify(ok=q is not None, q=q, f=f, has_force=f is not None) + + +@app.route("/api/capture") +def api_capture(): # read the current (hand-set) joint positions + v = read_state() + # Per-hand liveness. hand_bridge caches the last DDS state with no timestamp, so if + # inspire_g1 dies or one RS-485 link drops, it keeps serving the last values forever and + # the dashboard's "reads live" lamp stays green over a completely dead hand. A hand whose + # six joints are ALL exactly 0.000 is not a real pose — it is an unread bus. + live = [True, True] + if v: + for h, lo in ((0, 0), (1, 6)): + live[h] = any(abs(x) > 1e-9 for x in v[lo:lo + 6]) + return jsonify(ok=v is not None, q=v, live_right=live[0], live_left=live[1]) + + +@app.route("/api/selftest", methods=["POST"]) +def api_selftest(): # close -> read -> open -> read; report movement + read health + send_pose([0.0] * 12); time.sleep(2.0) + closed = read_state() or [0.0] * 12 + send_pose([1.0] * 12); time.sleep(2.0) + opened = read_state() or [0.0] * 12 + moved = any(abs(closed[i] - opened[i]) > 0.05 for i in range(12)) + reads_ok = any(abs(v) > 0.001 for v in closed + opened) + return jsonify(closed=closed, opened=opened, moved=moved, reads_ok=reads_ok) + + +@app.route("/api/armlist") +def api_armlist(): + return jsonify(ARM_ACTIONS) + + +@app.route("/api/arm", methods=["POST"]) +def api_arm(): + ok, out = run_arm(request.get_json(force=True).get("id")) + return jsonify(ok=ok, msg=out) + + +@app.route("/api/combo/play", methods=["POST"]) +def api_combo_play(): + d = request.get_json(force=True) + q, arm, release = d.get("q"), d.get("arm"), d.get("release", False) + if q: send_pose(q) + if arm not in (None, "", -1): + ok, msg = run_arm(arm) + if q: send_pose(q) + if release: run_arm(99) + return jsonify(ok=ok, msg=msg) + return jsonify(ok=True, msg="pose applied") + + +@app.route("/api/poses") +def api_poses(): + return jsonify(load_poses()) + + +@app.route("/api/save", methods=["POST"]) +def api_save(): + d = request.get_json(force=True) + name, q = d.get("name", "").strip(), d.get("q", []) + if not name or len(q) != 12: + return jsonify(ok=False, msg="name + 12 values") + save_pose(name, q); return jsonify(ok=True) + + +@app.route("/api/combos", methods=["GET", "POST"]) +def api_combos(): + if request.method == "GET": + return jsonify(load_combos()) + d = request.get_json(force=True); name = d.get("name", "").strip() + if not name: return jsonify(ok=False, msg="name required") + m = load_combos() + m[name] = {"q": d.get("q", []), "arm": d.get("arm"), "release": bool(d.get("release", True))} + write_combos(m) + return jsonify(ok=True) + + +@app.route("/api/pose/delete", methods=["POST"]) +def api_pose_delete(): + name = request.get_json(force=True).get("name", "") + m = load_poses() + if name in m: + del m[name]; write_poses(m); return jsonify(ok=True) + return jsonify(ok=False, msg="not found") + + +@app.route("/api/pose/rename", methods=["POST"]) +def api_pose_rename(): + d = request.get_json(force=True) + old, new = d.get("old", ""), d.get("new", "").strip() + m = load_poses() + if old in m and new and new not in m: + m[new] = m.pop(old); write_poses(m); return jsonify(ok=True) + return jsonify(ok=False, msg="bad/duplicate name") + + +@app.route("/api/combo/delete", methods=["POST"]) +def api_combo_delete(): + name = request.get_json(force=True).get("name", "") + m = load_combos() + if name in m: + del m[name]; write_combos(m); return jsonify(ok=True) + return jsonify(ok=False, msg="not found") + + +@app.route("/api/combo/rename", methods=["POST"]) +def api_combo_rename(): + d = request.get_json(force=True) + old, new = d.get("old", ""), d.get("new", "").strip() + m = load_combos() + if old in m and new and new not in m: + m[new] = m.pop(old); write_combos(m); return jsonify(ok=True) + return jsonify(ok=False, msg="bad/duplicate name") + + +# ---- record / replay ------------------------------------------------------------------ +ANSI = re.compile(r"\x1b\[[0-9;?]*[A-Za-z]") +# The recorder asks these two on stdin at the end of a take, and both are answered by the +# panel rather than by a human at a terminal. +PROMPT_PREVIEW = "Preview this recording" +PROMPT_SAVE = "Save recording?" +# Goes on a child process argv (never through a shell), so the only job here is to keep the +# name inside DataG1/. Parentheses are allowed because the recorder ITSELF names collisions +# that way — unique_path() writes grip1(1).jsonl — and without them those takes could not be +# listed, renamed, replayed or deleted from the panel. +SAFE_NAME = re.compile(r"^[A-Za-z0-9._()-]{1,64}$") +TAKE = None +TAKE_LOCK = threading.Lock() + + +class Take: + """One recorder run, driven over a pty. + + A pty and not a pipe: the recorder puts stdin in cbreak mode (tty.setcbreak) to read the + in-take keys, which raises on a pipe and would kill the take before the arms ever went + limp. The pty is also what lets this panel send those same keys — 1-9 shapes, o/c/[/]/;/' + — which is the only way to get a finger pose into a recording, since the RH56 cannot be + posed by hand. + """ + MAXLINES = 300 + KEY_GAP = 0.15 # see _writer + + def __init__(self, argv, action, seconds): + self.argv, self.action, self.seconds = argv, action, seconds + # The console opens with the command itself, so the log doubles as a receipt of + # exactly what ran — the same line you could paste into a terminal. + self.lines, self.cur, self.pending = ["$ " + cmd_str(argv)], "", "" + self._cr = False # a CR held over, waiting to see if an LF follows + self._tail = "" # rolling raw text, for the markers below + self.replaying = False # inside replay() — pause only applies there + self.paused = False + self.phase = "running" + self.rc = None + self.t0 = time.time() + self.lock = threading.Lock() + self.mfd, sfd = pty.openpty() + env = dict(os.environ, PYTHONUNBUFFERED="1", TERM="dumb") + # start_new_session: stop() signals the whole group, so the SIGINT reaches the + # recorder even once it has spawned DDS threads of its own. + self.proc = subprocess.Popen(argv, cwd=REC_DIR, stdin=sfd, stdout=sfd, stderr=sfd, + start_new_session=True, close_fds=True, env=env) + os.close(sfd) + self.keyq = queue.Queue(maxsize=64) + threading.Thread(target=self._pump, daemon=True).start() + threading.Thread(target=self._writer, daemon=True).start() + + def _pump(self): + while True: + try: + data = os.read(self.mfd, 4096) + except OSError: # master closes when the child exits + break + if not data: + break + self._feed(data.decode("utf-8", "replace")) + self.rc = self.proc.wait() + with self.lock: + if self.cur: + self._push() + self.phase = "done" + self.keyq.put(None) + try: + os.close(self.mfd) + except OSError: + pass + + def _writer(self): + """Send keys ONE AT A TIME, ~KEY_GAP apart. + + The recorder reads keys with a select() followed by sys.stdin.read(1), and Python's + stdin is buffered: a burst all lands in that buffer at once, select() then reports + nothing ready, and every key after the first is stranded there until the next one + arrives. Measured against the real read loop: three keys 10ms apart delivered two, + the same three 200ms apart delivered all three. Clicking two shape buttons quickly + must not silently drop one, so the pacing lives here rather than in the browser. + """ + while True: + item = self.keyq.get() + if item is None: + return + if not self.alive(): + continue + try: + os.write(self.mfd, item) + except OSError: + return + time.sleep(self.KEY_GAP) + + def _push(self): + self.lines.append(self.cur) + self.cur = "" + if len(self.lines) > self.MAXLINES: + del self.lines[:len(self.lines) - self.MAXLINES] + + def _feed(self, txt): + with self.lock: + self.pending += txt + # A pty turns every \n into \r\n on the way out, so a CR is only a line-redraw + # (the recorder's \r progress counters) when no LF follows it. Treating them all + # as redraws wiped the content of every finished line. + for ch in ANSI.sub("", txt): + if self._cr: + self._cr = False + if ch == "\n": + self._push() + continue + self.cur = "" # bare CR: the line redraws itself in place + if ch == "\r": + self._cr = True + elif ch == "\n": + self._push() + else: + self.cur += ch + # Both prompts arrive WITHOUT a trailing newline, so they never reach self.lines + # — they have to be spotted in the raw stream. pending is cleared on every + # answer so the same prompt is not re-detected after it has been dealt with. + if PROMPT_SAVE in self.pending: + self.phase = "save?" + elif PROMPT_PREVIEW in self.pending: + self.phase = "preview?" + # Pause/resume state is READ BACK from the recorder rather than assumed from the + # click, so the button cannot drift out of step with the process — and it also + # tracks a pause typed at the terminal. A rolling tail is used because a marker + # can land across two reads. + self._tail = (self._tail + txt)[-400:] + if "REPLAY" in self._tail: + self.replaying, self.paused = True, False + self._tail = "" + elif "replay done" in self._tail or "replay stopped" in self._tail: + self.replaying, self.paused = False, False + self._tail = "" + elif "PAUSED" in self._tail: + self.paused = True + self._tail = "" + elif "resumed" in self._tail: + self.paused = False + self._tail = "" + + def alive(self): + return self.proc.poll() is None + + def snapshot(self, n=20): + with self.lock: + tail = [l for l in self.lines[-n:]] + if self.cur: + tail.append(self.cur) + return {"phase": self.phase, "lines": tail[-n:], "rc": self.rc, + "action": self.action, "seconds": self.seconds, + "replaying": self.replaying, "paused": self.paused, + "elapsed": round(time.time() - self.t0, 1)} + + def key(self, k): + """Relay one in-take key. Never a newline: ask() at the end of a take counts a bare + Enter as YES, so a stray one would preview (arm moves) or save a discarded take.""" + k = k.replace("\r", "").replace("\n", "")[:1] + if not k or not self.alive(): + return False + try: + self.keyq.put_nowait(k.encode()) + except queue.Full: + return False + return True + + def answer(self, yes): + if self.phase not in ("preview?", "save?") or not self.alive(): + return False + with self.lock: + self.pending = "" + self.phase = "running" + # Through the same queue as the keys, so an answer can never overtake a key that is + # still waiting its turn. + self.keyq.put(b"y\n" if yes else b"n\n") + return True + + def stop(self, hard=False): + """SIGINT, not SIGKILL. The recorder catches KeyboardInterrupt, walks the arm home + and releases it; killing it outright abandons the arm limp, mid-air.""" + if not self.alive(): + return + sig = signal.SIGKILL if hard else signal.SIGINT + try: + os.killpg(os.getpgid(self.proc.pid), sig) + except Exception: + self.proc.send_signal(sig) + + +def rec_ready(): + return bool(REC_DIR) and os.path.isfile(os.path.join(REC_DIR, REC_SCRIPT)) + + +def sigint_ok(): + """Can Stop actually interrupt a take? + + A process started in the background by a non-interactive shell inherits SIGINT as + SIG_IGN, and unlike a handler, SIG_IGN SURVIVES EXEC — so the recorder would inherit it + and Stop would do nothing at all. manage.sh starts this dashboard exactly that way + (setsid ... &), so it is the normal case, not the odd one. fix_sigint() below repairs it; + this reports whether the repair held, and the panel says so if it did not. + """ + return signal.getsignal(signal.SIGINT) != signal.SIG_IGN + + +def fix_sigint(): + """Swap an inherited SIG_IGN for a no-op HANDLER (main thread only). + + A handler is reset to SIG_DFL in the child on exec, which is what makes Stop work, while + the dashboard itself stays as un-killable by SIGINT as it was before. + """ + if not sigint_ok(): + signal.signal(signal.SIGINT, lambda *_a: None) + + +# DataG1/ also holds the arm home pose, which every take returns through on its way out +# (--home). It is not a recording: listing it invites a replay, and deleting it would break +# the return-to-home of every take there is. +NOT_A_TAKE = ("arm_home",) + + +def take_path(name): + """Resolve a take name inside DataG1/, or None. Containment-checked: the name reaches + here from a browser and ends up on an argv and an unlink().""" + if not TAKES_DIR or not SAFE_NAME.match(name or "") or name in NOT_A_TAKE: + return None + p = os.path.abspath(os.path.join(TAKES_DIR, name + ".jsonl")) + return p if p.startswith(os.path.abspath(TAKES_DIR) + os.sep) else None + + +def num(d, key, default, lo, hi): + try: + v = float(d.get(key, default)) + except (TypeError, ValueError): + return default + return lo if v < lo else (hi if v > hi else v) + + +def rec_argv(d, action): + """Build the recorder command line. + + ONE builder, used by both /api/rec/start and /api/rec/preview, so the command the panel + shows is the command that runs — there is no second copy of this logic to drift. The + panel is a front-end for the CLI, not a reimplementation of it: every option here is a + flag you could type yourself, and a take made either way lands in the same DataG1/. + """ + name = str(d.get("name") or "take").strip() + argv = [REC_PY, REC_SCRIPT, get_iface(), action] + if action == "record": + argv += ["--output", name, + "--seconds", "%g" % num(d, "seconds", 20, 1, 600), + "--thresh", "%g" % num(d, "thresh", 12, 1, 500), + "--span", "%g" % num(d, "span", 120, 5, 2000), + "--fingers", (d.get("fingers") if d.get("fingers") in + ("touch", "position", "external", "follow") else "touch"), + # Shapes come from THIS dashboard, so keys 1-9 fire the same poses the + # sliders saved. The CLI default points at another host entirely. + "--shapes", "http://127.0.0.1:%d" % PORT] + else: + argv += ["--input", name, "--speed", "%g" % num(d, "speed", 1.0, 0.1, 3.0)] + if d.get("waist") == "lock": + argv += ["--waist", "lock"] + # Arm and hand are independent on BOTH actions: replay honours them too (the recorder + # builds its DDS client from use_arm/use_hand before either branch runs), so a take with + # both tracks can be replayed hand-only onto an arm you would rather not move. + if d.get("no_arm"): + argv += ["--no-arm"] + if d.get("no_hand"): + argv += ["--no-hand"] + return argv + + +def cmd_str(argv): + """The same command as you would type it, cwd included — the panel prints this.""" + return "cd %s && %s" % (shlex.quote(REC_DIR or "?"), " ".join(shlex.quote(a) for a in argv)) + + +@app.route("/api/rec/status") +def api_rec_status(): + t = TAKE + d = {"ok": True, "ready": rec_ready(), "dir": REC_DIR, "sigint": sigint_ok(), + "busy": bool(t and t.alive()), "follow": FOLLOW_ON} + if t: + d.update(t.snapshot()) + return jsonify(d) + + +@app.route("/api/rec/start", methods=["POST"]) +def api_rec_start(): + """Start a take (or a replay) as a child process. + + Refuses to run two at once — both drive the arm over the same DDS topics, and the + second one would fight the first for every joint. + """ + global TAKE + d = request.get_json(force=True) + action = "replay" if d.get("action") == "replay" else "record" + name = str(d.get("name") or "take").strip() + if not rec_ready(): + return jsonify(ok=False, msg="%s not found — set RECORDER_DIR" % REC_SCRIPT) + if not SAFE_NAME.match(name): + return jsonify(ok=False, msg="name: letters, digits, dot, dash, underscore only") + no_arm, no_hand = bool(d.get("no_arm")), bool(d.get("no_hand")) + if no_arm and no_hand: + return jsonify(ok=False, msg="--no-arm and --no-hand together records nothing") + if action == "replay": + # An arm-less replay still drives the hand, which is why the confirm is only + # required when the arm is actually in play. + if not no_arm and not d.get("confirm"): + return jsonify(ok=False, msg="replay moves the arm — confirm required") + if not take_path(name) or not os.path.exists(take_path(name)): + return jsonify(ok=False, msg="no take named %s" % name) + with TAKE_LOCK: + if TAKE and TAKE.alive(): + return jsonify(ok=False, msg="a %s is already running" % TAKE.action) + # Force-follow publishes rt/inspire/cmd at 30Hz from the bridge, and so does the + # recorder. Left armed, the two would overwrite each other every frame and the + # fingers would judder — so the take disarms it and says it did. + disarmed = False + if FOLLOW_ON and not no_hand: + disarmed = set_follow(0).get("ok", False) + argv = rec_argv(d, action) + secs = num(d, "seconds", 20, 1, 600) if action == "record" else None + try: + TAKE = Take(argv, action, secs) + except Exception as e: + return jsonify(ok=False, msg="spawn failed: %s" % e) + return jsonify(ok=True, action=action, argv=argv, cmd=cmd_str(argv), + disarmed_follow=disarmed) + + +@app.route("/api/rec/preview", methods=["POST"]) +def api_rec_preview(): + """The exact command Record/Replay would run, with nothing started. + + This is the panel's honesty check: what it prints is what it spawns, because both come + out of rec_argv(). + """ + d = request.get_json(force=True) + action = "replay" if d.get("action") == "replay" else "record" + argv = rec_argv(d, action) + return jsonify(ok=True, argv=argv, cmd=cmd_str(argv), ready=rec_ready()) + + +@app.route("/api/rec/key", methods=["POST"]) +def api_rec_key(): + t = TAKE + if not (t and t.alive()): + return jsonify(ok=False, msg="nothing running") + return jsonify(ok=t.key(str(request.get_json(force=True).get("k", "")))) + + +@app.route("/api/rec/answer", methods=["POST"]) +def api_rec_answer(): + """Answer the recorder's end-of-take prompts explicitly. + + Both are answered with a literal y or n and never a bare newline, because ask() treats + an empty line as YES — and the first prompt is 'preview', which moves the arm. + """ + t = TAKE + if not (t and t.alive()): + return jsonify(ok=False, msg="nothing running") + return jsonify(ok=t.answer(bool(request.get_json(force=True).get("yes"))), phase=t.phase) + + +@app.route("/api/rec/stop", methods=["POST"]) +def api_rec_stop(): + t = TAKE + if not (t and t.alive()): + return jsonify(ok=False, msg="nothing running") + t.stop(hard=bool(request.get_json(force=True).get("hard"))) + return jsonify(ok=True) + + +@app.route("/api/takes") +def api_takes(): + out = [] + for p in sorted(glob.glob(os.path.join(TAKES_DIR, "*.jsonl")) if TAKES_DIR else []): + if os.path.basename(p)[:-6] in NOT_A_TAKE: + continue + info = {"name": os.path.basename(p)[:-6], "kb": round(os.path.getsize(p) / 1024.0), + "mtime": int(os.path.getmtime(p)), "frames": None} + try: + with open(p, "rb") as f: + meta = json.loads(f.readline()).get("meta", {}) + # Count the rest in chunks — a 60Hz take is thousands of lines and there is + # no reason to parse any of them just to list the file. + n = sum(c.count(b"\n") for c in iter(lambda: f.read(1 << 20), b"")) + hz = float(meta.get("hz") or 0) + info.update(frames=n, hz=round(hz, 1), arm=bool(meta.get("arm")), + hand=bool(meta.get("hand")), + secs=round(n / hz, 1) if hz > 0 else None) + except Exception: + pass + out.append(info) + # now: so the page can age a take against the ROBOT's clock. Comparing mtime to the + # browser's clock would mislabel takes whenever the two hosts disagree. + return jsonify(ok=True, ready=rec_ready(), dir=TAKES_DIR, takes=out, now=int(time.time()), + total_kb=round(sum(t["kb"] for t in out))) + + +@app.route("/api/take/delete", methods=["POST"]) +def api_take_delete(): + d = request.get_json(force=True) + if d.get("all"): + # Wipes the library. The token is required so that a stray {"all": true} from a + # script or a mis-click cannot erase recordings that may be impossible to redo — + # and the list is re-read here rather than taken from the browser, so whatever the + # page happened to be showing does not decide what gets deleted. + if d.get("confirm") != "DELETE ALL": + return jsonify(ok=False, msg='delete-all needs confirm:"DELETE ALL"') + names = [os.path.basename(p)[:-6] + for p in glob.glob(os.path.join(TAKES_DIR, "*.jsonl"))] if TAKES_DIR else [] + names = [n for n in names if n not in NOT_A_TAKE] + if not names: + return jsonify(ok=False, msg="no takes to delete") + else: + names = d.get("names") or [d.get("name", "")] + gone, bad = [], [] + for n in names[:500]: + p = take_path(str(n)) + if p and os.path.exists(p): + os.remove(p); gone.append(n) + else: + bad.append(n) + return jsonify(ok=bool(gone), deleted=gone, msg=("no such take: %s" % ", ".join(bad)) if bad else "") + + +@app.route("/api/take/duplicate", methods=["POST"]) +def api_take_duplicate(): + """Copy a take under a free name — the safe way to try a destructive edit (a trim, a + rename scheme) without risking the only copy of a pose you cannot easily re-record.""" + d = request.get_json(force=True) + src = take_path(str(d.get("name", ""))) + if not src or not os.path.exists(src): + return jsonify(ok=False, msg="no such take") + stem = os.path.basename(src)[:-6] + for i in range(1, 100): # same naming the recorder uses: name(1) + dst = take_path("%s(%d)" % (stem, i)) + if dst and not os.path.exists(dst): + break + else: + return jsonify(ok=False, msg="too many copies") + with open(src, "rb") as a, open(dst, "wb") as b: + b.write(a.read()) + return jsonify(ok=True, name=os.path.basename(dst)[:-6]) + + +@app.route("/api/take/download") +def api_take_download(): + """Send a take to the browser. + + Hand-rolled rather than send_file(): the robot's container ships Flask 1.1, where the + keyword is attachment_filename, while newer Flask calls it download_name. Takes are a + couple of MB, so reading one into memory costs nothing and the endpoint works on both. + """ + name = request.args.get("name", "") + p = take_path(name) + if not p or not os.path.exists(p): + return jsonify(ok=False, msg="no such take"), 404 + with open(p, "rb") as f: + data = f.read() + return Response(data, mimetype="application/x-ndjson", + headers={"Content-Disposition": 'attachment; filename="%s.jsonl"' % name}) + + +@app.route("/api/take/upload", methods=["POST"]) +def api_take_upload(): + """Bring a take recorded elsewhere (e.g. from the workstation) into DataG1/. + + Validated, not trusted: the name is re-derived from the upload, and the first line must + parse as the recorder's own meta header — otherwise a stray file would sit in the list + looking like a take until someone pressed replay on it. + """ + f = request.files.get("file") + if not f: + return jsonify(ok=False, msg="no file") + stem = os.path.basename(f.filename or "") + stem = stem[:-6] if stem.endswith(".jsonl") else stem + if not SAFE_NAME.match(stem) or stem in NOT_A_TAKE: + return jsonify(ok=False, msg="name must be letters/digits/._- and not %s" % NOT_A_TAKE[0]) + data = f.read() + try: + head = json.loads(data.split(b"\n", 1)[0]) + if "meta" not in head: + raise ValueError("no meta header") + except Exception as e: + return jsonify(ok=False, msg="not a recording (%s)" % e) + dst = take_path(stem) + if not dst: + return jsonify(ok=False, msg="bad name") + if os.path.exists(dst): + if not request.form.get("overwrite"): + return jsonify(ok=False, msg="%s already exists" % stem) + with open(dst, "wb") as out: + out.write(data) + return jsonify(ok=True, name=stem, bytes=len(data)) + + +@app.route("/api/take/rename", methods=["POST"]) +def api_take_rename(): + d = request.get_json(force=True) + old, new = take_path(str(d.get("old", ""))), take_path(str(d.get("new", ""))) + if not old or not os.path.exists(old): + return jsonify(ok=False, msg="no such take") + if not new or os.path.exists(new): + return jsonify(ok=False, msg="bad or duplicate name") + os.rename(old, new) + return jsonify(ok=True) + + +HTML = r""" +Inspire Hand + + +
+
Inspire Hand fingers + arm
+ service + bridge + ports + eth0 + wifi + + +
+
+ +
+

Fingers — arm does not move

+
+ + + ready +
+
+
right
+
left
+
+
+ + + + + +
+
+ +
+

Force follow — press a finger and it moves under your hand

+
+ + off + + + +
+
+ Arm it, then push a fingertip pad — that finger closes while you push and holds when you + stop. Raise deadband if fingers drift from incidental contact; raise + sensitivity if you have to push too hard.
+ Only publishes while armed, so it never fights the sliders or a recording. + Safety: a finger closes while you keep pushing — capped at ~3s for full travel, + grip force 400g. Push with a knuckle first. +
+
+ +
+

Joint tracker — watch which joints actually CHANGE

+
+ + + + off + +
+
+
right
+
left
+
+
+ Trace = angle over time (fixed 0..1 scale, so flat really means flat).
Row 1 = angle · Row 2 = Δbase force, current, force limit, temp, ERROR/STATUS.
the exact signal the recorder thresholds on (amber above 12g = a touch it would capture).
+ Pressing a finger moves FORCE but not ANGLE — the drive is non-backdrivable, so contact + cannot bend a joint. Only a command changes the angle. +
+
+ +
+

Diagnostics — live read-back & self-test

+
+ + reads + +
+
+
right (actual)
+
left (actual)
+
+
+ +
+

Read back — copy the hand's current pose into the sliders

+
+ + + + +
+
+ +
+

Arm + hand combo ⚠ arm moves

+
+ + + +
+
+ + + + +
+
+ +
+

Record / replay ⚠ arm moves

+
+ + + + + + + + + idle + +
+
+ in-take keys + + + + + + + + R rest · L rest +
+
+
+ +
idle — nothing running.
+
takes — DataG1/
+
+ + + + + +
+
+
+ + + + +
+
+ +
+

Library — manage saved shapes & combos (modify = load → edit → Save over same name)

+
+
shapes
+
combos
+
+
+ +""" + +if __name__ == "__main__": + ap = argparse.ArgumentParser() + ap.add_argument("--port", type=int, default=8088) + a = ap.parse_args() + PORT = a.port # the recorder is told to pull its shapes off this port + fix_sigint() # before any take is spawned — see sigint_ok() + print("Hand dashboard http://0.0.0.0:%d iface=%s" % (a.port, get_iface())) + print(" recorder: %s" % (os.path.join(REC_DIR, REC_SCRIPT) if rec_ready() + else "NOT FOUND (set RECORDER_DIR)")) + app.run(host="0.0.0.0", port=a.port, threaded=True)