AndrePatri commited on
Commit
d9a72da
·
verified ·
1 Parent(s): 8b15c80

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +2 -0
  2. README.md +47 -3
  3. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/bundle.yaml +107 -0
  4. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/centauro_big_wheels_no_yaw_ub_2026_01_19_14_51_36_ID.srdf +147 -0
  5. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/centauro_big_wheels_no_yaw_ub_2026_01_19_14_51_36_ID.urdf +1569 -0
  6. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/centauro_rhc.py +144 -0
  7. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/centauro_rhc_wheels_continuous_no_yaw_ub.yaml +368 -0
  8. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl_model +3 -0
  9. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/fake_pos_track_env_phase_control.py +198 -0
  10. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/flight_phase_control_env.py +219 -0
  11. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/hybrid_quad_rhc.py +1243 -0
  12. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/isaac_world_interface.py +0 -0
  13. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/jnt_imp_config.yaml +44 -0
  14. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/jnt_imp_config_no_yaw.yaml +44 -0
  15. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/launch_control_cluster.py +106 -0
  16. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/launch_train_env.py +357 -0
  17. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/launch_world_interface.py +202 -0
  18. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/__init__.py +0 -0
  19. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/augmpc_cluster_client.py +105 -0
  20. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/augmpc_cluster_server.py +43 -0
  21. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/__init__.py +0 -0
  22. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/config/rhc_horizon_config.yaml +200 -0
  23. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/gait_manager.py +565 -0
  24. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/horizon_imports.py +18 -0
  25. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/horizon_imports_glob.py +28 -0
  26. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/hybrid_quad_rhc.py +1243 -0
  27. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/hybrid_quad_rhc_refs.py +381 -0
  28. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/utils/__init__.py +0 -0
  29. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/utils/math_utils.py +165 -0
  30. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/hybrid_quad_client.py +95 -0
  31. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/rhc.py +1226 -0
  32. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/sac.py +680 -0
  33. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/sactor_critic_algo.py +0 -0
  34. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/training_env_base.py +2000 -0
  35. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/twist_tracking_env.py +1396 -0
  36. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/world_interface_base.py +1486 -0
  37. bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/xbot2_basic.yaml +81 -0
  38. bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/bundle.yaml +107 -0
  39. bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/centauro_big_wheels_ub_2026_02_21_13_59_20_ID.srdf +147 -0
  40. bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/centauro_big_wheels_ub_2026_02_21_13_59_20_ID.urdf +1569 -0
  41. bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/centauro_rhc.py +145 -0
  42. bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/centauro_rhc_config_no_wheels_ub.yaml +295 -0
  43. bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv_model +3 -0
  44. bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/fake_pos_tracking_env.py +202 -0
  45. bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/hybrid_quad_rhc.py +1324 -0
  46. bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/isaac_world_interface.py +0 -0
  47. bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/jnt_imp_config.yaml +46 -0
  48. bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/jnt_imp_config_with_ub.yaml +46 -0
  49. bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/launch_control_cluster.py +106 -0
  50. bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/launch_train_env.py +358 -0
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+
37
+ *_model filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,3 +1,47 @@
1
- ---
2
- license: gpl-2.0
3
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: gpl-2.0
3
+ ---
4
+
5
+ # AugMPCModels
6
+
7
+ Deployment bundles for [AugMPC](https://github.com/AndrePatri/AugMPC), published alongside the preprint:
8
+
9
+ - Andrea Patrizi, Carlo Rizzardo, Arturo Laurenzi, Francesco Ruscelli, Luca Rossini, Nikos G. Tsagarakis
10
+ - *RL-Augmented MPC for Non-Gaited Legged and Hybrid Locomotion*
11
+ - arXiv: [2603.10878](https://arxiv.org/abs/2603.10878)
12
+
13
+ ## Repository structure
14
+
15
+ Each model is distributed as a **bundle**, not just as a checkpoint file. A bundle may include:
16
+
17
+ - the policy checkpoint
18
+ - environment and deployment configuration files
19
+ - robot description files (`URDF`, `SRDF`)
20
+ - controller/world-interface helpers needed for reproduction
21
+ - a `bundle.yaml` manifest describing the contents
22
+
23
+ Current layout:
24
+
25
+ ```text
26
+ bundles/
27
+ centauro/
28
+ <bundle_name>/
29
+ bundle.yaml
30
+ <checkpoint>_model
31
+ *.yaml
32
+ *.py
33
+ *.urdf
34
+ *.srdf
35
+ other/
36
+ ```
37
+
38
+ ## Usage model
39
+
40
+ The intended runtime flow is:
41
+
42
+ 1. resolve or download a bundle locally,
43
+ 2. read `bundle.yaml`,
44
+ 3. locate the checkpoint file and companion configs,
45
+ 4. launch AugMPC using the resolved local bundle path.
46
+
47
+ This repository is therefore meant to back a bundle resolver, rather than a direct `torch.load()` call on a remote file.
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/bundle.yaml ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ bundle_format: augmpc_model_bundle_v1
2
+ bundle_name: d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl
3
+ checkpoint_file: d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl_model
4
+ preserved_training_cfgs:
5
+ - ibrido_run__2026_01_19_14_51_36_ID/training_cfg_centauro_no_yaw_ub_cloop_perception.sh
6
+ framework:
7
+ repos:
8
+ AugMPC:
9
+ commit: f2ff28085ea76d2b548841de6f363f7183480f86
10
+ branch: ibrido
11
+ remote: git@github.com:AndrePatri/AugMPC.git
12
+ dirty: false
13
+ AugMPCEnvs:
14
+ commit: 46c5d4161cb4b249b3a2e50c93c6bc2aa087f027
15
+ branch: ibrido
16
+ remote: git@github.com:AndrePatri/AugMPCEnvs.git
17
+ dirty: false
18
+ AugMPCModels:
19
+ commit: 8b15c800c0f5684c61fdaf4847156ff71df61ebc
20
+ branch: main
21
+ remote: https://huggingface.co/AndrePatri/AugMPCModels
22
+ dirty: true
23
+ CentauroHybridMPC:
24
+ commit: 640889d4c3b9c8d360b5a3ccde6fc2bd8f139891
25
+ branch: ibrido
26
+ remote: git@github.com:ADVRHumanoids/CentauroHybridMPC.git
27
+ dirty: false
28
+ EigenIPC:
29
+ commit: 7c1c3ecd08716e61ed164a6e0bb788aa716705ca
30
+ branch: devel
31
+ remote: git@github.com:AndrePatri/EigenIPC.git
32
+ dirty: false
33
+ IBRIDO:
34
+ commit: 0ceb5f3e0508b6ecdce12bcc8f0dcbcde8f29a93
35
+ branch: main
36
+ remote: git@github.com:AndrePatri/IBRIDO.git
37
+ dirty: false
38
+ IsaacLab:
39
+ commit: a520a883ce996d855cc9d5255d71fd1c1307633f
40
+ branch: HEAD
41
+ remote: git@github.com:isaac-sim/IsaacLab.git
42
+ dirty: true
43
+ KyonRLStepping:
44
+ commit: 2bea2b8d70078974869df0549e90fc27ff31f851
45
+ branch: ibrido
46
+ remote: git@github.com:ADVRHumanoids/KyonRLStepping.git
47
+ dirty: false
48
+ MPCHive:
49
+ commit: 45b4fc692850cef607020dda2e32fd708e7fff62
50
+ branch: devel
51
+ remote: git@github.com:AndrePatri/MPCHive.git
52
+ dirty: false
53
+ MPCViz:
54
+ commit: 806d03efcc9d8ab1fb04991a159c19ba89bfb85b
55
+ branch: ros2_humble
56
+ remote: git@github.com:AndrePatri/MPCViz.git
57
+ dirty: false
58
+ adarl:
59
+ commit: f585499e49fa05fdd070a77f3211c0996599b87b
60
+ branch: ibrido
61
+ remote: git@github.com:c-rizz/adarl.git
62
+ dirty: false
63
+ casadi:
64
+ commit: 79cebe3b416dee22abc87de0076b80a5b97bd345
65
+ branch: optional_float
66
+ remote: git@github.com:AndrePatri/casadi.git
67
+ dirty: true
68
+ horizon:
69
+ commit: 3b084317709ff9b88d4a07ac5436f5988b39eece
70
+ branch: ibrido
71
+ remote: git@github.com:AndrePatri/horizon.git
72
+ dirty: true
73
+ iit-centauro-ros-pkg:
74
+ commit: 6069807ebb37a6d7df39430a02685e09ed9b167a
75
+ branch: ibrido_ros2
76
+ remote: git@github.com:AndrePatri/iit-centauro-ros-pkg.git
77
+ dirty: false
78
+ iit-dagana-ros-pkg:
79
+ commit: f7ecd6c84a0b7f3320c1b7de6449cbcd4445d2fe
80
+ branch: ibrido_ros2
81
+ remote: git@github.com:AndrePatri/iit-dagana-ros-pkg.git
82
+ dirty: false
83
+ iit-kyon-description:
84
+ commit: 50fd7c8909b3ddfd1ebbe67c61d6b775b86df6b1
85
+ branch: ibrido_ros2
86
+ remote: git@github.com:ADVRHumanoids/iit-kyon-ros-pkg.git
87
+ dirty: false
88
+ iit-kyon-description-mpc:
89
+ commit: 3a92bee28405172e8f6c628b4498703724d35bf5
90
+ branch: ibrido_ros2
91
+ remote: git@github.com:ADVRHumanoids/iit-kyon-ros-pkg.git
92
+ dirty: false
93
+ iit-kyon-ros-pkg:
94
+ commit: 298917efffb63dbca540e0aedbd21b41bf393fbf
95
+ branch: ibrido_ros2_simple
96
+ remote: git@github.com:ADVRHumanoids/iit-kyon-ros-pkg.git
97
+ dirty: false
98
+ phase_manager:
99
+ commit: 9925d18c0d7a55d013cbaa4998c61d85a3a8944f
100
+ branch: ibrido
101
+ remote: git@github.com:AndrePatri/phase_manager.git
102
+ dirty: false
103
+ unitree_ros:
104
+ commit: c75a622b8782d11dd6aa4c2ebd3b0f9c13a56aae
105
+ branch: ibrido
106
+ remote: git@github.com:AndrePatri/unitree_ros.git
107
+ dirty: true
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/centauro_big_wheels_no_yaw_ub_2026_01_19_14_51_36_ID.srdf ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" ?>
2
+ <!-- =================================================================================== -->
3
+ <!-- | This document was autogenerated by xacro from /root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_srdf/srdf/centauro.srdf.xacro | -->
4
+ <!-- | EDITING THIS FILE BY HAND IS NOT RECOMMENDED | -->
5
+ <!-- =================================================================================== -->
6
+ <!--This does not replace URDF, and is not an extension of URDF.
7
+ This is a format for representing semantic information about the robot structure.
8
+ A URDF file must exist for this robot as well, where the joints and the links that are referenced are defined
9
+ -->
10
+ <robot name="centauro">
11
+ <!-- none, ball, dagana_fixed, dagana -->
12
+ <group name="base">
13
+ <link name="pelvis"/>
14
+ </group>
15
+ <group name="imu_sensors">
16
+ <link name="imu_link"/>
17
+ </group>
18
+ <group name="front_left_leg">
19
+ <chain base_link="pelvis" tip_link="wheel_1"/>
20
+ </group>
21
+ <group name="front_right_leg">
22
+ <chain base_link="pelvis" tip_link="wheel_2"/>
23
+ </group>
24
+ <group name="rear_right_leg">
25
+ <chain base_link="pelvis" tip_link="wheel_4"/>
26
+ </group>
27
+ <group name="rear_left_leg">
28
+ <chain base_link="pelvis" tip_link="wheel_3"/>
29
+ </group>
30
+ <group name="left_arm">
31
+ <chain base_link="torso_2" tip_link="arm1_8"/>
32
+ </group>
33
+ <group name="right_arm">
34
+ <chain base_link="torso_2" tip_link="arm2_8"/>
35
+ </group>
36
+ <group name="torso">
37
+ <chain base_link="pelvis" tip_link="torso_2"/>
38
+ </group>
39
+ <group name="velodyne">
40
+ <chain base_link="torso_2" tip_link="velodyne_motor"/>
41
+ </group>
42
+ <group name="d435_head">
43
+ <chain base_link="torso_2" tip_link="d435_head_motor"/>
44
+ </group>
45
+ <group name="chains">
46
+ <group name="front_left_leg"/>
47
+ <group name="front_right_leg"/>
48
+ <group name="rear_right_leg"/>
49
+ <group name="rear_left_leg"/>
50
+ <group name="left_arm"/>
51
+ <group name="right_arm"/>
52
+ <group name="torso"/>
53
+ <group name="velodyne"/>
54
+ <group name="d435_head"/>
55
+ </group>
56
+ <!-- Homing posture -->
57
+ <!-- <group_state name="home" group="chains">
58
+ <joint name="hip_yaw_1" value="-0.746874"/>
59
+ <joint name="hip_pitch_1" value="-1.25409"/>
60
+ <joint name="knee_pitch_1" value="-1.55576"/>
61
+ <joint name="ankle_pitch_1" value="-0.301666"/>
62
+ <joint name="ankle_yaw_1" value="0.746874"/>
63
+ <joint name="hip_yaw_2" value="0.746874"/>
64
+ <joint name="hip_pitch_2" value="1.25409"/>
65
+ <joint name="knee_pitch_2" value="1.55576"/>
66
+ <joint name="ankle_pitch_2" value="0.301666"/>
67
+ <joint name="ankle_yaw_2" value="-0.746874"/>
68
+ <joint name="hip_yaw_3" value="0.746874"/>
69
+ <joint name="hip_pitch_3" value="1.25409"/>
70
+ <joint name="knee_pitch_3" value="1.55576"/>
71
+ <joint name="ankle_pitch_3" value="0.301667"/>
72
+ <joint name="ankle_yaw_3" value="-0.746874"/>
73
+ <joint name="hip_yaw_4" value="-0.746874"/>
74
+ <joint name="hip_pitch_4" value="-1.25409"/>
75
+ <joint name="knee_pitch_4" value="-1.55576"/>
76
+ <joint name="ankle_pitch_4" value="-0.301667"/>
77
+ <joint name="ankle_yaw_4" value="0.746874"/>
78
+ <joint name="torso_yaw" value="3.56617e-13"/>
79
+ <joint name="velodyne_joint" value="0"/>
80
+ <joint name="d435_head_joint" value="0"/>
81
+ <joint name="j_arm1_1" value="0.520149"/>
82
+ <joint name="j_arm1_2" value="0.320865"/>
83
+ <joint name="j_arm1_3" value="0.274669"/>
84
+ <joint name="j_arm1_4" value="-2.23604"/>
85
+ <joint name="j_arm1_5" value="0.0500815"/>
86
+ <joint name="j_arm1_6" value="-0.781461"/>
87
+ <joint name="j_arm2_1" value="0.520149"/>
88
+ <joint name="j_arm2_2" value="-0.320865"/>
89
+ <joint name="j_arm2_3" value="-0.274669"/>
90
+ <joint name="j_arm2_4" value="-2.23604"/>
91
+ <joint name="j_arm2_5" value="-0.0500815"/>
92
+ <joint name="j_arm2_6" value="-0.781461"/>
93
+ <joint name="j_wheel_1" value="0.0"/>
94
+ <joint name="j_wheel_2" value="0.0"/>
95
+ <joint name="j_wheel_3" value="0.0"/>
96
+ <joint name="j_wheel_4" value="0.0"/>
97
+ <xacro:if value="${END_EFFECTOR_LEFT == 'dagana'}">
98
+ <joint name="dagana_1_claw_joint" value="0"/>
99
+ </xacro:if>
100
+ <xacro:if value="${END_EFFECTOR_RIGHT == 'dagana'}">
101
+ <joint name="dagana_2_claw_joint" value="0"/>
102
+ </xacro:if>
103
+
104
+ </group_state> -->
105
+ <!-- Balanced weight homing -->
106
+ <group_state group="chains" name="home">
107
+ <joint name="hip_yaw_1" value="-0.5"/>
108
+ <joint name="hip_pitch_1" value="-1.37"/>
109
+ <joint name="knee_pitch_1" value="-1.34"/>
110
+ <joint name="ankle_pitch_1" value="-0.03"/>
111
+ <joint name="ankle_yaw_1" value="0.6"/>
112
+ <joint name="hip_yaw_2" value="0.5"/>
113
+ <joint name="hip_pitch_2" value="1.37"/>
114
+ <joint name="knee_pitch_2" value="1.34"/>
115
+ <joint name="ankle_pitch_2" value="-0.03"/>
116
+ <joint name="ankle_yaw_2" value="-0.6"/>
117
+ <joint name="hip_yaw_3" value="1.28"/>
118
+ <joint name="hip_pitch_3" value="1.29"/>
119
+ <joint name="knee_pitch_3" value="1.71"/>
120
+ <joint name="ankle_pitch_3" value="0.43"/>
121
+ <joint name="ankle_yaw_3" value="-1.3"/>
122
+ <joint name="hip_yaw_4" value="-1.28"/>
123
+ <joint name="hip_pitch_4" value="-1.29"/>
124
+ <joint name="knee_pitch_4" value="-1.71"/>
125
+ <joint name="ankle_pitch_4" value="-0.43"/>
126
+ <joint name="ankle_yaw_4" value="1.3"/>
127
+ <joint name="torso_yaw" value="3.56617e-13"/>
128
+ <joint name="velodyne_joint" value="0"/>
129
+ <joint name="d435_head_joint" value="0"/>
130
+ <joint name="j_arm1_1" value="0.520149"/>
131
+ <joint name="j_arm1_2" value="0.320865"/>
132
+ <joint name="j_arm1_3" value="0.274669"/>
133
+ <joint name="j_arm1_4" value="-2.23604"/>
134
+ <joint name="j_arm1_5" value="0.0500815"/>
135
+ <joint name="j_arm1_6" value="-0.781461"/>
136
+ <joint name="j_arm2_1" value="0.520149"/>
137
+ <joint name="j_arm2_2" value="-0.320865"/>
138
+ <joint name="j_arm2_3" value="-0.274669"/>
139
+ <joint name="j_arm2_4" value="-2.23604"/>
140
+ <joint name="j_arm2_5" value="-0.0500815"/>
141
+ <joint name="j_arm2_6" value="-0.781461"/>
142
+ <joint name="j_wheel_1" value="0.0"/>
143
+ <joint name="j_wheel_2" value="0.0"/>
144
+ <joint name="j_wheel_3" value="0.0"/>
145
+ <joint name="j_wheel_4" value="0.0"/>
146
+ </group_state>
147
+ </robot>
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/centauro_big_wheels_no_yaw_ub_2026_01_19_14_51_36_ID.urdf ADDED
@@ -0,0 +1,1569 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" ?>
2
+ <!-- =================================================================================== -->
3
+ <!-- | This document was autogenerated by xacro from /root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/urdf/centauro.urdf.xacro | -->
4
+ <!-- | EDITING THIS FILE BY HAND IS NOT RECOMMENDED | -->
5
+ <!-- =================================================================================== -->
6
+ <robot name="centauro">
7
+ <!-- none, ball, dagana_fixed, dagana -->
8
+ <!-- scaling facto for available peack torque link side, from 0 to 1 -->
9
+ <material name="black">
10
+ <color rgba="0.4 0.4 0.4 1"/>
11
+ </material>
12
+ <material name="dark">
13
+ <color rgba="0.1 0.1 0.1 1"/>
14
+ </material>
15
+ <material name="dark_grey">
16
+ <color rgba="0.2 0.2 0.2 1"/>
17
+ </material>
18
+ <!-- Note that the "23" suffix denotes the new bigger-size wheel -->
19
+ <!-- ??? -->
20
+ <!-- ??? -->
21
+ <!-- Origin -->
22
+ <!-- <xacro:property name="hip_yaw_lower" value="${[-2.827,-2.583, -2.583 ,-2.827]}"/>
23
+ <xacro:property name="hip_yaw_upper" value="${[ 2.583, 2.827, 2.827 , 2.583]}"/> -->
24
+ <link name="base_link"/>
25
+ <joint name="base_joint" type="fixed">
26
+ <parent link="base_link"/>
27
+ <child link="pelvis"/>
28
+ <origin rpy="0 0 0" xyz="0 0 0"/>
29
+ </joint>
30
+ <link name="pelvis">
31
+ <inertial>
32
+ <origin xyz="-0.0016320207 0.0026534004000000003 0.079167428"/>
33
+ <mass value="26.584529"/>
34
+ <inertia ixx="0.39195477" ixy="0.024781258999999996" ixz="0.017108606999999998" iyy="1.0000708999999999" iyz="0.0052627106000000005" izz="1.0679988999999999"/>
35
+ </inertial>
36
+ <visual>
37
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
38
+ <geometry>
39
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/pelvis.stl" scale="0.001 0.001 0.001"/>
40
+ </geometry>
41
+ </visual>
42
+ <collision>
43
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
44
+ <geometry>
45
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/pelvis_reduced.stl" scale="0.001 0.001 0.001"/>
46
+ </geometry>
47
+ </collision>
48
+ </link>
49
+ <link name="imu_link"/>
50
+ <joint name="imu_joint" type="fixed">
51
+ <parent link="pelvis"/>
52
+ <origin rpy="0.0 0.0 0.0" xyz="0.248316 0.0 0.015"/>
53
+ <child link="imu_link"/>
54
+ </joint>
55
+ <!-- torso links -->
56
+ <link name="torso_2">
57
+ <inertial>
58
+ <origin xyz="-0.019497794 0.0045317835 0.13768283"/>
59
+ <mass value="12.553731"/>
60
+ <inertia ixx="0.063643392" ixy="8.939e-05" ixz="-0.00086873" iyy="0.02680235" iyz="-4.657e-05" izz="0.04743015"/>
61
+ </inertial>
62
+ <visual>
63
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
64
+ <geometry>
65
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/link1_no_head.stl" scale="0.001 0.001 0.001"/>
66
+ </geometry>
67
+ </visual>
68
+ <collision>
69
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
70
+ <geometry>
71
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/link1_no_head.stl" scale="0.001 0.001 0.001"/>
72
+ </geometry>
73
+ </collision>
74
+ </link>
75
+ <!-- /torso links -->
76
+ <!-- torso joints -->
77
+ <joint name="torso_yaw" type="revolute">
78
+ <parent link="pelvis"/>
79
+ <origin xyz="0.2 0.0 0.256"/>
80
+ <child link="torso_2"/>
81
+ <axis xyz="0 0 1"/>
82
+ <limit effort="147" lower="-2.618" upper="2.618" velocity="5.7"/>
83
+ <!-- TODO -->
84
+ </joint>
85
+ <!-- /torso joints -->
86
+ <!-- /macro arms -->
87
+ <!-- LINKS -->
88
+ <!-- shoulder yaw-roll-->
89
+ <link name="arm1_1">
90
+ <inertial>
91
+ <origin xyz="-0.0074457212 0.03410796 0.00010978102"/>
92
+ <mass value="1.9628675"/>
93
+ <inertia ixx="0.0053547717" ixy="0.00036428926" ixz="1.5089568e-05" iyy="0.0033923328" iyz="5.5692312e-05" izz="0.0068921413"/>
94
+ </inertial>
95
+ <visual>
96
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
97
+ <geometry>
98
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/ShoulderPitch.STL" scale="0.001 -0.001 0.001"/>
99
+ </geometry>
100
+ <material name="dark_grey"/>
101
+ </visual>
102
+ <collision>
103
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
104
+ <geometry>
105
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/ShoulderPitch.STL" scale="0.001 -0.001 0.001"/>
106
+ </geometry>
107
+ </collision>
108
+ </link>
109
+ <!-- shoulder yaw-roll-->
110
+ <!-- shoulder roll-pitch-->
111
+ <link name="arm1_2">
112
+ <inertial>
113
+ <origin xyz="0.058142302 5.7450803e-05 -0.077477683"/>
114
+ <mass value="1.8595811"/>
115
+ <inertia ixx="0.013776643" ixy="3.7788675e-05" ixz="0.0037690171" iyy="0.015677464" iyz="-9.4893549e-06" izz="0.0046317657"/>
116
+ </inertial>
117
+ <visual>
118
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
119
+ <geometry>
120
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/ShoulderRoll.STL" scale="0.001 -0.001 0.001"/>
121
+ </geometry>
122
+ </visual>
123
+ <collision>
124
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
125
+ <geometry>
126
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/ShoulderRoll.STL" scale="0.001 -0.001 0.001"/>
127
+ </geometry>
128
+ </collision>
129
+ </link>
130
+ <!-- shoulder roll-pitch-->
131
+ <!-- shoulder-elbow -->
132
+ <link name="arm1_3">
133
+ <inertial>
134
+ <origin xyz="0.014625194 0.0008172672 -0.028333545"/>
135
+ <mass value="1.6678109"/>
136
+ <inertia ixx="0.0064480435" ixy="-0.00015639093" ixz="0.0012205359" iyy="0.0073372077" iyz="8.9941532e-05" izz="0.0036738448"/>
137
+ </inertial>
138
+ <visual>
139
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
140
+ <geometry>
141
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/ShoulderYaw.STL" scale="0.001 -0.001 0.001"/>
142
+ </geometry>
143
+ <material name="dark_grey"/>
144
+ </visual>
145
+ <collision>
146
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
147
+ <geometry>
148
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/ShoulderYaw.STL" scale="0.001 -0.001 0.001"/>
149
+ </geometry>
150
+ </collision>
151
+ </link>
152
+ <!-- /shoulder-elbow -->
153
+ <!-- elbow yaw-pitch -->
154
+ <link name="arm1_4">
155
+ <inertial>
156
+ <origin xyz="-0.0076833067 -0.040302205 -0.043492779"/>
157
+ <mass value="1.3157289"/>
158
+ <inertia ixx="0.004330394" ixy="-0.00011737391" ixz="-0.00041923199" iyy="0.0038539919" iyz="-0.00079573038" izz="0.0017594689"/>
159
+ </inertial>
160
+ <visual>
161
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
162
+ <geometry>
163
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/Elbow.STL" scale="0.001 -0.001 0.001"/>
164
+ </geometry>
165
+ </visual>
166
+ <collision>
167
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
168
+ <geometry>
169
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/Elbow.STL" scale="0.001 -0.001 0.001"/>
170
+ </geometry>
171
+ </collision>
172
+ </link>
173
+ <!-- /elbow yaw-pitch -->
174
+ <!-- elbow-wrist -->
175
+ <link name="arm1_5">
176
+ <inertial>
177
+ <origin xyz="-0.00011079615 0.011590836 -0.07816026"/>
178
+ <mass value="1.4908547"/>
179
+ <inertia ixx="0.0085692128" ixy="1.7856252e-05" ixz="1.9379365e-05" iyy="0.0077454159" iyz="-0.00032860094" izz="0.0027441921"/>
180
+ </inertial>
181
+ <visual>
182
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
183
+ <geometry>
184
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/Forearm.STL" scale="0.001 -0.001 0.001"/>
185
+ </geometry>
186
+ <material name="dark_grey"/>
187
+ </visual>
188
+ <collision>
189
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
190
+ <geometry>
191
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/Forearm.STL" scale="0.001 -0.001 0.001"/>
192
+ </geometry>
193
+ </collision>
194
+ </link>
195
+ <!-- /elbow-wrist -->
196
+ <!-- wrist yaw-pitch -->
197
+ <link name="arm1_6">
198
+ <inertial>
199
+ <origin xyz="-4.6502396e-06 -0.038014094 -0.069926878"/>
200
+ <mass value="1.1263612"/>
201
+ <inertia ixx="0.0051871784" ixy="2.724437e-05" ixz="2.2833496e-06" iyy="0.0048037789" iyz="-0.00072165653" izz="0.0012771388"/>
202
+ </inertial>
203
+ <visual>
204
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
205
+ <geometry>
206
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/wrist_pitch_right.stl" scale="0.001 -0.001 0.001"/>
207
+ </geometry>
208
+ </visual>
209
+ <collision>
210
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
211
+ <geometry>
212
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/wrist_pitch_right.stl" scale="0.001 -0.001 0.001"/>
213
+ </geometry>
214
+ </collision>
215
+ </link>
216
+ <!-- /wrist yaw-pitch -->
217
+ <!-- <link name="arm${arm_num}_7">
218
+ <inertial>
219
+ <origin xyz="${Wrist_3_x} ${rot*Wrist_3_y} ${Wrist_3_z}"/>
220
+ <mass value="${Wrist_3_mass}"/>
221
+ <inertia ixx="${Wrist_3_xx}" ixy="${rot*Wrist_3_xy}" ixz="${Wrist_3_xz}" iyy="${Wrist_3_yy}" iyz="${rot*Wrist_3_yz}" izz="${Wrist_3_zz}"/>
222
+ </inertial>
223
+
224
+ <visual>
225
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
226
+ <geometry>
227
+ <mesh filename="${MESH_PATH}/ForearmYaw.STL" scale="0.001 ${-rot*0.001} 0.001" />
228
+ </geometry>
229
+ <material name="dark_grey"/>
230
+ </visual>
231
+
232
+ <collision>
233
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
234
+ <geometry>
235
+ <mesh filename="${MESH_PATH}/simple/ForearmYaw.STL" scale="0.001 ${-rot*0.001} 0.001" />
236
+ </geometry>
237
+ </collision>
238
+ </link> -->
239
+ <!-- /LINKS -->
240
+ <!-- JOINTS -->
241
+ <!-- shoulder yaw -->
242
+ <joint name="j_arm1_1" type="revolute">
243
+ <parent link="torso_2"/>
244
+ <child link="arm1_1"/>
245
+ <origin rpy="0.174533 0.0 -0.5235988" xyz="0.0457475 0.169137 0.116626"/>
246
+ <axis xyz="0 1 0"/>
247
+ <limit effort="147.0" lower="-3.312" upper="1.615" velocity="3.86"/>
248
+ </joint>
249
+ <!-- /shoulder yaw -->
250
+ <!-- shoulder roll -->
251
+ <joint name="j_arm1_2" type="revolute">
252
+ <parent link="arm1_1"/>
253
+ <child link="arm1_2"/>
254
+ <origin rpy="-0.1745327 0.0 0.0" xyz="-0.09015 0.062 0.0"/>
255
+ <axis xyz="1 0 0"/>
256
+ <limit effort="147.0" lower="0.02" upper="3.431" velocity="3.86"/>
257
+ </joint>
258
+ <!-- /shoulder roll -->
259
+ <!-- shoulder pitch-->
260
+ <joint name="j_arm1_3" type="revolute">
261
+ <parent link="arm1_2"/>
262
+ <child link="arm1_3"/>
263
+ <origin xyz="0.09015 0.0 -0.21815"/>
264
+ <axis xyz="0 0 1"/>
265
+ <limit effort="147.0" lower="-2.552" upper="2.566" velocity="6.06"/>
266
+ </joint>
267
+ <!-- /shoulder pitch -->
268
+ <!-- elbow yaw -->
269
+ <joint name="j_arm1_4" type="revolute">
270
+ <parent link="arm1_3"/>
271
+ <child link="arm1_4"/>
272
+ <origin xyz="0.045 0.05515 -0.074"/>
273
+ <axis xyz="0 1 0"/>
274
+ <limit effort="147.0" lower="-2.465" upper="0.28" velocity="6.06"/>
275
+ </joint>
276
+ <!-- /elbow yaw -->
277
+ <!-- elbow pitch-->
278
+ <joint name="j_arm1_5" type="revolute">
279
+ <parent link="arm1_4"/>
280
+ <child link="arm1_5"/>
281
+ <origin xyz="-0.015 -0.05515 -0.095"/>
282
+ <axis xyz="0 0 1"/>
283
+ <limit effort="55.0" lower="-2.569" upper="2.562" velocity="11.72"/>
284
+ </joint>
285
+ <!-- /elbow pitch-->
286
+ <!-- wrist yaw-->
287
+ <joint name="j_arm1_6" type="revolute">
288
+ <parent link="arm1_5"/>
289
+ <child link="arm1_6"/>
290
+ <origin xyz="0.0 0.049 -0.156"/>
291
+ <axis xyz="0 1 0"/>
292
+ <limit effort="55.0" lower="-1.529" upper="1.509" velocity="11.72"/>
293
+ </joint>
294
+ <!-- /wrist yaw-->
295
+ <!-- wrist pitch-->
296
+ <!-- <joint name="j_arm${arm_num}_7" type="fixed">
297
+ <parent link="arm${arm_num}_6"/>
298
+ <child link="arm${arm_num}_7"/>
299
+ <origin xyz="${Wrist_3_Ox} ${rot*Wrist_3_Oy} ${Wrist_3_Oz}"/>
300
+ <axis xyz="0 0 1"/>
301
+ <limit lower="${j_arm_7_lower[arm_num-1]}" upper="${j_arm_7_upper[arm_num-1]}" effort="${j_arm_7_torque[arm_num-1]}" velocity="${j_arm_7_velocity[arm_num-1]}"/>
302
+ </joint> -->
303
+ <!-- wrist pitch-->
304
+ <!-- force-troque sensor -->
305
+ <!-- end-effector -->
306
+ <!-- wrist pitch-->
307
+ <!-- <joint name="j_ft_${arm_num}" type="fixed">
308
+ <parent link="arm${arm_num}_7"/>
309
+ <child link="ft_arm${arm_num}"/>
310
+ <origin xyz="${Ft_arm_Ox} ${Ft_arm_Oy} ${Ft_arm_Oz}" rpy="${Ft_arm_roll} ${Ft_arm_pitch} ${Ft_arm_yaw}" />
311
+ <axis xyz="0 0 1"/>
312
+ <limit lower="0.0" upper="0.0" effort="0" velocity="0"/>
313
+ </joint> -->
314
+ <!-- wrist pitch-->
315
+ <!-- <link name="ft_arm${arm_num}">
316
+ <inertial>
317
+ <origin xyz="${Ft_arm_x} ${rot*Ft_arm_y} ${Ft_arm_z}"/>
318
+ <mass value="${Ft_arm_mass}"/>
319
+ <inertia ixx="${Ft_arm_xx}" ixy="${rot*Ft_arm_xy}" ixz="${Ft_arm_xz}" iyy="${Ft_arm_yy}" iyz="${rot*Ft_arm_yz}" izz="${Ft_arm_zz}"/>
320
+ </inertial> -->
321
+ <!--visual>
322
+ <origin xyz="0.0 0.0 0.0" rpy="${PI} 0.0 0.0"/>
323
+ <geometry>
324
+ <mesh filename="${MESH_PATH}/Wrist.STL" scale="0.001 ${-rot*0.001} 0.001" />
325
+ </geometry>
326
+ </visual-->
327
+ <!--collision>
328
+ <origin xyz="0.0 0.0 0.0" rpy="${PI} 0.0 0.0"/>
329
+ <geometry>
330
+ <mesh filename="${MESH_PATH}/simple/Wrist.STL" scale="0.001 ${-rot*0.001} 0.001" />
331
+ </geometry>
332
+ </collision-->
333
+ <!-- </link> -->
334
+ <!-- LINKS -->
335
+ <!-- shoulder yaw-roll-->
336
+ <link name="arm2_1">
337
+ <inertial>
338
+ <origin xyz="-0.0074457212 -0.03410796 0.00010978102"/>
339
+ <mass value="1.9628675"/>
340
+ <inertia ixx="0.0053547717" ixy="-0.00036428926" ixz="1.5089568e-05" iyy="0.0033923328" iyz="-5.5692312e-05" izz="0.0068921413"/>
341
+ </inertial>
342
+ <visual>
343
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
344
+ <geometry>
345
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/ShoulderPitch.STL" scale="0.001 0.001 0.001"/>
346
+ </geometry>
347
+ <material name="dark_grey"/>
348
+ </visual>
349
+ <collision>
350
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
351
+ <geometry>
352
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/ShoulderPitch.STL" scale="0.001 0.001 0.001"/>
353
+ </geometry>
354
+ </collision>
355
+ </link>
356
+ <!-- shoulder yaw-roll-->
357
+ <!-- shoulder roll-pitch-->
358
+ <link name="arm2_2">
359
+ <inertial>
360
+ <origin xyz="0.058142302 -5.7450803e-05 -0.077477683"/>
361
+ <mass value="1.8595811"/>
362
+ <inertia ixx="0.013776643" ixy="-3.7788675e-05" ixz="0.0037690171" iyy="0.015677464" iyz="9.4893549e-06" izz="0.0046317657"/>
363
+ </inertial>
364
+ <visual>
365
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
366
+ <geometry>
367
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/ShoulderRoll.STL" scale="0.001 0.001 0.001"/>
368
+ </geometry>
369
+ </visual>
370
+ <collision>
371
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
372
+ <geometry>
373
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/ShoulderRoll.STL" scale="0.001 0.001 0.001"/>
374
+ </geometry>
375
+ </collision>
376
+ </link>
377
+ <!-- shoulder roll-pitch-->
378
+ <!-- shoulder-elbow -->
379
+ <link name="arm2_3">
380
+ <inertial>
381
+ <origin xyz="0.014625194 -0.0008172672 -0.028333545"/>
382
+ <mass value="1.6678109"/>
383
+ <inertia ixx="0.0064480435" ixy="0.00015639093" ixz="0.0012205359" iyy="0.0073372077" iyz="-8.9941532e-05" izz="0.0036738448"/>
384
+ </inertial>
385
+ <visual>
386
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
387
+ <geometry>
388
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/ShoulderYaw.STL" scale="0.001 0.001 0.001"/>
389
+ </geometry>
390
+ <material name="dark_grey"/>
391
+ </visual>
392
+ <collision>
393
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
394
+ <geometry>
395
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/ShoulderYaw.STL" scale="0.001 0.001 0.001"/>
396
+ </geometry>
397
+ </collision>
398
+ </link>
399
+ <!-- /shoulder-elbow -->
400
+ <!-- elbow yaw-pitch -->
401
+ <link name="arm2_4">
402
+ <inertial>
403
+ <origin xyz="-0.0076833067 0.040302205 -0.043492779"/>
404
+ <mass value="1.3157289"/>
405
+ <inertia ixx="0.004330394" ixy="0.00011737391" ixz="-0.00041923199" iyy="0.0038539919" iyz="0.00079573038" izz="0.0017594689"/>
406
+ </inertial>
407
+ <visual>
408
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
409
+ <geometry>
410
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/Elbow.STL" scale="0.001 0.001 0.001"/>
411
+ </geometry>
412
+ </visual>
413
+ <collision>
414
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
415
+ <geometry>
416
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/Elbow.STL" scale="0.001 0.001 0.001"/>
417
+ </geometry>
418
+ </collision>
419
+ </link>
420
+ <!-- /elbow yaw-pitch -->
421
+ <!-- elbow-wrist -->
422
+ <link name="arm2_5">
423
+ <inertial>
424
+ <origin xyz="-0.00011079615 -0.011590836 -0.07816026"/>
425
+ <mass value="1.4908547"/>
426
+ <inertia ixx="0.0085692128" ixy="-1.7856252e-05" ixz="1.9379365e-05" iyy="0.0077454159" iyz="0.00032860094" izz="0.0027441921"/>
427
+ </inertial>
428
+ <visual>
429
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
430
+ <geometry>
431
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/Forearm.STL" scale="0.001 0.001 0.001"/>
432
+ </geometry>
433
+ <material name="dark_grey"/>
434
+ </visual>
435
+ <collision>
436
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
437
+ <geometry>
438
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/Forearm.STL" scale="0.001 0.001 0.001"/>
439
+ </geometry>
440
+ </collision>
441
+ </link>
442
+ <!-- /elbow-wrist -->
443
+ <!-- wrist yaw-pitch -->
444
+ <link name="arm2_6">
445
+ <inertial>
446
+ <origin xyz="-4.6502396e-06 0.038014094 -0.069926878"/>
447
+ <mass value="1.1263612"/>
448
+ <inertia ixx="0.0051871784" ixy="-2.724437e-05" ixz="2.2833496e-06" iyy="0.0048037789" iyz="0.00072165653" izz="0.0012771388"/>
449
+ </inertial>
450
+ <visual>
451
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
452
+ <geometry>
453
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/wrist_pitch_right.stl" scale="0.001 0.001 0.001"/>
454
+ </geometry>
455
+ </visual>
456
+ <collision>
457
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
458
+ <geometry>
459
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/wrist_pitch_right.stl" scale="0.001 0.001 0.001"/>
460
+ </geometry>
461
+ </collision>
462
+ </link>
463
+ <!-- /wrist yaw-pitch -->
464
+ <!-- <link name="arm${arm_num}_7">
465
+ <inertial>
466
+ <origin xyz="${Wrist_3_x} ${rot*Wrist_3_y} ${Wrist_3_z}"/>
467
+ <mass value="${Wrist_3_mass}"/>
468
+ <inertia ixx="${Wrist_3_xx}" ixy="${rot*Wrist_3_xy}" ixz="${Wrist_3_xz}" iyy="${Wrist_3_yy}" iyz="${rot*Wrist_3_yz}" izz="${Wrist_3_zz}"/>
469
+ </inertial>
470
+
471
+ <visual>
472
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
473
+ <geometry>
474
+ <mesh filename="${MESH_PATH}/ForearmYaw.STL" scale="0.001 ${-rot*0.001} 0.001" />
475
+ </geometry>
476
+ <material name="dark_grey"/>
477
+ </visual>
478
+
479
+ <collision>
480
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
481
+ <geometry>
482
+ <mesh filename="${MESH_PATH}/simple/ForearmYaw.STL" scale="0.001 ${-rot*0.001} 0.001" />
483
+ </geometry>
484
+ </collision>
485
+ </link> -->
486
+ <!-- /LINKS -->
487
+ <!-- JOINTS -->
488
+ <!-- shoulder yaw -->
489
+ <joint name="j_arm2_1" type="revolute">
490
+ <parent link="torso_2"/>
491
+ <child link="arm2_1"/>
492
+ <origin rpy="-0.174533 0.0 0.5235988" xyz="0.0457475 -0.169137 0.116626"/>
493
+ <axis xyz="0 1 0"/>
494
+ <limit effort="147.0" lower="-3.3458" upper="1.6012" velocity="3.86"/>
495
+ </joint>
496
+ <!-- /shoulder yaw -->
497
+ <!-- shoulder roll -->
498
+ <joint name="j_arm2_2" type="revolute">
499
+ <parent link="arm2_1"/>
500
+ <child link="arm2_2"/>
501
+ <origin rpy="0.1745327 0.0 0.0" xyz="-0.09015 -0.062 0.0"/>
502
+ <axis xyz="1 0 0"/>
503
+ <limit effort="147.0" lower="-3.4258" upper="-0.0138" velocity="3.86"/>
504
+ </joint>
505
+ <!-- /shoulder roll -->
506
+ <!-- shoulder pitch-->
507
+ <joint name="j_arm2_3" type="revolute">
508
+ <parent link="arm2_2"/>
509
+ <child link="arm2_3"/>
510
+ <origin xyz="0.09015 0.0 -0.21815"/>
511
+ <axis xyz="0 0 1"/>
512
+ <limit effort="147.0" lower="-2.5614" upper="2.5606" velocity="6.06"/>
513
+ </joint>
514
+ <!-- /shoulder pitch -->
515
+ <!-- elbow yaw -->
516
+ <joint name="j_arm2_4" type="revolute">
517
+ <parent link="arm2_3"/>
518
+ <child link="arm2_4"/>
519
+ <origin xyz="0.045 -0.05515 -0.074"/>
520
+ <axis xyz="0 1 0"/>
521
+ <limit effort="147.0" lower="-2.4794" upper="0.2886" velocity="6.06"/>
522
+ </joint>
523
+ <!-- /elbow yaw -->
524
+ <!-- elbow pitch-->
525
+ <joint name="j_arm2_5" type="revolute">
526
+ <parent link="arm2_4"/>
527
+ <child link="arm2_5"/>
528
+ <origin xyz="-0.015 0.05515 -0.095"/>
529
+ <axis xyz="0 0 1"/>
530
+ <limit effort="55.0" lower="-2.5394" upper="2.5546" velocity="11.72"/>
531
+ </joint>
532
+ <!-- /elbow pitch-->
533
+ <!-- wrist yaw-->
534
+ <joint name="j_arm2_6" type="revolute">
535
+ <parent link="arm2_5"/>
536
+ <child link="arm2_6"/>
537
+ <origin xyz="0.0 -0.049 -0.156"/>
538
+ <axis xyz="0 1 0"/>
539
+ <limit effort="55.0" lower="-1.5154" upper="1.5156" velocity="11.72"/>
540
+ </joint>
541
+ <!-- /wrist yaw-->
542
+ <!-- wrist pitch-->
543
+ <!-- <joint name="j_arm${arm_num}_7" type="fixed">
544
+ <parent link="arm${arm_num}_6"/>
545
+ <child link="arm${arm_num}_7"/>
546
+ <origin xyz="${Wrist_3_Ox} ${rot*Wrist_3_Oy} ${Wrist_3_Oz}"/>
547
+ <axis xyz="0 0 1"/>
548
+ <limit lower="${j_arm_7_lower[arm_num-1]}" upper="${j_arm_7_upper[arm_num-1]}" effort="${j_arm_7_torque[arm_num-1]}" velocity="${j_arm_7_velocity[arm_num-1]}"/>
549
+ </joint> -->
550
+ <!-- wrist pitch-->
551
+ <!-- force-troque sensor -->
552
+ <!-- end-effector -->
553
+ <!-- wrist pitch-->
554
+ <!-- <joint name="j_ft_${arm_num}" type="fixed">
555
+ <parent link="arm${arm_num}_7"/>
556
+ <child link="ft_arm${arm_num}"/>
557
+ <origin xyz="${Ft_arm_Ox} ${Ft_arm_Oy} ${Ft_arm_Oz}" rpy="${Ft_arm_roll} ${Ft_arm_pitch} ${Ft_arm_yaw}" />
558
+ <axis xyz="0 0 1"/>
559
+ <limit lower="0.0" upper="0.0" effort="0" velocity="0"/>
560
+ </joint> -->
561
+ <!-- wrist pitch-->
562
+ <!-- <link name="ft_arm${arm_num}">
563
+ <inertial>
564
+ <origin xyz="${Ft_arm_x} ${rot*Ft_arm_y} ${Ft_arm_z}"/>
565
+ <mass value="${Ft_arm_mass}"/>
566
+ <inertia ixx="${Ft_arm_xx}" ixy="${rot*Ft_arm_xy}" ixz="${Ft_arm_xz}" iyy="${Ft_arm_yy}" iyz="${rot*Ft_arm_yz}" izz="${Ft_arm_zz}"/>
567
+ </inertial> -->
568
+ <!--visual>
569
+ <origin xyz="0.0 0.0 0.0" rpy="${PI} 0.0 0.0"/>
570
+ <geometry>
571
+ <mesh filename="${MESH_PATH}/Wrist.STL" scale="0.001 ${-rot*0.001} 0.001" />
572
+ </geometry>
573
+ </visual-->
574
+ <!--collision>
575
+ <origin xyz="0.0 0.0 0.0" rpy="${PI} 0.0 0.0"/>
576
+ <geometry>
577
+ <mesh filename="${MESH_PATH}/simple/Wrist.STL" scale="0.001 ${-rot*0.001} 0.001" />
578
+ </geometry>
579
+ </collision-->
580
+ <!-- </link> -->
581
+ <link name="ball1">
582
+ <inertial>
583
+ <origin rpy="0 0 0" xyz="-1.0072498e-05 3.7590658e-05 0.019772332"/>
584
+ <mass value="1.27"/>
585
+ <inertia ixx="0.0023061092" ixy="2.9181758e-07" ixz="-2.1246683999999997e-07" iyy="0.0023053155000000002" iyz="7.9290978e-07" izz="0.0030675615999999997"/>
586
+ </inertial>
587
+ <visual>
588
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
589
+ <geometry name="ball1_visual">
590
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/BallHand.stl" scale="0.001 0.001 0.001"/>
591
+ </geometry>
592
+ <material name="dark_grey"/>
593
+ </visual>
594
+ <collision>
595
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
596
+ <geometry name="ball1_collision">
597
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/BallHand.stl" scale="0.001 0.001 0.001"/>
598
+ </geometry>
599
+ </collision>
600
+ </link>
601
+ <link name="ball1_tip"/>
602
+ <joint name="j_arm1_8" type="fixed">
603
+ <parent link="arm1_6"/>
604
+ <child link="ball1"/>
605
+ <origin rpy="0. 0. 0." xyz="0. -0.05 -0.105"/>
606
+ </joint>
607
+ <joint name="j_ball1_tip" type="fixed">
608
+ <parent link="ball1"/>
609
+ <child link="ball1_tip"/>
610
+ <origin rpy="0 0 0" xyz="0 0 -0.07"/>
611
+ </joint>
612
+ <!-- add armX_8 link for legacy reasons (same as ballX) -->
613
+ <joint name="j_ball1_fixed" type="fixed">
614
+ <parent link="ball1"/>
615
+ <child link="arm1_8"/>
616
+ </joint>
617
+ <link name="arm1_8">
618
+ <inertial>
619
+ <mass value="0"/>
620
+ <inertia ixx="0" ixy="0" ixz="0" iyy="0" iyz="0" izz="0"/>
621
+ </inertial>
622
+ </link>
623
+ <link name="ball2">
624
+ <inertial>
625
+ <origin rpy="0 0 0" xyz="-1.0072498e-05 3.7590658e-05 0.019772332"/>
626
+ <mass value="1.27"/>
627
+ <inertia ixx="0.0023061092" ixy="2.9181758e-07" ixz="-2.1246683999999997e-07" iyy="0.0023053155000000002" iyz="7.9290978e-07" izz="0.0030675615999999997"/>
628
+ </inertial>
629
+ <visual>
630
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
631
+ <geometry name="ball2_visual">
632
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/BallHand.stl" scale="0.001 0.001 0.001"/>
633
+ </geometry>
634
+ <material name="dark_grey"/>
635
+ </visual>
636
+ <collision>
637
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
638
+ <geometry name="ball2_collision">
639
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/BallHand.stl" scale="0.001 0.001 0.001"/>
640
+ </geometry>
641
+ </collision>
642
+ </link>
643
+ <link name="ball2_tip"/>
644
+ <joint name="j_arm2_8" type="fixed">
645
+ <parent link="arm2_6"/>
646
+ <child link="ball2"/>
647
+ <origin rpy="0. 0. 0." xyz="0. 0.05 -0.105"/>
648
+ </joint>
649
+ <joint name="j_ball2_tip" type="fixed">
650
+ <parent link="ball2"/>
651
+ <child link="ball2_tip"/>
652
+ <origin rpy="0 0 0" xyz="0 0 -0.07"/>
653
+ </joint>
654
+ <!-- add armX_8 link for legacy reasons (same as ballX) -->
655
+ <joint name="j_ball2_fixed" type="fixed">
656
+ <parent link="ball2"/>
657
+ <child link="arm2_8"/>
658
+ </joint>
659
+ <link name="arm2_8">
660
+ <inertial>
661
+ <mass value="0"/>
662
+ <inertia ixx="0" ixy="0" ixz="0" iyy="0" iyz="0" izz="0"/>
663
+ </inertial>
664
+ </link>
665
+ <!-- LINKS -->
666
+ <link name="neck_1">
667
+ <inertial>
668
+ <origin xyz="0 0 0"/>
669
+ <mass value="0.33878686"/>
670
+ <!-- MASS/INERTIA TO DO -->
671
+ <inertia ixx="0.0003718568" ixy="-9.42983e-05" ixz="-2.80644e-05" iyy="0.0001886879" iyz="-1.54534e-05" izz="0.0005023443"/>
672
+ </inertial>
673
+ <visual>
674
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
675
+ <geometry>
676
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/head-base_mesh.stl" scale="0.001 0.001 0.001"/>
677
+ </geometry>
678
+ </visual>
679
+ <collision>
680
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
681
+ <geometry>
682
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/head-base_mesh.stl" scale="0.001 0.001 0.001"/>
683
+ </geometry>
684
+ </collision>
685
+ </link>
686
+ <link name="velodyne_motor">
687
+ <inertial>
688
+ <origin xyz="0 0 0"/>
689
+ <mass value="0.33878686"/>
690
+ <!-- MASS/INERTIA TO DO -->
691
+ <inertia ixx="0.0003718568" ixy="-9.42983e-05" ixz="-2.80644e-05" iyy="0.0001886879" iyz="-1.54534e-05" izz="0.0005023443"/>
692
+ </inertial>
693
+ <visual>
694
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
695
+ <geometry>
696
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/lidar_mesh.stl" scale="0.001 0.001 0.001"/>
697
+ </geometry>
698
+ </visual>
699
+ <collision>
700
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
701
+ <geometry>
702
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/lidar_mesh.stl" scale="0.001 0.001 0.001"/>
703
+ </geometry>
704
+ </collision>
705
+ </link>
706
+ <link name="d435_head_motor">
707
+ <inertial>
708
+ <origin xyz="0 0 0"/>
709
+ <mass value="0.33878686"/>
710
+ <!-- MASS/INERTIA TO DO -->
711
+ <inertia ixx="0.0003718568" ixy="-9.42983e-05" ixz="-2.80644e-05" iyy="0.0001886879" iyz="-1.54534e-05" izz="0.0005023443"/>
712
+ </inertial>
713
+ <visual>
714
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
715
+ <geometry>
716
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/neck-pitch_mesh.stl" scale="0.001 0.001 0.001"/>
717
+ </geometry>
718
+ </visual>
719
+ <collision>
720
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
721
+ <geometry>
722
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/neck-pitch_mesh.stl" scale="0.001 0.001 0.001"/>
723
+ </geometry>
724
+ </collision>
725
+ </link>
726
+ <!--JOINTS -->
727
+ <joint name="neck_base" type="fixed">
728
+ <parent link="torso_2"/>
729
+ <origin rpy="0 0 0" xyz="0.0030637045 0.0061607221 0.158"/>
730
+ <child link="neck_1"/>
731
+ <axis xyz="0 0 1"/>
732
+ </joint>
733
+ <joint name="velodyne_joint" type="revolute">
734
+ <parent link="neck_1"/>
735
+ <origin rpy="0 0 0" xyz="0.0822724 0 0.242"/>
736
+ <child link="velodyne_motor"/>
737
+ <axis xyz="0 -1 0"/>
738
+ <limit effort="35" lower="-1.1" upper="1.1" velocity="5.7"/>
739
+ <dynamics friction="10.0"/>
740
+ </joint>
741
+ <joint name="d435_head_joint" type="revolute">
742
+ <parent link="neck_1"/>
743
+ <origin rpy="0 0 0" xyz="0.06 0 0.1375"/>
744
+ <child link="d435_head_motor"/>
745
+ <axis xyz="0 -1 0"/>
746
+ <limit effort="35" lower="-1.1" upper="1.1" velocity="5.7"/>
747
+ <dynamics friction="10.0"/>
748
+ </joint>
749
+ <!-- camera body, with origin at bottom screw mount -->
750
+ <joint name="D435_head_camera_joint" type="fixed">
751
+ <origin rpy="0 0 0" xyz="0.03015 0 -0.0125"/>
752
+ <parent link="d435_head_motor"/>
753
+ <child link="D435_head_camera_bottom_screw_frame"/>
754
+ </joint>
755
+ <link name="D435_head_camera_bottom_screw_frame"/>
756
+ <joint name="D435_head_camera_link_joint" type="fixed">
757
+ <origin rpy="0 0 0" xyz="0 0.0175 0.0125"/>
758
+ <parent link="D435_head_camera_bottom_screw_frame"/>
759
+ <child link="D435_head_camera_link"/>
760
+ </joint>
761
+ <link name="D435_head_camera_link">
762
+ <visual>
763
+ <origin rpy="1.570796326795 0 1.570796326795" xyz="0.0149 -0.0175 0"/>
764
+ <geometry>
765
+ <box size="0.09 0.025 0.02505"/>
766
+ <!-- <mesh filename="${MESH_PATH}/realsense/d435.dae"/> -->
767
+ </geometry>
768
+ <material name="D435_head_camera_aluminum"/>
769
+ </visual>
770
+ <collision>
771
+ <origin rpy="0 0 0" xyz="0 -0.0175 0"/>
772
+ <geometry>
773
+ <box size="0.02505 0.09 0.025"/>
774
+ </geometry>
775
+ </collision>
776
+ <inertial>
777
+ <!-- The following are not reliable values, and should not be used for modeling -->
778
+ <mass value="0.564"/>
779
+ <origin xyz="0 0 0"/>
780
+ <inertia ixx="0.003881243" ixy="0.0" ixz="0.0" iyy="0.000498940" iyz="0.0" izz="0.003879257"/>
781
+ </inertial>
782
+ </link>
783
+ <!-- camera body, with origin at bottom screw mount -->
784
+ <joint name="D435i_camera_joint" type="fixed">
785
+ <origin rpy="0 0.54 0" xyz="0.272 0 0.084"/>
786
+ <parent link="pelvis"/>
787
+ <child link="D435i_camera_bottom_screw_frame"/>
788
+ </joint>
789
+ <link name="D435i_camera_bottom_screw_frame"/>
790
+ <joint name="D435i_camera_link_joint" type="fixed">
791
+ <origin rpy="0 0.0050 -0.008" xyz="0.053 0.0 -0.035"/>
792
+ <parent link="D435i_camera_bottom_screw_frame"/>
793
+ <child link="D435i_camera_link"/>
794
+ </joint>
795
+ <link name="D435i_camera_link">
796
+ <visual>
797
+ <origin rpy="1.570796326795 0 1.570796326795" xyz="0.053 -0.0 0"/>
798
+ <geometry>
799
+ <!-- <box size="${d435_cam_width} ${d435_cam_height} ${d435_cam_depth}"/> -->
800
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/realsense/d435.dae"/>
801
+ </geometry>
802
+ <material name="D435i_camera_aluminum"/>
803
+ </visual>
804
+ <collision>
805
+ <origin rpy="0 0 0" xyz="0 -0.0 0"/>
806
+ <geometry>
807
+ <box size="0.02505 0.09 0.025"/>
808
+ </geometry>
809
+ </collision>
810
+ <inertial>
811
+ <!-- The following are not reliable values, and should not be used for modeling -->
812
+ <mass value="0.564"/>
813
+ <origin xyz="0 0 0"/>
814
+ <inertia ixx="0.003881243" ixy="0.0" ixz="0.0" iyy="0.000498940" iyz="0.0" izz="0.003879257"/>
815
+ </inertial>
816
+ </link>
817
+ <!-- /macro centauro_wheel -->
818
+ <!-- /macro arms -->
819
+ <!-- /macro legs -->
820
+ <link name="hip1_1">
821
+ <inertial>
822
+ <origin xyz="0.0003595855 0.033338818000000006 -0.059190309"/>
823
+ <mass value="2.3349153"/>
824
+ <!-- org 2 -->
825
+ <inertia ixx="0.0068405374" ixy="1.8572633e-05" ixz="-4.8325972e-05" iyy="0.0026261815" iyz="0.0001564273" izz="0.0066933354"/>
826
+ </inertial>
827
+ <visual>
828
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
829
+ <geometry>
830
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-yaw.stl" scale="0.001 -0.001 0.001"/>
831
+ </geometry>
832
+ <material name="dark_grey"/>
833
+ </visual>
834
+ <collision>
835
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
836
+ <geometry>
837
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-yaw.stl" scale="0.001 -0.001 0.001"/>
838
+ </geometry>
839
+ </collision>
840
+ </link>
841
+ <link name="hip2_1">
842
+ <inertial>
843
+ <origin xyz="-4.6285952e-06 -0.2214019 0.03316685"/>
844
+ <mass value="3.4572118"/>
845
+ <!-- org 4.5 -->
846
+ <inertia ixx="0.050469853" ixy="-7.784938699999999e-06" ixz="-9.220580999999999e-06" iyy="0.008181596999999999" iyz="0.0059796848" izz="0.048213808999999996"/>
847
+ </inertial>
848
+ <visual>
849
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
850
+ <geometry>
851
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-pitch.stl" scale="0.001 0.001 -0.001"/>
852
+ </geometry>
853
+ </visual>
854
+ <collision>
855
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
856
+ <geometry>
857
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-pitch.stl" scale="0.001 0.001 -0.001"/>
858
+ </geometry>
859
+ </collision>
860
+ </link>
861
+ <link name="knee_1">
862
+ <inertial>
863
+ <origin xyz="0.00012063381 0.14620674 0.029740711000000003"/>
864
+ <mass value="2.1199315"/>
865
+ <!-- org 3.5, foot un modeled-->
866
+ <inertia ixx="0.018426402999999997" ixy="2.8226635999999997e-05" ixz="-2.9204348e-05" iyy="0.0047685615" iyz="-0.002640771" izz="0.017793722"/>
867
+ </inertial>
868
+ <visual>
869
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
870
+ <geometry>
871
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/knee.stl" scale="0.001 0.001 -0.001"/>
872
+ </geometry>
873
+ <material name="dark_grey"/>
874
+ </visual>
875
+ <collision>
876
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
877
+ <geometry>
878
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/knee.stl" scale="0.001 0.001 -0.001"/>
879
+ </geometry>
880
+ </collision>
881
+ </link>
882
+ <link name="ankle1_1">
883
+ <inertial>
884
+ <origin xyz="0.00012360094 -0.06274712 -0.0049068453"/>
885
+ <mass value="1.4023368"/>
886
+ <!-- org 3.5, foot un modeled-->
887
+ <inertia ixx="0.0049928829" ixy="5.762355699999999e-06" ixz="1.9606394e-05" iyy="0.0017621464" iyz="0.00045315091" izz="0.0048706328"/>
888
+ </inertial>
889
+ <visual>
890
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
891
+ <geometry>
892
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/ankle-pitch.stl" scale="0.001 0.001 -0.001"/>
893
+ </geometry>
894
+ </visual>
895
+ <collision>
896
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
897
+ <geometry>
898
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/ankle-pitch.stl" scale="0.001 0.001 -0.001"/>
899
+ </geometry>
900
+ </collision>
901
+ </link>
902
+ <link name="ankle2_1">
903
+ <inertial>
904
+ <origin xyz="0.00018512273 0.0017369405000000001 -0.091340683"/>
905
+ <mass value="2.440929"/>
906
+ <!-- org 3.5, foot un modeled-->
907
+ <inertia ixx="0.017711499" ixy="1.0713141e-05" ixz="-2.2094697e-05" iyy="0.014263485" iyz="0.00033426702" izz="0.0080747557"/>
908
+ </inertial>
909
+ <visual>
910
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
911
+ <geometry>
912
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_ankle-yaw_wheel23.stl" scale="0.001 -0.001 0.001"/>
913
+ </geometry>
914
+ <material name="dark_grey"/>
915
+ </visual>
916
+ <!-- <collision>
917
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
918
+ <geometry>
919
+ <mesh
920
+ filename="${MESH_PATH}/v2/collision/mesh_ankle-yaw_wheel23.stl"
921
+ scale="0.001 ${mirror*0.001} 0.001" />
922
+ </geometry>
923
+ </collision> -->
924
+ </link>
925
+ <joint name="j_wheel_1" type="continuous">
926
+ <parent link="ankle2_1"/>
927
+ <child link="wheel_1"/>
928
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 -0.0 -0.14435"/>
929
+ <axis xyz="0 0 -1"/>
930
+ <limit effort="35" velocity="20.0"/>
931
+ <!-- 23 Nm, 1
932
+ rad/s-->
933
+ </joint>
934
+ <link name="wheel_1">
935
+ <inertial>
936
+ <origin xyz="0.0 0.0 0.00015403767"/>
937
+ <mass value="1.8569388"/>
938
+ <inertia ixx="0.008637359099999999" ixy="0.0" ixz="-0.0" iyy="0.0086373644" iyz="-0.0" izz="0.016376551"/>
939
+ </inertial>
940
+ <visual>
941
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
942
+ <geometry>
943
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_wheel23.stl" scale="0.001 0.001 -0.001"/>
944
+ </geometry>
945
+ <material name="black"/>
946
+ </visual>
947
+ <collision>
948
+ <origin rpy="0 0 0" xyz="0 0 0"/>
949
+ <geometry>
950
+ <cylinder length="0.03" radius="0.124"/>
951
+ </geometry>
952
+ <!-- <geometry>
953
+ <mesh filename="${MESH_PATH}/v2/collision/wheel23_donut.stl"
954
+ scale="0.001 0.001 ${mirror*0.001}" />
955
+ </geometry> -->
956
+ <material name="black"/>
957
+ </collision>
958
+ </link>
959
+ <!-- *********** CONTACT FRAME ******************-->
960
+ <link name="contact_1"/>
961
+ <joint name="contact_joint_1" type="fixed">
962
+ <parent link="ankle2_1"/>
963
+ <child link="contact_1"/>
964
+ <origin rpy="0 0 0" xyz="0 0 -0.26835"/>
965
+ </joint>
966
+ <!-- ******************************** -->
967
+ <joint name="hip_yaw_1" type="revolute">
968
+ <parent link="pelvis"/>
969
+ <child link="hip1_1"/>
970
+ <origin rpy="0.0 0.0 0.0" xyz="0.25 0.125 -0.056600000000000004"/>
971
+ <axis xyz="0 0 -1"/>
972
+ <limit effort="304" lower="-2.03" upper="2.51" velocity="8.8"/>
973
+ <!-- 200 Nm, 1 rad/s-->
974
+ </joint>
975
+ <joint name="hip_pitch_1" type="revolute">
976
+ <parent link="hip1_1"/>
977
+ <child link="hip2_1"/>
978
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 0.1146 -0.0625"/>
979
+ <axis xyz="0 0 -1"/>
980
+ <limit effort="304" lower="-2.0226" upper="2.02748" velocity="8.8"/>
981
+ <!-- 200 Nm, 1 rad/s-->
982
+ </joint>
983
+ <joint name="knee_pitch_1" type="revolute">
984
+ <parent link="hip2_1"/>
985
+ <child link="knee_1"/>
986
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 -0.3 0.1182"/>
987
+ <axis xyz="0 0 -1"/>
988
+ <limit effort="304" lower="-2.4056" upper="2.3994" velocity="8.8"/>
989
+ <!-- 200 Nm, 1 rad/s-->
990
+ </joint>
991
+ <joint name="ankle_pitch_1" type="revolute">
992
+ <parent link="knee_1"/>
993
+ <child link="ankle1_1"/>
994
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 0.2 0.10015"/>
995
+ <axis xyz="0 0 -1"/>
996
+ <limit effort="147" lower="-2.4056" upper="2.3184" velocity="8.1"/>
997
+ <!-- 98 Nm, 1 rad/s-->
998
+ </joint>
999
+ <joint name="ankle_yaw_1" type="revolute">
1000
+ <parent link="ankle1_1"/>
1001
+ <child link="ankle2_1"/>
1002
+ <origin rpy="-1.57079632679 0.0 0.0" xyz="0.0 -0.139 -0.00085"/>
1003
+ <axis xyz="0 0 -1"/>
1004
+ <limit effort="35" lower="-2.5626" upper="2.5384" velocity="20.0"/>
1005
+ <!-- 23 Nm, 1 rad/s-->
1006
+ </joint>
1007
+ <link name="hip1_2">
1008
+ <inertial>
1009
+ <origin xyz="0.0003595855 -0.033338818000000006 -0.059190309"/>
1010
+ <mass value="2.3349153"/>
1011
+ <!-- org 2 -->
1012
+ <inertia ixx="0.0068405374" ixy="-1.8572633e-05" ixz="-4.8325972e-05" iyy="0.0026261815" iyz="-0.0001564273" izz="0.0066933354"/>
1013
+ </inertial>
1014
+ <visual>
1015
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1016
+ <geometry>
1017
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-yaw.stl" scale="0.001 0.001 0.001"/>
1018
+ </geometry>
1019
+ <material name="dark_grey"/>
1020
+ </visual>
1021
+ <collision>
1022
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1023
+ <geometry>
1024
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-yaw.stl" scale="0.001 0.001 0.001"/>
1025
+ </geometry>
1026
+ </collision>
1027
+ </link>
1028
+ <link name="hip2_2">
1029
+ <inertial>
1030
+ <origin xyz="-4.6285952e-06 -0.2214019 -0.03316685"/>
1031
+ <mass value="3.4572118"/>
1032
+ <!-- org 4.5 -->
1033
+ <inertia ixx="0.050469853" ixy="-7.784938699999999e-06" ixz="9.220580999999999e-06" iyy="0.008181596999999999" iyz="-0.0059796848" izz="0.048213808999999996"/>
1034
+ </inertial>
1035
+ <visual>
1036
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1037
+ <geometry>
1038
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-pitch.stl" scale="0.001 0.001 0.001"/>
1039
+ </geometry>
1040
+ </visual>
1041
+ <collision>
1042
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1043
+ <geometry>
1044
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-pitch.stl" scale="0.001 0.001 0.001"/>
1045
+ </geometry>
1046
+ </collision>
1047
+ </link>
1048
+ <link name="knee_2">
1049
+ <inertial>
1050
+ <origin xyz="0.00012063381 0.14620674 -0.029740711000000003"/>
1051
+ <mass value="2.1199315"/>
1052
+ <!-- org 3.5, foot un modeled-->
1053
+ <inertia ixx="0.018426402999999997" ixy="2.8226635999999997e-05" ixz="2.9204348e-05" iyy="0.0047685615" iyz="0.002640771" izz="0.017793722"/>
1054
+ </inertial>
1055
+ <visual>
1056
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1057
+ <geometry>
1058
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/knee.stl" scale="0.001 0.001 0.001"/>
1059
+ </geometry>
1060
+ <material name="dark_grey"/>
1061
+ </visual>
1062
+ <collision>
1063
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1064
+ <geometry>
1065
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/knee.stl" scale="0.001 0.001 0.001"/>
1066
+ </geometry>
1067
+ </collision>
1068
+ </link>
1069
+ <link name="ankle1_2">
1070
+ <inertial>
1071
+ <origin xyz="0.00012360094 -0.06274712 0.0049068453"/>
1072
+ <mass value="1.4023368"/>
1073
+ <!-- org 3.5, foot un modeled-->
1074
+ <inertia ixx="0.0049928829" ixy="5.762355699999999e-06" ixz="-1.9606394e-05" iyy="0.0017621464" iyz="-0.00045315091" izz="0.0048706328"/>
1075
+ </inertial>
1076
+ <visual>
1077
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1078
+ <geometry>
1079
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/ankle-pitch.stl" scale="0.001 0.001 0.001"/>
1080
+ </geometry>
1081
+ </visual>
1082
+ <collision>
1083
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1084
+ <geometry>
1085
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/ankle-pitch.stl" scale="0.001 0.001 0.001"/>
1086
+ </geometry>
1087
+ </collision>
1088
+ </link>
1089
+ <link name="ankle2_2">
1090
+ <inertial>
1091
+ <origin xyz="0.00018512273 -0.0017369405000000001 -0.091340683"/>
1092
+ <mass value="2.440929"/>
1093
+ <!-- org 3.5, foot un modeled-->
1094
+ <inertia ixx="0.017711499" ixy="-1.0713141e-05" ixz="-2.2094697e-05" iyy="0.014263485" iyz="-0.00033426702" izz="0.0080747557"/>
1095
+ </inertial>
1096
+ <visual>
1097
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1098
+ <geometry>
1099
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_ankle-yaw_wheel23.stl" scale="0.001 0.001 0.001"/>
1100
+ </geometry>
1101
+ <material name="dark_grey"/>
1102
+ </visual>
1103
+ <!-- <collision>
1104
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
1105
+ <geometry>
1106
+ <mesh
1107
+ filename="${MESH_PATH}/v2/collision/mesh_ankle-yaw_wheel23.stl"
1108
+ scale="0.001 ${mirror*0.001} 0.001" />
1109
+ </geometry>
1110
+ </collision> -->
1111
+ </link>
1112
+ <joint name="j_wheel_2" type="continuous">
1113
+ <parent link="ankle2_2"/>
1114
+ <child link="wheel_2"/>
1115
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 0.0 -0.14435"/>
1116
+ <axis xyz="0 0 1"/>
1117
+ <limit effort="35" velocity="20.0"/>
1118
+ <!-- 23 Nm, 1
1119
+ rad/s-->
1120
+ </joint>
1121
+ <link name="wheel_2">
1122
+ <inertial>
1123
+ <origin xyz="0.0 0.0 -0.00015403767"/>
1124
+ <mass value="1.8569388"/>
1125
+ <inertia ixx="0.008637359099999999" ixy="0.0" ixz="0.0" iyy="0.0086373644" iyz="0.0" izz="0.016376551"/>
1126
+ </inertial>
1127
+ <visual>
1128
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1129
+ <geometry>
1130
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_wheel23.stl" scale="0.001 0.001 0.001"/>
1131
+ </geometry>
1132
+ <material name="black"/>
1133
+ </visual>
1134
+ <collision>
1135
+ <origin rpy="0 0 0" xyz="0 0 0"/>
1136
+ <geometry>
1137
+ <cylinder length="0.03" radius="0.124"/>
1138
+ </geometry>
1139
+ <!-- <geometry>
1140
+ <mesh filename="${MESH_PATH}/v2/collision/wheel23_donut.stl"
1141
+ scale="0.001 0.001 ${mirror*0.001}" />
1142
+ </geometry> -->
1143
+ <material name="black"/>
1144
+ </collision>
1145
+ </link>
1146
+ <!-- *********** CONTACT FRAME ******************-->
1147
+ <link name="contact_2"/>
1148
+ <joint name="contact_joint_2" type="fixed">
1149
+ <parent link="ankle2_2"/>
1150
+ <child link="contact_2"/>
1151
+ <origin rpy="0 0 0" xyz="0 0 -0.26835"/>
1152
+ </joint>
1153
+ <!-- ******************************** -->
1154
+ <joint name="hip_yaw_2" type="revolute">
1155
+ <parent link="pelvis"/>
1156
+ <child link="hip1_2"/>
1157
+ <origin rpy="0.0 0.0 0.0" xyz="0.25 -0.125 -0.056600000000000004"/>
1158
+ <axis xyz="0 0 -1"/>
1159
+ <limit effort="304" lower="-2.51" upper="2.03" velocity="8.8"/>
1160
+ <!-- 200 Nm, 1 rad/s-->
1161
+ </joint>
1162
+ <joint name="hip_pitch_2" type="revolute">
1163
+ <parent link="hip1_2"/>
1164
+ <child link="hip2_2"/>
1165
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 -0.1146 -0.0625"/>
1166
+ <axis xyz="0 0 1"/>
1167
+ <limit effort="304" lower="-2.0156" upper="2.0354" velocity="8.8"/>
1168
+ <!-- 200 Nm, 1 rad/s-->
1169
+ </joint>
1170
+ <joint name="knee_pitch_2" type="revolute">
1171
+ <parent link="hip2_2"/>
1172
+ <child link="knee_2"/>
1173
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 -0.3 -0.1182"/>
1174
+ <axis xyz="0 0 1"/>
1175
+ <limit effort="304" lower="-2.4006" upper="2.4034" velocity="8.8"/>
1176
+ <!-- 200 Nm, 1 rad/s-->
1177
+ </joint>
1178
+ <joint name="ankle_pitch_2" type="revolute">
1179
+ <parent link="knee_2"/>
1180
+ <child link="ankle1_2"/>
1181
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 0.2 -0.10015"/>
1182
+ <axis xyz="0 0 1"/>
1183
+ <limit effort="147" lower="-2.3266" upper="2.3914" velocity="8.1"/>
1184
+ <!-- 98 Nm, 1 rad/s-->
1185
+ </joint>
1186
+ <joint name="ankle_yaw_2" type="revolute">
1187
+ <parent link="ankle1_2"/>
1188
+ <child link="ankle2_2"/>
1189
+ <origin rpy="-1.57079632679 0.0 0.0" xyz="0.0 -0.139 0.00085"/>
1190
+ <axis xyz="0 0 -1"/>
1191
+ <limit effort="35" lower="-2.5546" upper="2.5484" velocity="20.0"/>
1192
+ <!-- 23 Nm, 1 rad/s-->
1193
+ </joint>
1194
+ <link name="hip1_3">
1195
+ <inertial>
1196
+ <origin xyz="0.0003595855 0.033338818000000006 -0.059190309"/>
1197
+ <mass value="2.3349153"/>
1198
+ <!-- org 2 -->
1199
+ <inertia ixx="0.0068405374" ixy="1.8572633e-05" ixz="-4.8325972e-05" iyy="0.0026261815" iyz="0.0001564273" izz="0.0066933354"/>
1200
+ </inertial>
1201
+ <visual>
1202
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1203
+ <geometry>
1204
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-yaw.stl" scale="0.001 -0.001 0.001"/>
1205
+ </geometry>
1206
+ <material name="dark_grey"/>
1207
+ </visual>
1208
+ <collision>
1209
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1210
+ <geometry>
1211
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-yaw.stl" scale="0.001 -0.001 0.001"/>
1212
+ </geometry>
1213
+ </collision>
1214
+ </link>
1215
+ <link name="hip2_3">
1216
+ <inertial>
1217
+ <origin xyz="-4.6285952e-06 -0.2214019 0.03316685"/>
1218
+ <mass value="3.4572118"/>
1219
+ <!-- org 4.5 -->
1220
+ <inertia ixx="0.050469853" ixy="-7.784938699999999e-06" ixz="-9.220580999999999e-06" iyy="0.008181596999999999" iyz="0.0059796848" izz="0.048213808999999996"/>
1221
+ </inertial>
1222
+ <visual>
1223
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1224
+ <geometry>
1225
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-pitch.stl" scale="0.001 0.001 -0.001"/>
1226
+ </geometry>
1227
+ </visual>
1228
+ <collision>
1229
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1230
+ <geometry>
1231
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-pitch.stl" scale="0.001 0.001 -0.001"/>
1232
+ </geometry>
1233
+ </collision>
1234
+ </link>
1235
+ <link name="knee_3">
1236
+ <inertial>
1237
+ <origin xyz="0.00012063381 0.14620674 0.029740711000000003"/>
1238
+ <mass value="2.1199315"/>
1239
+ <!-- org 3.5, foot un modeled-->
1240
+ <inertia ixx="0.018426402999999997" ixy="2.8226635999999997e-05" ixz="-2.9204348e-05" iyy="0.0047685615" iyz="-0.002640771" izz="0.017793722"/>
1241
+ </inertial>
1242
+ <visual>
1243
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1244
+ <geometry>
1245
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/knee.stl" scale="0.001 0.001 -0.001"/>
1246
+ </geometry>
1247
+ <material name="dark_grey"/>
1248
+ </visual>
1249
+ <collision>
1250
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1251
+ <geometry>
1252
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/knee.stl" scale="0.001 0.001 -0.001"/>
1253
+ </geometry>
1254
+ </collision>
1255
+ </link>
1256
+ <link name="ankle1_3">
1257
+ <inertial>
1258
+ <origin xyz="0.00012360094 -0.06274712 -0.0049068453"/>
1259
+ <mass value="1.4023368"/>
1260
+ <!-- org 3.5, foot un modeled-->
1261
+ <inertia ixx="0.0049928829" ixy="5.762355699999999e-06" ixz="1.9606394e-05" iyy="0.0017621464" iyz="0.00045315091" izz="0.0048706328"/>
1262
+ </inertial>
1263
+ <visual>
1264
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1265
+ <geometry>
1266
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/ankle-pitch.stl" scale="0.001 0.001 -0.001"/>
1267
+ </geometry>
1268
+ </visual>
1269
+ <collision>
1270
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1271
+ <geometry>
1272
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/ankle-pitch.stl" scale="0.001 0.001 -0.001"/>
1273
+ </geometry>
1274
+ </collision>
1275
+ </link>
1276
+ <link name="ankle2_3">
1277
+ <inertial>
1278
+ <origin xyz="0.00018512273 0.0017369405000000001 -0.091340683"/>
1279
+ <mass value="2.440929"/>
1280
+ <!-- org 3.5, foot un modeled-->
1281
+ <inertia ixx="0.017711499" ixy="1.0713141e-05" ixz="-2.2094697e-05" iyy="0.014263485" iyz="0.00033426702" izz="0.0080747557"/>
1282
+ </inertial>
1283
+ <visual>
1284
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1285
+ <geometry>
1286
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_ankle-yaw_wheel23.stl" scale="0.001 -0.001 0.001"/>
1287
+ </geometry>
1288
+ <material name="dark_grey"/>
1289
+ </visual>
1290
+ <!-- <collision>
1291
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
1292
+ <geometry>
1293
+ <mesh
1294
+ filename="${MESH_PATH}/v2/collision/mesh_ankle-yaw_wheel23.stl"
1295
+ scale="0.001 ${mirror*0.001} 0.001" />
1296
+ </geometry>
1297
+ </collision> -->
1298
+ </link>
1299
+ <joint name="j_wheel_3" type="continuous">
1300
+ <parent link="ankle2_3"/>
1301
+ <child link="wheel_3"/>
1302
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 -0.0 -0.14435"/>
1303
+ <axis xyz="0 0 -1"/>
1304
+ <limit effort="35" velocity="20.0"/>
1305
+ <!-- 23 Nm, 1
1306
+ rad/s-->
1307
+ </joint>
1308
+ <link name="wheel_3">
1309
+ <inertial>
1310
+ <origin xyz="0.0 0.0 0.00015403767"/>
1311
+ <mass value="1.8569388"/>
1312
+ <inertia ixx="0.008637359099999999" ixy="0.0" ixz="-0.0" iyy="0.0086373644" iyz="-0.0" izz="0.016376551"/>
1313
+ </inertial>
1314
+ <visual>
1315
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1316
+ <geometry>
1317
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_wheel23.stl" scale="0.001 0.001 -0.001"/>
1318
+ </geometry>
1319
+ <material name="black"/>
1320
+ </visual>
1321
+ <collision>
1322
+ <origin rpy="0 0 0" xyz="0 0 0"/>
1323
+ <geometry>
1324
+ <cylinder length="0.03" radius="0.124"/>
1325
+ </geometry>
1326
+ <!-- <geometry>
1327
+ <mesh filename="${MESH_PATH}/v2/collision/wheel23_donut.stl"
1328
+ scale="0.001 0.001 ${mirror*0.001}" />
1329
+ </geometry> -->
1330
+ <material name="black"/>
1331
+ </collision>
1332
+ </link>
1333
+ <!-- *********** CONTACT FRAME ******************-->
1334
+ <link name="contact_3"/>
1335
+ <joint name="contact_joint_3" type="fixed">
1336
+ <parent link="ankle2_3"/>
1337
+ <child link="contact_3"/>
1338
+ <origin rpy="0 0 0" xyz="0 0 -0.26835"/>
1339
+ </joint>
1340
+ <!-- ******************************** -->
1341
+ <joint name="hip_yaw_3" type="revolute">
1342
+ <parent link="pelvis"/>
1343
+ <child link="hip1_3"/>
1344
+ <origin rpy="0.0 0.0 0.0" xyz="-0.25 0.125 -0.056600000000000004"/>
1345
+ <axis xyz="0 0 -1"/>
1346
+ <limit effort="304" lower="-2.51" upper="2.03" velocity="8.8"/>
1347
+ <!-- 200 Nm, 1 rad/s-->
1348
+ </joint>
1349
+ <joint name="hip_pitch_3" type="revolute">
1350
+ <parent link="hip1_3"/>
1351
+ <child link="hip2_3"/>
1352
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 0.1146 -0.0625"/>
1353
+ <axis xyz="0 0 -1"/>
1354
+ <limit effort="304" lower="-2.0126" upper="2.0384" velocity="8.8"/>
1355
+ <!-- 200 Nm, 1 rad/s-->
1356
+ </joint>
1357
+ <joint name="knee_pitch_3" type="revolute">
1358
+ <parent link="hip2_3"/>
1359
+ <child link="knee_3"/>
1360
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 -0.3 0.1182"/>
1361
+ <axis xyz="0 0 -1"/>
1362
+ <limit effort="304" lower="-2.4056" upper="2.4064" velocity="8.8"/>
1363
+ <!-- 200 Nm, 1 rad/s-->
1364
+ </joint>
1365
+ <joint name="ankle_pitch_3" type="revolute">
1366
+ <parent link="knee_3"/>
1367
+ <child link="ankle1_3"/>
1368
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 0.2 0.10015"/>
1369
+ <axis xyz="0 0 -1"/>
1370
+ <limit effort="147" lower="-2.4696" upper="2.4464" velocity="8.1"/>
1371
+ <!-- 98 Nm, 1 rad/s-->
1372
+ </joint>
1373
+ <joint name="ankle_yaw_3" type="revolute">
1374
+ <parent link="ankle1_3"/>
1375
+ <child link="ankle2_3"/>
1376
+ <origin rpy="-1.57079632679 0.0 0.0" xyz="0.0 -0.139 -0.00085"/>
1377
+ <axis xyz="0 0 -1"/>
1378
+ <limit effort="35" lower="-2.5606" upper="2.5454" velocity="20.0"/>
1379
+ <!-- 23 Nm, 1 rad/s-->
1380
+ </joint>
1381
+ <link name="hip1_4">
1382
+ <inertial>
1383
+ <origin xyz="0.0003595855 -0.033338818000000006 -0.059190309"/>
1384
+ <mass value="2.3349153"/>
1385
+ <!-- org 2 -->
1386
+ <inertia ixx="0.0068405374" ixy="-1.8572633e-05" ixz="-4.8325972e-05" iyy="0.0026261815" iyz="-0.0001564273" izz="0.0066933354"/>
1387
+ </inertial>
1388
+ <visual>
1389
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1390
+ <geometry>
1391
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-yaw.stl" scale="0.001 0.001 0.001"/>
1392
+ </geometry>
1393
+ <material name="dark_grey"/>
1394
+ </visual>
1395
+ <collision>
1396
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1397
+ <geometry>
1398
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-yaw.stl" scale="0.001 0.001 0.001"/>
1399
+ </geometry>
1400
+ </collision>
1401
+ </link>
1402
+ <link name="hip2_4">
1403
+ <inertial>
1404
+ <origin xyz="-4.6285952e-06 -0.2214019 -0.03316685"/>
1405
+ <mass value="3.4572118"/>
1406
+ <!-- org 4.5 -->
1407
+ <inertia ixx="0.050469853" ixy="-7.784938699999999e-06" ixz="9.220580999999999e-06" iyy="0.008181596999999999" iyz="-0.0059796848" izz="0.048213808999999996"/>
1408
+ </inertial>
1409
+ <visual>
1410
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1411
+ <geometry>
1412
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-pitch.stl" scale="0.001 0.001 0.001"/>
1413
+ </geometry>
1414
+ </visual>
1415
+ <collision>
1416
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1417
+ <geometry>
1418
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-pitch.stl" scale="0.001 0.001 0.001"/>
1419
+ </geometry>
1420
+ </collision>
1421
+ </link>
1422
+ <link name="knee_4">
1423
+ <inertial>
1424
+ <origin xyz="0.00012063381 0.14620674 -0.029740711000000003"/>
1425
+ <mass value="2.1199315"/>
1426
+ <!-- org 3.5, foot un modeled-->
1427
+ <inertia ixx="0.018426402999999997" ixy="2.8226635999999997e-05" ixz="2.9204348e-05" iyy="0.0047685615" iyz="0.002640771" izz="0.017793722"/>
1428
+ </inertial>
1429
+ <visual>
1430
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1431
+ <geometry>
1432
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/knee.stl" scale="0.001 0.001 0.001"/>
1433
+ </geometry>
1434
+ <material name="dark_grey"/>
1435
+ </visual>
1436
+ <collision>
1437
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1438
+ <geometry>
1439
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/knee.stl" scale="0.001 0.001 0.001"/>
1440
+ </geometry>
1441
+ </collision>
1442
+ </link>
1443
+ <link name="ankle1_4">
1444
+ <inertial>
1445
+ <origin xyz="0.00012360094 -0.06274712 0.0049068453"/>
1446
+ <mass value="1.4023368"/>
1447
+ <!-- org 3.5, foot un modeled-->
1448
+ <inertia ixx="0.0049928829" ixy="5.762355699999999e-06" ixz="-1.9606394e-05" iyy="0.0017621464" iyz="-0.00045315091" izz="0.0048706328"/>
1449
+ </inertial>
1450
+ <visual>
1451
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1452
+ <geometry>
1453
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/ankle-pitch.stl" scale="0.001 0.001 0.001"/>
1454
+ </geometry>
1455
+ </visual>
1456
+ <collision>
1457
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1458
+ <geometry>
1459
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/ankle-pitch.stl" scale="0.001 0.001 0.001"/>
1460
+ </geometry>
1461
+ </collision>
1462
+ </link>
1463
+ <link name="ankle2_4">
1464
+ <inertial>
1465
+ <origin xyz="0.00018512273 -0.0017369405000000001 -0.091340683"/>
1466
+ <mass value="2.440929"/>
1467
+ <!-- org 3.5, foot un modeled-->
1468
+ <inertia ixx="0.017711499" ixy="-1.0713141e-05" ixz="-2.2094697e-05" iyy="0.014263485" iyz="-0.00033426702" izz="0.0080747557"/>
1469
+ </inertial>
1470
+ <visual>
1471
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1472
+ <geometry>
1473
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_ankle-yaw_wheel23.stl" scale="0.001 0.001 0.001"/>
1474
+ </geometry>
1475
+ <material name="dark_grey"/>
1476
+ </visual>
1477
+ <!-- <collision>
1478
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
1479
+ <geometry>
1480
+ <mesh
1481
+ filename="${MESH_PATH}/v2/collision/mesh_ankle-yaw_wheel23.stl"
1482
+ scale="0.001 ${mirror*0.001} 0.001" />
1483
+ </geometry>
1484
+ </collision> -->
1485
+ </link>
1486
+ <joint name="j_wheel_4" type="continuous">
1487
+ <parent link="ankle2_4"/>
1488
+ <child link="wheel_4"/>
1489
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 0.0 -0.14435"/>
1490
+ <axis xyz="0 0 1"/>
1491
+ <limit effort="35" velocity="20.0"/>
1492
+ <!-- 23 Nm, 1
1493
+ rad/s-->
1494
+ </joint>
1495
+ <link name="wheel_4">
1496
+ <inertial>
1497
+ <origin xyz="0.0 0.0 -0.00015403767"/>
1498
+ <mass value="1.8569388"/>
1499
+ <inertia ixx="0.008637359099999999" ixy="0.0" ixz="0.0" iyy="0.0086373644" iyz="0.0" izz="0.016376551"/>
1500
+ </inertial>
1501
+ <visual>
1502
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1503
+ <geometry>
1504
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_wheel23.stl" scale="0.001 0.001 0.001"/>
1505
+ </geometry>
1506
+ <material name="black"/>
1507
+ </visual>
1508
+ <collision>
1509
+ <origin rpy="0 0 0" xyz="0 0 0"/>
1510
+ <geometry>
1511
+ <cylinder length="0.03" radius="0.124"/>
1512
+ </geometry>
1513
+ <!-- <geometry>
1514
+ <mesh filename="${MESH_PATH}/v2/collision/wheel23_donut.stl"
1515
+ scale="0.001 0.001 ${mirror*0.001}" />
1516
+ </geometry> -->
1517
+ <material name="black"/>
1518
+ </collision>
1519
+ </link>
1520
+ <!-- *********** CONTACT FRAME ******************-->
1521
+ <link name="contact_4"/>
1522
+ <joint name="contact_joint_4" type="fixed">
1523
+ <parent link="ankle2_4"/>
1524
+ <child link="contact_4"/>
1525
+ <origin rpy="0 0 0" xyz="0 0 -0.26835"/>
1526
+ </joint>
1527
+ <!-- ******************************** -->
1528
+ <joint name="hip_yaw_4" type="revolute">
1529
+ <parent link="pelvis"/>
1530
+ <child link="hip1_4"/>
1531
+ <origin rpy="0.0 0.0 0.0" xyz="-0.25 -0.125 -0.056600000000000004"/>
1532
+ <axis xyz="0 0 -1"/>
1533
+ <limit effort="304" lower="-2.03" upper="2.51" velocity="8.8"/>
1534
+ <!-- 200 Nm, 1 rad/s-->
1535
+ </joint>
1536
+ <joint name="hip_pitch_4" type="revolute">
1537
+ <parent link="hip1_4"/>
1538
+ <child link="hip2_4"/>
1539
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 -0.1146 -0.0625"/>
1540
+ <axis xyz="0 0 1"/>
1541
+ <limit effort="304" lower="-2.0326" upper="2.0524" velocity="8.8"/>
1542
+ <!-- 200 Nm, 1 rad/s-->
1543
+ </joint>
1544
+ <joint name="knee_pitch_4" type="revolute">
1545
+ <parent link="hip2_4"/>
1546
+ <child link="knee_4"/>
1547
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 -0.3 -0.1182"/>
1548
+ <axis xyz="0 0 1"/>
1549
+ <limit effort="304" lower="-2.4176" upper="2.3884" velocity="8.8"/>
1550
+ <!-- 200 Nm, 1 rad/s-->
1551
+ </joint>
1552
+ <joint name="ankle_pitch_4" type="revolute">
1553
+ <parent link="knee_4"/>
1554
+ <child link="ankle1_4"/>
1555
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 0.2 -0.10015"/>
1556
+ <axis xyz="0 0 1"/>
1557
+ <limit effort="147" lower="-2.3426" upper="2.3934" velocity="8.1"/>
1558
+ <!-- 98 Nm, 1 rad/s-->
1559
+ </joint>
1560
+ <joint name="ankle_yaw_4" type="revolute">
1561
+ <parent link="ankle1_4"/>
1562
+ <child link="ankle2_4"/>
1563
+ <origin rpy="-1.57079632679 0.0 0.0" xyz="0.0 -0.139 0.00085"/>
1564
+ <axis xyz="0 0 -1"/>
1565
+ <limit effort="35" lower="-2.6046" upper="2.5514" velocity="20.0"/>
1566
+ <!-- 23 Nm, 1 rad/s-->
1567
+ </joint>
1568
+ <!-- ************ Control frames ************ -->
1569
+ </robot>
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/centauro_rhc.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from aug_mpc.controllers.rhc.horizon_based.hybrid_quad_rhc import HybridQuadRhc
2
+
3
+ import numpy as np
4
+
5
+ from typing import Dict
6
+
7
+ from centaurohybridmpc.utils.sysutils import PathsGetter
8
+
9
+ class CentauroRhc(HybridQuadRhc):
10
+
11
+ def __init__(self,
12
+ srdf_path: str,
13
+ urdf_path: str,
14
+ robot_name: str, # used for shared memory namespaces
15
+ codegen_dir: str,
16
+ n_nodes: float = 31,
17
+ dt: float = 0.03,
18
+ injection_node: int = 10,
19
+ max_solver_iter = 1, # defaults to rt-iteration
20
+ open_loop: bool = True,
21
+ close_loop_all: bool = False,
22
+ dtype = np.float32,
23
+ verbose = False,
24
+ debug = False,
25
+ refs_in_hor_frame = True,
26
+ timeout_ms: int = 60000,
27
+ custom_opts: Dict = {}
28
+ ):
29
+
30
+ paths = PathsGetter()
31
+ self._files_suffix=""
32
+ if open_loop:
33
+ self._files_suffix="_open"
34
+
35
+ self._add_upper_body=False
36
+ if ("add_upper_body" in custom_opts) and \
37
+ (custom_opts["add_upper_body"]):
38
+ self._add_upper_body=True
39
+ self._files_suffix+="_ub"
40
+
41
+ config_path=paths.RHCCONFIGPATH_NO_WHEELS+self._files_suffix+".yaml"
42
+
43
+ super().__init__(srdf_path=srdf_path,
44
+ urdf_path=urdf_path,
45
+ config_path=config_path,
46
+ robot_name=robot_name, # used for shared memory namespaces
47
+ codegen_dir=codegen_dir,
48
+ n_nodes=n_nodes,
49
+ dt=dt,
50
+ injection_node=injection_node,
51
+ max_solver_iter=max_solver_iter, # defaults to rt-iteration
52
+ open_loop=open_loop,
53
+ close_loop_all=close_loop_all,
54
+ dtype=dtype,
55
+ verbose=verbose,
56
+ debug=debug,
57
+ refs_in_hor_frame=refs_in_hor_frame,
58
+ timeout_ms=timeout_ms,
59
+ custom_opts=custom_opts)
60
+
61
+ self._fail_idx_scale=1e-9
62
+ self._fail_idx_thresh_open_loop=1e0
63
+ self._fail_idx_thresh_close_loop=10
64
+
65
+ if open_loop:
66
+ self._fail_idx_thresh=self._fail_idx_thresh_open_loop
67
+ else:
68
+ self._fail_idx_thresh=self._fail_idx_thresh_close_loop
69
+
70
+ # adding some additional config files for jnt imp control
71
+ self._rhc_fpaths.append(paths.JNT_IMP_CONFIG_XBOT+".yaml")
72
+ self._rhc_fpaths.append(paths.JNT_IMP_CONFIG+".yaml")
73
+
74
+ def _set_rhc_pred_idx(self):
75
+ self._pred_node_idx=round((self._n_nodes-1)*2/3)
76
+
77
+ def _set_rhc_cmds_idx(self):
78
+ self._rhc_cmds_node_idx=2
79
+
80
+ def _config_override(self):
81
+ paths = PathsGetter()
82
+ if ("control_wheels" in self._custom_opts):
83
+ if self._custom_opts["control_wheels"]:
84
+ self.config_path = paths.RHCCONFIGPATH_WHEELS+self._files_suffix+".yaml"
85
+ if ("fix_yaw" in self._custom_opts) and \
86
+ (self._custom_opts["fix_yaw"]):
87
+ self.config_path = paths.RHCCONFIGPATH_WHEELS_NO_YAW+self._files_suffix+".yaml"
88
+ if ("replace_continuous_joints" in self._custom_opts) and \
89
+ (not self._custom_opts["replace_continuous_joints"]):
90
+ # use continuous joints -> different config
91
+ self.config_path = paths.RHCCONFIGPATH_WHEELS_CONTINUOUS+self._files_suffix+".yaml"
92
+ if ("fix_yaw" in self._custom_opts) and \
93
+ (self._custom_opts["fix_yaw"]):
94
+ self.config_path = paths.RHCCONFIGPATH_WHEELS_CONTINUOUS_NO_YAW+self._files_suffix+".yaml"
95
+
96
+ else:
97
+ self._custom_opts["control_wheels"]=False
98
+
99
+ if not self._custom_opts["control_wheels"]:
100
+ self._fixed_jnt_patterns=self._fixed_jnt_patterns+\
101
+ ["j_wheel",
102
+ "ankle_yaw"]
103
+ self._custom_opts["replace_continuous_joints"]=True
104
+
105
+ def _init_problem(self):
106
+
107
+ if not self._custom_opts["control_wheels"]:
108
+ self._yaw_vertical_weight=120.0
109
+ else:
110
+ self._yaw_vertical_weight=50.0
111
+
112
+ fixed_jnts_patterns=[
113
+ "d435_head",
114
+ "velodyne_joint"]
115
+
116
+ if not self._add_upper_body:
117
+ fixed_jnts_patterns.append("j_arm")
118
+ fixed_jnts_patterns.append("torso")
119
+
120
+ if ("fix_yaw" in self._custom_opts) and \
121
+ (self._custom_opts["fix_yaw"]):
122
+ fixed_jnts_patterns.append("ankle_yaw")
123
+
124
+ flight_duration_sec=0.5 # [s]
125
+ flight_duration=int(flight_duration_sec/self._dt)
126
+ post_flight_duration_sec=0.2 # [s]
127
+ post_flight_duration=int(post_flight_duration_sec/self._dt)
128
+
129
+ step_height=0.1
130
+ if ("step_height" in self._custom_opts):
131
+ step_height=self._custom_opts["step_height"]
132
+
133
+ super()._init_problem(fixed_jnt_patterns=fixed_jnts_patterns,
134
+ wheels_patterns=["wheel_"],
135
+ foot_linkname="wheel_1",
136
+ flight_duration=flight_duration,
137
+ post_flight_stance=post_flight_duration,
138
+ step_height=step_height,
139
+ keep_yaw_vert=True,
140
+ yaw_vertical_weight=self._yaw_vertical_weight,
141
+ vertical_landing=True,
142
+ vertical_land_weight=10.0,
143
+ phase_force_reg=5e-2,
144
+ vel_bounds_weight=1.0)
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/centauro_rhc_wheels_continuous_no_yaw_ub.yaml ADDED
@@ -0,0 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ solver:
2
+ type: ilqr
3
+ ipopt.linear_solver: ma57
4
+ ipopt.tol: 0.1
5
+ # ilqr.merit_der_threshold: 1e-3
6
+ # ilqr.defect_norm_threshold: 1e-3
7
+ ipopt.constr_viol_tol: 0.01
8
+ ilqr.constraint_violation_threshold: 1e-2
9
+ # ipopt.hessian_approximation: exact
10
+ ipopt.print_level: 5
11
+ ipopt.suppress_all_output: 'yes'
12
+ ipopt.sb: 'yes'
13
+ ilqr.suppress_all_output: 'yes'
14
+ ilqr.codegen_enabled: true
15
+ ilqr.codegen_workdir: /tmp/tyhio
16
+ ilqr.enable_gn: true
17
+ ilqr.hxx_reg_base: 0.0
18
+ ilqr.n_threads: 0
19
+ print_time: 0
20
+
21
+ constraints:
22
+ - contact_1
23
+ - contact_2
24
+ - contact_3
25
+ - contact_4
26
+
27
+ costs:
28
+ - z_contact_1
29
+ - z_contact_2
30
+ - z_contact_3
31
+ - z_contact_4
32
+ # - vz_contact_1
33
+ # - vz_contact_2
34
+ # - vz_contact_3
35
+ # - vz_contact_4
36
+ # - xy_contact_1
37
+ # - xy_contact_2
38
+ # - xy_contact_3
39
+ # - xy_contact_4
40
+ # - vxy_contact_1
41
+ # - vxy_contact_2
42
+ # - vxy_contact_3
43
+ # - vxy_contact_4
44
+ - base_lin_velxy
45
+ - base_lin_velz
46
+ - base_omega
47
+ - base_capture
48
+ - joint_posture_capture
49
+ - joint_posture_capture_torso
50
+ - joint_posture_capture_ub
51
+ - v_root_regularization
52
+ - v_jnts_regularization
53
+ - a_regularization
54
+ - joint_v_reg_wheels
55
+ - joint_a_reg_wheels
56
+ # - force_regularization
57
+
58
+ .define:
59
+ - &w_base_omega 15.
60
+ - &w_base_vxy 20.
61
+ - &w_base_vz 20.
62
+ - &w_base_z 15.
63
+ - &w_contact_z 175.0
64
+ - &w_contact_vz 250.0
65
+ - &w_contact_xy 60.0
66
+ - &w_contact_vxy 50.0
67
+ - &w_base_capture 200.
68
+ - &w_capture_postural 30.0
69
+ - &w_capture_postural_torso 30.0
70
+ - &w_capture_postural_ub 5.0
71
+ - &w_v_root 2e-1
72
+ - &w_v_jnts 2e0
73
+ - &w_v_wheels 1e0
74
+ - &w_a 4e-1
75
+ - &w_a_wheels 8e-1
76
+ - &wheel_radius 0.124
77
+
78
+ base_lin_velxy:
79
+ type: Cartesian
80
+ distal_link: base_link
81
+ indices: [0, 1]
82
+ nodes: ${range(1, N-5)}
83
+ cartesian_type: velocity
84
+ weight: *w_base_vxy
85
+
86
+ base_lin_velz:
87
+ type: Cartesian
88
+ distal_link: base_link
89
+ indices: [2]
90
+ nodes: ${range(1, N-5)}
91
+ cartesian_type: velocity
92
+ weight: *w_base_vz
93
+
94
+ base_omega:
95
+ type: Cartesian
96
+ distal_link: base_link
97
+ indices: [3, 4, 5]
98
+ nodes: ${range(1, N-5)}
99
+ cartesian_type: velocity
100
+ weight: *w_base_omega
101
+
102
+ base_capture:
103
+ type: Cartesian
104
+ distal_link: base_link
105
+ indices: [0, 1, 2, 3, 4, 5]
106
+ nodes: ${range(N-5, N+1)}
107
+ cartesian_type: velocity
108
+ weight: *w_base_capture
109
+
110
+ # ===============================
111
+
112
+ rolling_contact_1:
113
+ type: Rolling
114
+ frame: wheel_1
115
+ radius: *wheel_radius
116
+
117
+ rolling_contact_2:
118
+ type: Rolling
119
+ frame: wheel_2
120
+ radius: *wheel_radius
121
+
122
+ rolling_contact_3:
123
+ type: Rolling
124
+ frame: wheel_3
125
+ radius: *wheel_radius
126
+
127
+ rolling_contact_4:
128
+ type: Rolling
129
+ frame: wheel_4
130
+ radius: *wheel_radius
131
+
132
+ # ==================================
133
+
134
+ interaction_contact_1:
135
+ type: VertexForce
136
+ frame: contact_1
137
+ fn_min: 100.0
138
+ enable_fc: true
139
+ friction_coeff: 0.5
140
+ vertex_frames:
141
+ - wheel_1
142
+
143
+ interaction_contact_2:
144
+ type: VertexForce
145
+ frame: contact_2
146
+ fn_min: 100.0
147
+ enable_fc: true
148
+ friction_coeff: 0.5
149
+ vertex_frames:
150
+ - wheel_2
151
+
152
+ interaction_contact_3:
153
+ type: VertexForce
154
+ frame: contact_3
155
+ fn_min: 100.0
156
+ enable_fc: true
157
+ friction_coeff: 0.5
158
+ vertex_frames:
159
+ - wheel_3
160
+
161
+ interaction_contact_4:
162
+ type: VertexForce
163
+ frame: contact_4
164
+ fn_min: 100.0
165
+ enable_fc: true
166
+ friction_coeff: 0.5
167
+ vertex_frames:
168
+ - wheel_4
169
+
170
+ contact_1:
171
+ type: Contact
172
+ subtask: [interaction_contact_1, rolling_contact_1]
173
+
174
+ contact_2:
175
+ type: Contact
176
+ subtask: [interaction_contact_2, rolling_contact_2]
177
+
178
+ contact_3:
179
+ type: Contact
180
+ subtask: [interaction_contact_3, rolling_contact_3]
181
+
182
+ contact_4:
183
+ type: Contact
184
+ subtask: [interaction_contact_4, rolling_contact_4]
185
+
186
+ joint_posture_capture:
187
+ type: Postural
188
+ weight: *w_capture_postural
189
+ indices: [0, 1, 2, 3,
190
+ 6, 7, 8, 9,
191
+ 12, 13, 14, 15,
192
+ 18, 19, 20, 21]
193
+ nodes: ${range(N-5, N+1)}
194
+
195
+ joint_posture_capture_torso:
196
+ type: Postural
197
+ weight: *w_capture_postural_torso
198
+ indices: [24]
199
+ nodes: ${range(N-5, N+1)}
200
+
201
+ joint_posture_capture_ub:
202
+ type: Postural
203
+ weight: *w_capture_postural_ub
204
+ indices: [
205
+ 25, 26, 27, 28, 29, 30,
206
+ 31, 32, 33, 34, 35, 36]
207
+ nodes: ${range(N-5, N+1)}
208
+
209
+ v_root_regularization:
210
+ type: Regularization
211
+ nodes: ${range(0, 1)}
212
+ indices: [0, 1, 2, 3, 4, 5]
213
+ weight:
214
+ v: *w_v_root
215
+
216
+ v_jnts_regularization:
217
+ type: Regularization
218
+ nodes: ${range(0, N+1)}
219
+ indices: [6, 7, 8, 9,
220
+ 11, 12, 13, 14,
221
+ 16, 17, 18, 19,
222
+ 21, 21, 23, 24,
223
+ 26,
224
+ 27, 28, 29, 30, 31, 32,
225
+ 33, 34, 35, 36, 37, 38]
226
+ weight:
227
+ v: *w_v_jnts
228
+
229
+ a_regularization:
230
+ type: Regularization
231
+ nodes: ${range(0, N+1)}
232
+ indices: [0, 1, 2, 3, 4, 5,
233
+ 6, 7, 8, 9,
234
+ 11, 12, 13, 14,
235
+ 16, 17, 18, 19,
236
+ 21, 21, 23, 24,
237
+ 26,
238
+ 27, 28, 29, 30, 31, 32,
239
+ 33, 34, 35, 36, 37, 38]
240
+ weight:
241
+ a: *w_a # 0.01
242
+
243
+ joint_v_reg_wheels:
244
+ type: Regularization
245
+ nodes: ${range(0, N+1)}
246
+ indices: [10, 15, 20, 25]
247
+ weight:
248
+ v: *w_v_wheels
249
+
250
+ joint_a_reg_wheels:
251
+ type: Regularization
252
+ nodes: ${range(0, N+1)}
253
+ indices: [10, 15, 20, 25]
254
+ weight:
255
+ a: *w_a_wheels # 0.01
256
+
257
+ # flight phase traj tracking
258
+ z_contact_1:
259
+ type: Cartesian
260
+ distal_link: contact_1
261
+ indices: [2]
262
+ cartesian_type: position
263
+ weight: *w_contact_z
264
+
265
+ z_contact_2:
266
+ type: Cartesian
267
+ distal_link: contact_2
268
+ indices: [2]
269
+ cartesian_type: position
270
+ weight: *w_contact_z
271
+
272
+ z_contact_3:
273
+ type: Cartesian
274
+ distal_link: contact_3
275
+ indices: [2]
276
+ cartesian_type: position
277
+ weight: *w_contact_z
278
+
279
+ z_contact_4:
280
+ type: Cartesian
281
+ distal_link: contact_4
282
+ indices: [2]
283
+ cartesian_type: position
284
+ weight: *w_contact_z
285
+
286
+ xy_contact_1:
287
+ type: Cartesian
288
+ distal_link: contact_1
289
+ indices: [0, 1]
290
+ cartesian_type: position
291
+ weight: *w_contact_xy
292
+
293
+ xy_contact_2:
294
+ type: Cartesian
295
+ distal_link: contact_2
296
+ indices: [0, 1]
297
+ cartesian_type: position
298
+ weight: *w_contact_xy
299
+
300
+ xy_contact_3:
301
+ type: Cartesian
302
+ distal_link: contact_3
303
+ indices: [0, 1]
304
+ cartesian_type: position
305
+ weight: *w_contact_xy
306
+
307
+ xy_contact_4:
308
+ type: Cartesian
309
+ distal_link: contact_4
310
+ indices: [0, 1]
311
+ cartesian_type: position
312
+ weight: *w_contact_xy
313
+
314
+ vz_contact_1:
315
+ type: Cartesian
316
+ distal_link: contact_1
317
+ indices: [2]
318
+ cartesian_type: velocity
319
+ weight: *w_contact_vz
320
+
321
+ vz_contact_2:
322
+ type: Cartesian
323
+ distal_link: contact_2
324
+ indices: [2]
325
+ cartesian_type: velocity
326
+ weight: *w_contact_vz
327
+
328
+ vz_contact_3:
329
+ type: Cartesian
330
+ distal_link: contact_3
331
+ indices: [2]
332
+ cartesian_type: velocity
333
+ weight: *w_contact_vz
334
+
335
+ vz_contact_4:
336
+ type: Cartesian
337
+ distal_link: contact_4
338
+ indices: [2]
339
+ cartesian_type: velocity
340
+ weight: *w_contact_vz
341
+
342
+ vxy_contact_1:
343
+ type: Cartesian
344
+ distal_link: contact_1
345
+ indices: [0, 1]
346
+ cartesian_type: velocity
347
+ weight: *w_contact_vxy
348
+
349
+ vxy_contact_2:
350
+ type: Cartesian
351
+ distal_link: contact_2
352
+ indices: [0, 1]
353
+ cartesian_type: velocity
354
+ weight: *w_contact_vxy
355
+
356
+ vxy_contact_3:
357
+ type: Cartesian
358
+ distal_link: contact_3
359
+ indices: [0, 1]
360
+ cartesian_type: velocity
361
+ weight: *w_contact_vxy
362
+
363
+ vxy_contact_4:
364
+ type: Cartesian
365
+ distal_link: contact_4
366
+ indices: [0, 1]
367
+ cartesian_type: velocity
368
+ weight: *w_contact_vxy
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl_model ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0c9df84000339df696ddf1be50309bbbb823ad25881edfe072b9afe15a41fce4
3
+ size 10021303
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/fake_pos_track_env_phase_control.py ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict
2
+
3
+ import os
4
+
5
+ import torch
6
+
7
+ from EigenIPC.PyEigenIPC import VLevel
8
+
9
+ from mpc_hive.utilities.math_utils_torch import world2base_frame
10
+
11
+ from aug_mpc_envs.training_envs.flight_phase_control_env import FlightPhaseControl
12
+
13
+ class FakePosTrackEnvPhaseControl(FlightPhaseControl):
14
+ """Adds positional goals to the flight-parameter control env so agents chase waypoints while directly setting gait contact flags and flight profiles."""
15
+
16
+ def __init__(self,
17
+ namespace: str,
18
+ verbose: bool = False,
19
+ vlevel: VLevel = VLevel.V1,
20
+ use_gpu: bool = True,
21
+ dtype: torch.dtype = torch.float32,
22
+ debug: bool = True,
23
+ override_agent_refs: bool = False,
24
+ timeout_ms: int = 60000,
25
+ env_opts: Dict = {}):
26
+
27
+ self._add_env_opt(env_opts, "max_distance", default=5.0) # [m]
28
+ self._add_env_opt(env_opts, "min_distance", default=0.0) # [m]
29
+ self._add_env_opt(env_opts, "max_vref", default=1.0) # [m/s]
30
+ self._add_env_opt(env_opts, "max_dp", default=5.0) # [m] after this, v ref saturates
31
+ self._add_env_opt(env_opts, "max_dt", default=env_opts["max_dp"]/ env_opts["max_vref"])
32
+
33
+ FlightPhaseControl.__init__(self,
34
+ namespace=namespace,
35
+ verbose=verbose,
36
+ vlevel=vlevel,
37
+ use_gpu=use_gpu,
38
+ dtype=dtype,
39
+ debug=debug,
40
+ override_agent_refs=override_agent_refs,
41
+ timeout_ms=timeout_ms,
42
+ env_opts=env_opts)
43
+
44
+ def get_file_paths(self):
45
+ paths=FlightPhaseControl.get_file_paths(self)
46
+ paths.append(os.path.abspath(__file__))
47
+ return paths
48
+
49
+ def _custom_post_init(self):
50
+ FlightPhaseControl._custom_post_init(self)
51
+
52
+ # position targets to be reached (wrt robot's pos at ep start)
53
+ self._p_trgt_w=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)[:, 0:2].detach().clone()
54
+ self._p_delta_w=self._p_trgt_w.detach().clone()
55
+ self._dp_norm=torch.zeros((self._n_envs, 1),dtype=self._dtype,device=self._device)
56
+ self._dp_versor=self._p_trgt_w.detach().clone()
57
+
58
+ self._trgt_d=torch.zeros((self._n_envs, 1),dtype=self._dtype,device=self._device)
59
+ self._trgt_theta=torch.zeros((self._n_envs, 1),dtype=self._dtype,device=self._device)
60
+
61
+ def _update_loc_twist_refs(self):
62
+ # this is called at each env substep
63
+
64
+ self._compute_twist_ref_w()
65
+
66
+ if not self._override_agent_refs:
67
+ agent_p_ref_current=self._agent_refs.rob_refs.root_state.get(data_type="p",
68
+ gpu=self._use_gpu)
69
+ agent_p_ref_current[:, 0:2]=self._p_trgt_w
70
+
71
+ # then convert it to base ref local for the agent
72
+ robot_q = self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
73
+ # rotate agent ref from world to robot base
74
+ world2base_frame(t_w=self._agent_twist_ref_current_w, q_b=robot_q,
75
+ t_out=self._agent_twist_ref_current_base_loc)
76
+ # write it to agent refs tensors
77
+ self._agent_refs.rob_refs.root_state.set(data_type="twist", data=self._agent_twist_ref_current_base_loc,
78
+ gpu=self._use_gpu)
79
+
80
+ def _compute_twist_ref_w(self, env_indxs: torch.Tensor = None):
81
+
82
+ # angular refs are not altered
83
+ if env_indxs is None:
84
+ # we update the position error using the current base position
85
+ self._p_delta_w[:, :]=self._p_trgt_w-\
86
+ self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)[:, 0:2]
87
+
88
+ self._dp_norm[:, :]=self._p_delta_w.norm(dim=1,keepdim=True)+1e-6
89
+ self._dp_versor[:, :]=self._p_delta_w/self._dp_norm
90
+
91
+ # apply for vref saturation
92
+ to_be_saturated=self._dp_norm[:, :]>self._env_opts["max_dp"]
93
+ self._dp_norm[to_be_saturated.flatten(), :]=self._env_opts["max_dp"]
94
+
95
+ # we compute the twist refs for the agent depending of the position error
96
+ self._agent_twist_ref_current_w[:, 0:2]=self._dp_norm*self._dp_versor/self._env_opts["max_dt"]
97
+ self._agent_twist_ref_current_w[:, 2:3]=0 # no vertical vel
98
+
99
+ # apply pof0 using last value of bernoully coeffs
100
+ self._agent_twist_ref_current_w[:, 0:3] = self._agent_twist_ref_current_w[:, 0:3]*self._bernoulli_coeffs_linvel # linvel
101
+ self._agent_twist_ref_current_w[:, 3:6] = self._agent_twist_ref_current_w[:, 3:6]*self._bernoulli_coeffs_omega # omega
102
+ else:
103
+ self._p_delta_w[env_indxs, :]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)[env_indxs, 0:2] -\
104
+ self._p_trgt_w[env_indxs, :]
105
+
106
+ # apply for vref saturation
107
+ to_be_saturated=torch.logical_and((self._dp_norm[:, :]>self._env_opts["max_dp"]).flatten(),env_indxs)
108
+ self._dp_norm[to_be_saturated.flatten(), :]=self._env_opts["max_dp"]
109
+
110
+ self._dp_norm[env_indxs, :]=self._p_delta_w[env_indxs, :].norm(dim=1,keepdim=True)+1e-6
111
+ self._dp_versor[env_indxs, :]=self._p_delta_w[env_indxs, :]/self._dp_norm[env_indxs, :]
112
+
113
+ self._agent_twist_ref_current_w[env_indxs, 0:2]=self._dp_norm[env_indxs, :]*self._dp_versor[env_indxs, :]/self._env_opts["max_dt"]
114
+ self._agent_twist_ref_current_w[env_indxs, 2:3]=0 # no vertical vel
115
+
116
+ # apply pof0 using last value of bernoully coeffs
117
+ self._agent_twist_ref_current_w[env_indxs, 0:3] = self._agent_twist_ref_current_w[env_indxs, 0:3]*self._bernoulli_coeffs_linvel[env_indxs, :]
118
+ self._agent_twist_ref_current_w[env_indxs, 3:6] = self._agent_twist_ref_current_w[env_indxs, 3:6]*self._bernoulli_coeffs_omega[env_indxs, :] # omega
119
+
120
+ def _override_refs(self,
121
+ env_indxs: torch.Tensor = None):
122
+
123
+ # runs at every post_step
124
+ self._agent_refs.rob_refs.root_state.synch_all(read=True,retry=True) # first read from mem
125
+ if self._use_gpu:
126
+ # copies latest refs to GPU
127
+ self._agent_refs.rob_refs.root_state.synch_mirror(from_gpu=False,non_blocking=False)
128
+
129
+ agent_p_ref_current=self._agent_refs.rob_refs.root_state.get(data_type="p",
130
+ gpu=self._use_gpu)
131
+
132
+ agent_yaw_omega_ref_current=self._agent_refs.rob_refs.root_state.get(data_type="omega",
133
+ gpu=self._use_gpu)
134
+
135
+ # self._p_trgt_w[:, :]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)[:, 0:2] + \
136
+ # agent_p_ref_current[:, 0:2]
137
+ self._p_trgt_w[:, :]=agent_p_ref_current[:, 0:2] # set p target target from shared mem
138
+
139
+ self._agent_twist_ref_current_w[:, 5:6]=agent_yaw_omega_ref_current[:, 2:3] # set yaw ang. vel target from shared mem
140
+
141
+ def _debug_agent_refs(self):
142
+ if self._use_gpu:
143
+ self._agent_refs.rob_refs.root_state.synch_mirror(from_gpu=True,non_blocking=False)
144
+ self._agent_refs.rob_refs.root_state.synch_all(read=False, retry = True)
145
+
146
+ def _randomize_task_refs(self,
147
+ env_indxs: torch.Tensor = None):
148
+
149
+ # we randomize the target position/omega in world frame
150
+ if env_indxs is None:
151
+ self._trgt_d.uniform_(self._env_opts["min_distance"], self._env_opts["max_distance"])
152
+ self._trgt_theta.uniform_(0.0, 2*torch.pi)
153
+
154
+ self._p_trgt_w[:, :]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)[:, 0:2] +\
155
+ torch.cat((self._trgt_d*torch.cos(self._trgt_theta)
156
+ ,self._trgt_d*torch.sin(self._trgt_theta)), dim=1)
157
+
158
+ # randomize just omega
159
+ random_uniform=torch.full_like(self._agent_twist_ref_current_w[:, 3:6], fill_value=0.0)
160
+ torch.nn.init.uniform_(random_uniform, a=-1, b=1)
161
+ self._agent_twist_ref_current_w[:, 3:6] = random_uniform*self._twist_ref_scale[:, 3:6] + self._twist_ref_offset[:, 3:6]
162
+
163
+ # sample for all envs pof0
164
+ if self._env_opts["use_pof0"]: # sample from bernoulli distribution and update coefficients
165
+ torch.bernoulli(input=self._pof1_b_linvel,out=self._bernoulli_coeffs_linvel) # by default bernoulli_coeffs are 1 if not self._env_opts["use_pof0"]
166
+ torch.bernoulli(input=self._pof1_b_omega,out=self._bernoulli_coeffs_omega)
167
+
168
+ else:
169
+
170
+ if env_indxs.any():
171
+ integer_idxs=torch.nonzero(env_indxs).flatten()
172
+
173
+ trgt_d_selected=self._trgt_d[integer_idxs, :]
174
+ trgt_d_selected.uniform_(self._env_opts["min_distance"], self._env_opts["max_distance"])
175
+ self._trgt_d[integer_idxs, :]=trgt_d_selected
176
+
177
+ trgt_theta_selected=self._trgt_theta[integer_idxs, :]
178
+ trgt_theta_selected.uniform_(0.0, 2*torch.pi)
179
+ self._trgt_theta[integer_idxs, :]=trgt_theta_selected
180
+
181
+ self._p_trgt_w[integer_idxs, 0:1]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)[integer_idxs, 0:1] +\
182
+ self._trgt_d[integer_idxs, :]*torch.cos(self._trgt_theta[integer_idxs, :])
183
+ self._p_trgt_w[integer_idxs, 1:2]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)[integer_idxs, 1:2] +\
184
+ self._trgt_d[integer_idxs, :]*torch.sin(self._trgt_theta[integer_idxs, :])
185
+
186
+ # randomize just omega
187
+ random_uniform=torch.full_like(self._agent_twist_ref_current_w[env_indxs, 3:6], fill_value=0.0)
188
+ torch.nn.init.uniform_(random_uniform, a=-1, b=1)
189
+ self._agent_twist_ref_current_w[env_indxs, 3:6] = random_uniform*self._twist_ref_scale[:, 3:6] + self._twist_ref_offset[:, 3:6]
190
+
191
+ # sample for all envs pof0, then reset to 1 for envs which are not to be randomized
192
+ if self._env_opts["use_pof0"]: # sample from bernoulli distribution and update coefficients
193
+ torch.bernoulli(input=self._pof1_b_linvel,out=self._bernoulli_coeffs_linvel) # by default bernoulli_coeffs are 1 if not self._env_opts["use_pof0"]
194
+ torch.bernoulli(input=self._pof1_b_omega,out=self._bernoulli_coeffs_omega)
195
+ self._bernoulli_coeffs_linvel[~env_indxs, :]=1
196
+ self._bernoulli_coeffs_omega[~env_indxs, :]=1
197
+
198
+ self._compute_twist_ref_w(env_indxs=env_indxs) # update linear vel twist refs based on pos error
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/flight_phase_control_env.py ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from typing import Dict
3
+
4
+ import os
5
+
6
+ import torch
7
+
8
+ from EigenIPC.PyEigenIPC import VLevel
9
+
10
+ from mpc_hive.utilities.shared_data.rhc_data import RobotState
11
+
12
+ from aug_mpc_envs.training_envs.twist_tracking_env import TwistTrackingEnv
13
+
14
+ class FlightPhaseControl(TwistTrackingEnv):
15
+ """Extends twist tracking with per-contact flight length/apex/end actions so agents learn to modulate aerial phases alongside base velocity tracking."""
16
+
17
+ def __init__(self,
18
+ namespace: str,
19
+ verbose: bool = False,
20
+ vlevel: VLevel = VLevel.V1,
21
+ use_gpu: bool = True,
22
+ dtype: torch.dtype = torch.float32,
23
+ debug: bool = True,
24
+ override_agent_refs: bool = False,
25
+ timeout_ms: int = 60000,
26
+ env_opts: Dict = {}):
27
+
28
+ self._add_env_opt(env_opts, "control_flength", default=True)
29
+ self._add_env_opt(env_opts, "control_fapex", default=True)
30
+ self._add_env_opt(env_opts, "control_fend", default=True)
31
+ self._add_env_opt(env_opts, "control_flanding", default=False)
32
+
33
+ self._add_env_opt(env_opts, "flength_min", default=8) # substeps
34
+
35
+ # temporarily creating robot state client to get some data
36
+ robot_state_tmp = RobotState(namespace=namespace,
37
+ is_server=False,
38
+ safe=False,
39
+ verbose=verbose,
40
+ vlevel=vlevel,
41
+ with_gpu_mirror=False,
42
+ with_torch_view=False)
43
+ robot_state_tmp.run()
44
+ n_contacts = len(robot_state_tmp.contact_names())
45
+ robot_state_tmp.close()
46
+
47
+ actions_dim=10 # base size
48
+ if env_opts["control_flength"]:
49
+ actions_dim+=n_contacts
50
+ if env_opts["control_fapex"]:
51
+ actions_dim+=n_contacts
52
+ if env_opts["control_fend"]:
53
+ actions_dim+=n_contacts
54
+ if env_opts["control_flanding"]:
55
+ actions_dim+=2*n_contacts
56
+
57
+ TwistTrackingEnv.__init__(self,
58
+ namespace=namespace,
59
+ actions_dim=actions_dim,
60
+ verbose=verbose,
61
+ vlevel=vlevel,
62
+ use_gpu=use_gpu,
63
+ dtype=dtype,
64
+ debug=debug,
65
+ override_agent_refs=override_agent_refs,
66
+ timeout_ms=timeout_ms,
67
+ env_opts=env_opts)
68
+
69
+ def get_file_paths(self):
70
+ paths=TwistTrackingEnv.get_file_paths(self)
71
+ paths.append(os.path.abspath(__file__))
72
+ return paths
73
+
74
+ def _custom_post_init(self):
75
+
76
+ TwistTrackingEnv._custom_post_init(self)
77
+
78
+ self._add_env_opt(self._env_opts, "flength_max", default=self._n_nodes_rhc.mean().item()) # MPC steps (substeps)
79
+
80
+ # additional actions bounds
81
+
82
+ # flight params (length)
83
+ if self._env_opts["control_flength"]:
84
+ idx=self._actions_map["flight_len_start"]
85
+ self._actions_lb[:, idx:(idx+self._n_contacts)]=self._env_opts["flength_min"]
86
+ self._actions_ub[:, idx:(idx+self._n_contacts)]=self._env_opts["flength_max"]
87
+ self._is_continuous_actions[idx:(idx+self._n_contacts)]=True
88
+ # flight params (apex)
89
+ if self._env_opts["control_fapex"]:
90
+ idx=self._actions_map["flight_apex_start"]
91
+ self._actions_lb[:, idx:(idx+self._n_contacts)]=0.05
92
+ self._actions_ub[:, idx:(idx+self._n_contacts)]=0.35
93
+ self._is_continuous_actions[idx:(idx+self._n_contacts)]=True
94
+ # flight params (end)
95
+ if self._env_opts["control_fend"]:
96
+ idx=self._actions_map["flight_end_start"]
97
+ self._actions_lb[:, idx:(idx+self._n_contacts)]=0.0
98
+ self._actions_ub[:, idx:(idx+self._n_contacts)]=0.2
99
+ self._is_continuous_actions[idx:(idx+self._n_contacts)]=True
100
+ # flight params (landing dx, dy)
101
+ if self._env_opts["control_flanding"]:
102
+ idx=self._actions_map["flight_land_dx_start"]
103
+ self._actions_lb[:, idx:(idx+self._n_contacts)]=-0.5
104
+ self._actions_ub[:, idx:(idx+self._n_contacts)]=0.5
105
+ self._is_continuous_actions[idx:(idx+self._n_contacts)]=True
106
+ idx=self._actions_map["flight_land_dy_start"]
107
+ self._actions_lb[:, idx:(idx+self._n_contacts)]=-0.5
108
+ self._actions_ub[:, idx:(idx+self._n_contacts)]=0.5
109
+ self._is_continuous_actions[idx:(idx+self._n_contacts)]=True
110
+
111
+ # redefine default actions
112
+ self.default_action[:, :] = (self._actions_ub+self._actions_lb)/2.0
113
+ # self.default_action[:, ~self._is_continuous_actions] = 1.0
114
+
115
+ if self._env_opts["control_flength"]:
116
+ idx=self._actions_map["flight_len_start"]
117
+ self.safe_action[:, idx:(idx+self._n_contacts)]=(self._env_opts["flength_max"]+self._env_opts["flength_min"])/3.0
118
+
119
+ if self._env_opts["control_fapex"]:
120
+ idx=self._actions_map["flight_apex_start"]
121
+ self.safe_action[:, idx:(idx+self._n_contacts)]=0.1
122
+
123
+ if self._env_opts["control_fend"]:
124
+ idx=self._actions_map["flight_end_start"]
125
+ self.safe_action[:, idx:(idx+self._n_contacts)]=0.0
126
+
127
+ if self._env_opts["control_flanding"]:
128
+ idx=self._actions_map["flight_land_dx_start"]
129
+ self.safe_action[:, idx:(idx+self._n_contacts)]=0.0
130
+ idx=self._actions_map["flight_land_dy_start"]
131
+ self.safe_action[:, idx:(idx+self._n_contacts)]=0.0
132
+
133
+ def _set_rhc_refs(self):
134
+ TwistTrackingEnv._set_rhc_refs(self)
135
+
136
+ action_to_be_applied = self.get_actual_actions()
137
+
138
+ if self._env_opts["control_flength"]:
139
+ idx=self._actions_map["flight_len_start"]
140
+ flen_now=self._rhc_refs.flight_settings_req.get(data_type="len_remain", gpu=self._use_gpu)
141
+ flen_now[:, :]=action_to_be_applied[:, idx:(idx+self._n_contacts)]
142
+ self._rhc_refs.flight_settings_req.set(data=flen_now, data_type="len_remain", gpu=self._use_gpu)
143
+
144
+ if self._env_opts["control_fapex"]:
145
+ idx=self._actions_map["flight_apex_start"]
146
+ fapex_now=self._rhc_refs.flight_settings_req.get(data_type="apex_dpos", gpu=self._use_gpu)
147
+ fapex_now[:, :]=action_to_be_applied[:, idx:(idx+self._n_contacts)]
148
+ self._rhc_refs.flight_settings_req.set(data=fapex_now, data_type="apex_dpos", gpu=self._use_gpu)
149
+
150
+ if self._env_opts["control_fend"]:
151
+ idx=self._actions_map["flight_end_start"]
152
+ fend_now=self._rhc_refs.flight_settings_req.get(data_type="end_dpos", gpu=self._use_gpu)
153
+ fend_now[:, :]=action_to_be_applied[:, idx:(idx+self._n_contacts)]
154
+ self._rhc_refs.flight_settings_req.set(data=fend_now, data_type="end_dpos", gpu=self._use_gpu)
155
+
156
+ if self._env_opts["control_flanding"]:
157
+ idx=self._actions_map["flight_land_dx_start"]
158
+ fland_dx_now=self._rhc_refs.flight_settings_req.get(data_type="land_dx", gpu=self._use_gpu)
159
+ fland_dx_now[:, :]=action_to_be_applied[:, idx:(idx+self._n_contacts)]
160
+ self._rhc_refs.flight_settings_req.set(data=fland_dx_now, data_type="land_dx", gpu=self._use_gpu)
161
+ idx=self._actions_map["flight_land_dy_start"]
162
+ fland_dy_now=self._rhc_refs.flight_settings_req.get(data_type="land_dy", gpu=self._use_gpu)
163
+ fland_dy_now[:, :]=action_to_be_applied[:, idx:(idx+self._n_contacts)]
164
+ self._rhc_refs.flight_settings_req.set(data=fland_dy_now, data_type="land_dy", gpu=self._use_gpu)
165
+
166
+ def _write_rhc_refs(self):
167
+ TwistTrackingEnv._write_rhc_refs(self)
168
+ if self._use_gpu:
169
+ self._rhc_refs.flight_settings_req.synch_mirror(from_gpu=True,non_blocking=False)
170
+ self._rhc_refs.flight_settings_req.synch_all(read=False, retry=True)
171
+
172
+ def _get_action_names(self):
173
+
174
+ action_names = [""] * self.actions_dim()
175
+ action_names[0] = "vx_cmd" # twist commands from agent to RHC controller
176
+ action_names[1] = "vy_cmd"
177
+ action_names[2] = "vz_cmd"
178
+ action_names[3] = "roll_omega_cmd"
179
+ action_names[4] = "pitch_omega_cmd"
180
+ action_names[5] = "yaw_omega_cmd"
181
+
182
+ next_idx=6
183
+ self._actions_map["contact_flag_start"]=next_idx
184
+ for i in range(len(self._contact_names)):
185
+ contact=self._contact_names[i]
186
+ action_names[next_idx] = f"contact_flag_{contact}"
187
+ next_idx+=1
188
+ if self._env_opts["control_flength"]:
189
+ self._actions_map["flight_len_start"]=next_idx
190
+ for i in range(len(self._contact_names)):
191
+ contact=self._contact_names[i]
192
+ action_names[next_idx+i] = f"flight_len_{contact}"
193
+ next_idx+=len(self._contact_names)
194
+ if self._env_opts["control_fapex"]:
195
+ self._actions_map["flight_apex_start"]=next_idx
196
+ for i in range(len(self._contact_names)):
197
+ contact=self._contact_names[i]
198
+ action_names[next_idx+i] = f"flight_apex_{contact}"
199
+ next_idx+=len(self._contact_names)
200
+ if self._env_opts["control_fend"]:
201
+ self._actions_map["flight_end_start"]=next_idx
202
+ for i in range(len(self._contact_names)):
203
+ contact=self._contact_names[i]
204
+ action_names[next_idx+i] = f"flight_end_{contact}"
205
+ next_idx+=len(self._contact_names)
206
+ if self._env_opts["control_flanding"]:
207
+ self._actions_map["flight_land_dx_start"]=next_idx
208
+ for i in range(len(self._contact_names)):
209
+ contact=self._contact_names[i]
210
+ action_names[next_idx+i] = f"flight_land_dx_{contact}"
211
+ next_idx+=len(self._contact_names)
212
+ self._actions_map["flight_land_dy_start"]=next_idx
213
+ for i in range(len(self._contact_names)):
214
+ contact=self._contact_names[i]
215
+ action_names[next_idx+i] = f"flight_land_dy_{contact}"
216
+ next_idx+=len(self._contact_names)
217
+
218
+ return action_names
219
+
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/hybrid_quad_rhc.py ADDED
@@ -0,0 +1,1243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mpc_hive.controllers.rhc import RHController
2
+ # from perf_sleep.pyperfsleep import PerfSleep
3
+ # from mpc_hive.utilities.cpu_utils.core_utils import get_memory_usage
4
+
5
+ from aug_mpc.controllers.rhc.horizon_based.horizon_imports import *
6
+
7
+ from aug_mpc.controllers.rhc.horizon_based.hybrid_quad_rhc_refs import HybridQuadRhcRefs
8
+ from aug_mpc.controllers.rhc.horizon_based.gait_manager import GaitManager
9
+
10
+ from EigenIPC.PyEigenIPC import VLevel
11
+ from EigenIPC.PyEigenIPC import Journal, LogType
12
+
13
+ import numpy as np
14
+
15
+ import os
16
+ # import shutil
17
+
18
+ import time
19
+ from abc import ABC, abstractmethod
20
+
21
+ from typing import Dict, List
22
+ import re
23
+
24
+ class HybridQuadRhc(RHController):
25
+
26
+ def __init__(self,
27
+ srdf_path: str,
28
+ urdf_path: str,
29
+ config_path: str,
30
+ robot_name: str, # used for shared memory namespaces
31
+ codegen_dir: str,
32
+ n_nodes:float = 25,
33
+ injection_node:int = 10,
34
+ dt: float = 0.02,
35
+ max_solver_iter = 1, # defaults to rt-iteration
36
+ open_loop: bool = True,
37
+ close_loop_all: bool = False,
38
+ dtype = np.float32,
39
+ verbose = False,
40
+ debug = False,
41
+ refs_in_hor_frame = True,
42
+ timeout_ms: int = 60000,
43
+ custom_opts: Dict = {}):
44
+
45
+ self._refs_in_hor_frame = refs_in_hor_frame
46
+
47
+ self._injection_node = injection_node
48
+
49
+ self._open_loop = open_loop
50
+ self._close_loop_all = close_loop_all
51
+
52
+ self._codegen_dir = codegen_dir
53
+ if not os.path.exists(self._codegen_dir):
54
+ os.makedirs(self._codegen_dir)
55
+ # else:
56
+ # # Directory already exists, delete it and recreate
57
+ # shutil.rmtree(self._codegen_dir)
58
+ # os.makedirs(self._codegen_dir)
59
+
60
+ self.step_counter = 0
61
+ self.sol_counter = 0
62
+
63
+ self.max_solver_iter = max_solver_iter
64
+
65
+ self._timer_start = time.perf_counter()
66
+ self._prb_update_time = time.perf_counter()
67
+ self._phase_shift_time = time.perf_counter()
68
+ self._task_ref_update_time = time.perf_counter()
69
+ self._rti_time = time.perf_counter()
70
+
71
+ self.robot_name = robot_name
72
+
73
+ self.config_path = config_path
74
+
75
+ self.urdf_path = urdf_path
76
+ # read urdf and srdf files
77
+ with open(self.urdf_path, 'r') as file:
78
+ self.urdf = file.read()
79
+ self._base_init = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0])
80
+
81
+ self._c_timelines = dict()
82
+ self._f_reg_timelines = dict()
83
+
84
+ self._custom_opts={"replace_continuous_joints": False,
85
+ "use_force_feedback": False,
86
+ "lin_a_feedback": False,
87
+ "is_open_loop": self._open_loop, # fully open (just for db)
88
+ "fully_closed": False, # closed loop with full feedback (just for db)
89
+ "closed_partial": False, # closed loop with partial feedback
90
+ "adaptive_is": True, # closed loop with adaptation
91
+ "estimate_v_root": False, # when adaptive_is or closed_partial, estimate vbase
92
+ "alpha_from_outside": False, # alpha set ext. from shared memory
93
+ "alpha_half": 1.0,
94
+ "only_vel_wheels": True, # whether wheels (if present) are just vel controlled
95
+ "use_jnt_v_feedback": False
96
+ }
97
+
98
+ self._custom_opts.update(custom_opts)
99
+
100
+ self._alpha_half=self._custom_opts["alpha_half"]
101
+
102
+ if self._open_loop:
103
+ self._custom_opts["fully_closed"]=False
104
+ self._custom_opts["adaptive_is"]=False
105
+ self._custom_opts["closed_partial"]=False
106
+ else:
107
+ self._custom_opts["is_open_loop"]=False
108
+ if self._custom_opts["fully_closed"]:
109
+ self._custom_opts["adaptive_is"]=False
110
+ self._custom_opts["closed_partial"]=False
111
+ self._custom_opts["lin_a_feedback"]=False
112
+ if self._custom_opts["closed_partial"]:
113
+ self._custom_opts["adaptive_is"]=False
114
+ self._custom_opts["fully_closed"]=False
115
+ self._custom_opts["lin_a_feedback"]=False
116
+ if self._custom_opts["adaptive_is"]:
117
+ self._custom_opts["closed_partial"]=False
118
+ self._custom_opts["fully_closed"]=False
119
+
120
+ super().__init__(srdf_path=srdf_path,
121
+ n_nodes=n_nodes,
122
+ dt=dt,
123
+ namespace=self.robot_name,
124
+ dtype=dtype,
125
+ verbose=verbose,
126
+ debug=debug,
127
+ timeout_ms=timeout_ms)
128
+
129
+ self._rhc_fpaths.append(self.config_path)
130
+
131
+ def _config_override(self):
132
+ pass
133
+
134
+ def _post_problem_init(self):
135
+
136
+ self.rhc_costs={}
137
+ self.rhc_constr={}
138
+
139
+ self._fail_idx_scale=0.0
140
+ self._expl_idx_window_size=int(1*self._n_nodes)
141
+ self._explosion_idx_buffer=np.zeros((1,self._expl_idx_window_size))
142
+ self._expl_idx_counter=0
143
+ self._expl_idx_buffer_counter=0
144
+
145
+ self._pred_node_idx=self._n_nodes-1
146
+
147
+ self._nq=self.nq()
148
+ self._nq_jnts=self._nq-7# assuming floating base
149
+ self._nv=self.nv()
150
+ self._nv_jnts=self._nv-6
151
+
152
+ self._alphas_q_root=np.zeros((7, 1), dtype=self._dtype)
153
+ self._alphas_q_jnts=np.zeros((self._nq_jnts, 1), dtype=self._dtype)
154
+ self._alphas_twist_root=np.zeros((6, 1), dtype=self._dtype)
155
+ self._alphas_v_jnts=np.zeros((self._nv_jnts, 1), dtype=self._dtype)
156
+ self._alphas_a=np.zeros((self._nv, 1), dtype=self._dtype)
157
+ self._alphas_q_root[:, :]=1.0 # default to all open
158
+ self._alphas_q_jnts[:, :]=1.0
159
+ self._alphas_twist_root[:, :]=1.0
160
+ self._alphas_v_jnts[:, :]=1.0
161
+ self._alphas_a[:, :]=1.0
162
+
163
+ def _init_problem(self,
164
+ fixed_jnt_patterns: List[str] = None,
165
+ wheels_patterns: List[str] = None,
166
+ foot_linkname: str = None,
167
+ flight_duration: int = 10,
168
+ post_flight_stance: int = 3,
169
+ step_height: float = 0.12,
170
+ keep_yaw_vert: bool = False,
171
+ yaw_vertical_weight: float = 2.0,
172
+ vertical_landing: bool = False,
173
+ vertical_land_weight: float = 1.0,
174
+ phase_force_reg: float = 1e-2,
175
+ vel_bounds_weight: float = 1.0):
176
+
177
+ self._fixed_jnt_patterns=fixed_jnt_patterns
178
+
179
+ self._config_override()
180
+
181
+ Journal.log(self.__class__.__name__,
182
+ "_init_problem",
183
+ f" Will use horizon config file at {self.config_path}",
184
+ LogType.INFO,
185
+ throw_when_excep=True)
186
+
187
+ self._vel_bounds_weight=vel_bounds_weight
188
+ self._phase_force_reg=phase_force_reg
189
+ self._yaw_vertical_weight=yaw_vertical_weight
190
+ self._vertical_land_weight=vertical_land_weight
191
+ # overrides parent
192
+ self._prb = Problem(self._n_intervals,
193
+ receding=True,
194
+ casadi_type=cs.SX)
195
+ self._prb.setDt(self._dt)
196
+
197
+ if "replace_continuous_joints" in self._custom_opts:
198
+ # continous joints are parametrized in So2
199
+ if self._custom_opts["replace_continuous_joints"]:
200
+ self.urdf = self.urdf.replace('continuous', 'revolute')
201
+ else:
202
+ self.urdf = self.urdf.replace('continuous', 'revolute')
203
+
204
+ self._kin_dyn = casadi_kin_dyn.CasadiKinDyn(self.urdf) # used for getting joint names
205
+ self._assign_controller_side_jnt_names(jnt_names=self._get_robot_jnt_names())
206
+
207
+ self._init_robot_homer()
208
+
209
+ # handle fixed joints
210
+ fixed_joint_map={}
211
+ if self._fixed_jnt_patterns is not None:
212
+ for jnt_name in self._get_robot_jnt_names():
213
+ for fixed_jnt_pattern in self._fixed_jnt_patterns:
214
+ if fixed_jnt_pattern in jnt_name:
215
+ fixed_joint_map.update({f"{jnt_name}":
216
+ self._homer.get_homing_val(jnt_name=jnt_name)})
217
+ break # do not search for other pattern matches
218
+
219
+ if not len(fixed_joint_map)==0: # we need to recreate kin dyn and homers
220
+ Journal.log(self.__class__.__name__,
221
+ "_init_problem",
222
+ f"Will fix following joints: \n{str(fixed_joint_map)}",
223
+ LogType.INFO,
224
+ throw_when_excep=True)
225
+ # with the fixed joint map
226
+ self._kin_dyn = casadi_kin_dyn.CasadiKinDyn(self.urdf,fixed_joints=fixed_joint_map)
227
+ # assign again controlled joints names
228
+ self._assign_controller_side_jnt_names(jnt_names=self._get_robot_jnt_names())
229
+ # updated robot homer for controlled joints
230
+ self._init_robot_homer()
231
+
232
+ # handle continuous joints (need to change homing and retrieve
233
+ # cont jnts indexes) and homing
234
+ self._continuous_joints=self._get_continuous_jnt_names()
235
+ # reduced
236
+ self._continuous_joints_idxs=[]
237
+ self._continuous_joints_idxs_cos=[]
238
+ self._continuous_joints_idxs_sin=[]
239
+ self._continuous_joints_idxs_red=[]
240
+ self._rev_joints_idxs=[]
241
+ self._rev_joints_idxs_red=[]
242
+ # qfull
243
+ self._continuous_joints_idxs_qfull=[]
244
+ self._continuous_joints_idxs_cos_qfull=[]
245
+ self._continuous_joints_idxs_sin_qfull=[]
246
+ self._continuous_joints_idxs_red_qfull=[]
247
+ self._rev_joints_idxs_qfull=[]
248
+ self._rev_joints_idxs_red_qfull=[]
249
+ jnt_homing=[""]*(len(self._homer.get_homing())+len(self._continuous_joints))
250
+ jnt_names=self._get_robot_jnt_names()
251
+ for i in range(len(jnt_names)):
252
+ jnt=jnt_names[i]
253
+ index=self._get_jnt_id(jnt)# accounting for floating joint
254
+ homing_idx=index-7 # homing is only for actuated joints
255
+ homing_value=self._homer.get_homing_val(jnt)
256
+ if jnt in self._continuous_joints:
257
+ jnt_homing[homing_idx]=np.cos(homing_value).item()
258
+ jnt_homing[homing_idx+1]=np.sin(homing_value).item()
259
+ # just actuated joints
260
+ self._continuous_joints_idxs.append(homing_idx) # cos
261
+ self._continuous_joints_idxs.append(homing_idx+1) # sin
262
+ self._continuous_joints_idxs_cos.append(homing_idx)
263
+ self._continuous_joints_idxs_sin.append(homing_idx+1)
264
+ self._continuous_joints_idxs_red.append(i)
265
+ # q full
266
+ self._continuous_joints_idxs_qfull.append(index) # cos
267
+ self._continuous_joints_idxs_qfull.append(index+1) # sin
268
+ self._continuous_joints_idxs_cos_qfull.append(index)
269
+ self._continuous_joints_idxs_sin_qfull.append(index+1)
270
+ self._continuous_joints_idxs_red_qfull.append(i+7)
271
+ else:
272
+ jnt_homing[homing_idx]=homing_value
273
+ # just actuated joints
274
+ self._rev_joints_idxs.append(homing_idx)
275
+ self._rev_joints_idxs_red.append(i)
276
+ # q full
277
+ self._rev_joints_idxs_qfull.append(index)
278
+ self._rev_joints_idxs_red_qfull.append(i+7)
279
+
280
+ self._jnts_q_reduced=None
281
+ if not len(self._continuous_joints)==0:
282
+ cont_joints=", ".join(self._continuous_joints)
283
+
284
+ Journal.log(self.__class__.__name__,
285
+ "_init_problem",
286
+ f"The following continuous joints were found: \n{cont_joints}",
287
+ LogType.INFO,
288
+ throw_when_excep=True)
289
+ # preallocating data
290
+ self._jnts_q_reduced=np.zeros((1,self.nv()-6),dtype=self._dtype)
291
+ self._jnts_q_expanded=np.zeros((self.nq()-7,1),dtype=self._dtype)
292
+ self._full_q_reduced=np.zeros((7+len(jnt_names), self._n_nodes),dtype=self._dtype)
293
+ self._jnts_q_delta_expanded=np.zeros((self.nq()-7,1),dtype=self._dtype)
294
+ else:
295
+ self._custom_opts["replace_continuous_joints"]=True
296
+ Journal.log(self.__class__.__name__,
297
+ "_init_problem",
298
+ f"No continuous joints were found.",
299
+ LogType.INFO,
300
+ throw_when_excep=True)
301
+
302
+ # retrieve wheels indexes (not considering continuous joints,
303
+ # ok just for v, eff, etc..)
304
+ self._wheel_patterns=wheels_patterns
305
+ self._wheels_idxs_v=self._get_wheels_jnt_v_idxs(wheel_patterns=self._wheel_patterns)
306
+ self._f0 = [0, 0, self._kin_dyn.mass()/4*9.81]
307
+
308
+ # we can create an init for the base
309
+ init = self._base_init.tolist() + jnt_homing
310
+
311
+ if foot_linkname is not None:
312
+ FK = self._kin_dyn.fk(foot_linkname) # just to get robot reference height
313
+ ground_level = FK(q=init)['ee_pos']
314
+ self._base_init[2] = -ground_level[2] # override init
315
+
316
+ self._model = FullModelInverseDynamics(problem=self._prb,
317
+ kd=self._kin_dyn,
318
+ q_init=self._homer.get_homing_map(),
319
+ base_init=self._base_init)
320
+
321
+ self._ti = TaskInterface(prb=self._prb,
322
+ model=self._model,
323
+ max_solver_iter=self.max_solver_iter,
324
+ debug = self._debug,
325
+ verbose = self._verbose,
326
+ codegen_workdir = self._codegen_dir)
327
+ self._ti.setTaskFromYaml(self.config_path)
328
+
329
+ # setting initial base pos ref if exists
330
+ base_pos = self._ti.getTask('base_height')
331
+ if base_pos is not None:
332
+ base_pos.setRef(np.atleast_2d(self._base_init).T)
333
+
334
+ self._pm = pymanager.PhaseManager(self._n_nodes, debug=False) # intervals or nodes?????
335
+
336
+ self._gm = GaitManager(self._ti,
337
+ self._pm,
338
+ self._injection_node,
339
+ keep_yaw_vert=keep_yaw_vert,
340
+ yaw_vertical_weight=self._yaw_vertical_weight,
341
+ vertical_landing=vertical_landing,
342
+ landing_vert_weight=self._vertical_land_weight,
343
+ phase_force_reg=self._phase_force_reg,
344
+ custom_opts=self._custom_opts,
345
+ flight_duration=flight_duration,
346
+ post_flight_stance=post_flight_stance,
347
+ step_height=step_height,
348
+ dh=0.0)
349
+
350
+ self._ti.model.q.setBounds(self._ti.model.q0, self._ti.model.q0, nodes=0)
351
+ self._ti.model.v.setBounds(self._ti.model.v0, self._ti.model.v0, nodes=0)
352
+ self._ti.model.q.setInitialGuess(self._ti.model.q0)
353
+ self._ti.model.v.setInitialGuess(self._ti.model.v0)
354
+ for _, cforces in self._ti.model.cmap.items():
355
+ n_contact_f=len(cforces)
356
+ for c in cforces:
357
+ c.setInitialGuess(np.array(self._f0)/n_contact_f)
358
+
359
+ vel_lims = self._model.kd.velocityLimits()
360
+ import horizon.utils as utils
361
+ self._prb.createResidual('vel_lb_barrier', self._vel_bounds_weight*utils.utils.barrier(vel_lims[7:] - self._model.v[7:]))
362
+ self._prb.createResidual('vel_ub_barrier', self._vel_bounds_weight*utils.utils.barrier1(-1 * vel_lims[7:] - self._model.v[7:]))
363
+
364
+ self._meas_lin_a_par=None
365
+ # if self._custom_opts["lin_a_feedback"]:
366
+ # # acceleration feedback on first node
367
+ # self._meas_lin_a_par=self._prb.createParameter(name="lin_a_feedback",
368
+ # dim=3, nodes=0)
369
+ # base_lin_a_prb=self._prb.getInput().getVars()[0:3]
370
+ # self._prb.createConstraint('lin_acceleration_feedback', base_lin_a_prb - self._meas_lin_a_par,
371
+ # nodes=[0])
372
+
373
+ # if not self._open_loop:
374
+ # # we create a residual cost to be used as an attractor to the measured state on the first node
375
+ # # hard constraints injecting meas. states are pure EVIL!
376
+ # prb_state=self._prb.getState()
377
+ # full_state=prb_state.getVars()
378
+ # state_dim=prb_state.getBounds()[0].shape[0]
379
+ # meas_state=self._prb.createParameter(name="measured_state",
380
+ # dim=state_dim, nodes=0)
381
+ # self._prb.createResidual('meas_state_attractor', meas_state_attractor_weight * (full_state - meas_state),
382
+ # nodes=[0])
383
+
384
+ self._ti.finalize()
385
+ self._ti.bootstrap()
386
+
387
+ self._ti.init_inv_dyn_for_res() # we initialize some objects for sol. postprocessing purposes
388
+ self._ti.load_initial_guess()
389
+
390
+ self.n_dofs = self._get_ndofs() # after loading the URDF and creating the controller we
391
+ # know n_dofs -> we assign it (by default = None)
392
+
393
+ self.n_contacts = len(self._model.cmap.keys())
394
+
395
+ # remove variables bounds (before they were necessary to have a good
396
+ # quality bootstrap solution)
397
+ self._q_inf=np.zeros((self.nq(), 1))
398
+ self._q_inf[:, :]=np.inf
399
+ self._v_inf=np.zeros((self.nv(), 1))
400
+ self._v_inf[:, :]=np.inf
401
+ self._ti.model.q.setBounds(-self._q_inf, self._q_inf, nodes=0)
402
+ self._ti.model.v.setBounds(-self._v_inf, self._v_inf, nodes=0)
403
+
404
+ # self.horizon_anal = analyzer.ProblemAnalyzer(self._prb)
405
+
406
+ def get_file_paths(self):
407
+ # can be overriden by child
408
+ paths = super().get_file_paths()
409
+ return paths
410
+
411
+ def _get_quat_remap(self):
412
+ # overrides parent
413
+ return [1, 2, 3, 0] # mapping from robot quat. to Horizon's quaternion convention
414
+
415
+ def _zmp(self, model):
416
+
417
+ num = cs.SX([0, 0])
418
+ den = cs.SX([0])
419
+ pos_contact = dict()
420
+ force_val = dict()
421
+
422
+ q = cs.SX.sym('q', model.nq)
423
+ v = cs.SX.sym('v', model.nv)
424
+ a = cs.SX.sym('a', model.nv)
425
+
426
+ com = model.kd.centerOfMass()(q=q, v=v, a=a)['com']
427
+
428
+ n = cs.SX([0, 0, 1])
429
+ for c in model.fmap.keys():
430
+ pos_contact[c] = model.kd.fk(c)(q=q)['ee_pos']
431
+ force_val[c] = cs.SX.sym('force_val', 3)
432
+ num += (pos_contact[c][0:2] - com[0:2]) * cs.dot(force_val[c], n)
433
+ den += cs.dot(force_val[c], n)
434
+
435
+ zmp = com[0:2] + (num / den)
436
+ input_list = []
437
+ input_list.append(q)
438
+ input_list.append(v)
439
+ input_list.append(a)
440
+
441
+ for elem in force_val.values():
442
+ input_list.append(elem)
443
+
444
+ f = cs.Function('zmp', input_list, [zmp])
445
+ return f
446
+
447
+ def _add_zmp(self):
448
+
449
+ input_zmp = []
450
+
451
+ input_zmp.append(self._model.q)
452
+ input_zmp.append(self._model.v)
453
+ input_zmp.append(self._model.a)
454
+
455
+ for f_var in self._model.fmap.values():
456
+ input_zmp.append(f_var)
457
+
458
+ c_mean = cs.SX([0, 0, 0])
459
+ for c_name, f_var in self._model.fmap.items():
460
+ fk_c_pos = self._kin_dyn.fk(c_name)(q=self._model.q)['ee_pos']
461
+ c_mean += fk_c_pos
462
+
463
+ c_mean /= len(self._model.cmap.keys())
464
+
465
+ zmp_nominal_weight = 10.
466
+ zmp_fun = self._zmp(self._model)(*input_zmp)
467
+
468
+ if 'wheel_joint_1' in self._model.kd.joint_names():
469
+ zmp_residual = self._prb.createIntermediateResidual('zmp', zmp_nominal_weight * (zmp_fun[0:2] - c_mean[0:2]))
470
+
471
+ def _quaternion_multiply(self,
472
+ q1, q2):
473
+ x1, y1, z1, w1 = q1
474
+ x2, y2, z2, w2 = q2
475
+ w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2
476
+ x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
477
+ y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2
478
+ z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2
479
+ return np.array([x, y, z, w])
480
+
481
+ def _get_continuous_jnt_names(self):
482
+ import xml.etree.ElementTree as ET
483
+ root = ET.fromstring(self.urdf)
484
+ continuous_joints = []
485
+ for joint in root.findall('joint'):
486
+ joint_type = joint.get('type')
487
+ if joint_type == 'continuous':
488
+ joint_name = joint.get('name')
489
+ continuous_joints.append(joint_name)
490
+ return continuous_joints
491
+
492
+ def _get_wheels_jnt_v_idxs(self, wheel_patterns: List[str]):
493
+ jnt_names=self._get_robot_jnt_names()
494
+ wheels_idxs=[]
495
+ for i in range(len(jnt_names)):
496
+ jnt_name=jnt_names[i]
497
+ for wheel_pattern in wheel_patterns:
498
+ if wheel_pattern in jnt_name:
499
+ wheels_idxs.append(i)
500
+ break
501
+ return wheels_idxs
502
+
503
+ def _get_jnt_id(self, jnt_name):
504
+ return self._kin_dyn.joint_iq(jnt_name)
505
+
506
+ def _init_rhc_task_cmds(self):
507
+
508
+ rhc_refs = HybridQuadRhcRefs(gait_manager=self._gm,
509
+ robot_index_shm=self.controller_index,
510
+ robot_index_view=0, # when using optimize_mem the view if always of shape 1x...
511
+ namespace=self.namespace,
512
+ safe=False,
513
+ verbose=self._verbose,
514
+ vlevel=VLevel.V2,
515
+ use_force_feedback=self._custom_opts["use_force_feedback"],
516
+ optimize_mem=True)
517
+
518
+ rhc_refs.run()
519
+
520
+ rhc_refs.rob_refs.set_jnts_remapping(jnts_remapping=self._to_controller)
521
+ rhc_refs.rob_refs.set_q_remapping(q_remapping=self._get_quat_remap())
522
+
523
+ rhc_refs.set_default_refs(p_ref=np.atleast_2d(self._base_init)[:, 0:3],
524
+ q_ref=np.atleast_2d(self._base_init)[:, 3:7])
525
+
526
+ return rhc_refs
527
+
528
+ def get_vertex_fnames_from_ti(self):
529
+ tasks=self._ti.task_list
530
+ contact_f_names=[]
531
+ for task in tasks:
532
+ if isinstance(task, ContactTask):
533
+ interaction_task=task.dynamics_tasks[0]
534
+ contact_f_names.append(interaction_task.vertex_frames[0])
535
+ return contact_f_names
536
+
537
+ def _get_contact_names(self):
538
+ # should get contact names from vertex frames
539
+ # list(self._ti.model.cmap.keys())
540
+ return self.get_vertex_fnames_from_ti()
541
+
542
+ def _get_robot_jnt_names(self):
543
+
544
+ joints_names = self._kin_dyn.joint_names()
545
+ to_be_removed = ["universe",
546
+ "reference",
547
+ "world",
548
+ "floating",
549
+ "floating_base"]
550
+ for name in to_be_removed:
551
+ if name in joints_names:
552
+ joints_names.remove(name)
553
+
554
+ return joints_names
555
+
556
+ def _get_ndofs(self):
557
+ return len(self._model.joint_names)
558
+
559
+ def nq(self):
560
+ return self._kin_dyn.nq()
561
+
562
+ def nv(self):
563
+ return self._kin_dyn.nv()
564
+
565
+ def _get_robot_mass(self):
566
+
567
+ return self._kin_dyn.mass()
568
+
569
+ def _get_root_full_q_from_sol(self, node_idx=1):
570
+
571
+ root_q_full=self._ti.solution['q'][0:7, node_idx].reshape(1, 7).astype(self._dtype)
572
+
573
+ np.nan_to_num(root_q_full, nan=1e3, posinf=1e3, neginf=-1e3, copy=False)
574
+ np.clip(a=root_q_full, a_min=-1e3, a_max=1e3, out=root_q_full)
575
+
576
+ return root_q_full
577
+
578
+ def _get_full_q_from_sol(self, node_idx=1):
579
+
580
+ return self._ti.solution['q'][:, node_idx].reshape(1, -1).astype(self._dtype)
581
+
582
+ def _get_root_twist_from_sol(self, node_idx=1):
583
+ # provided in world frame
584
+ twist_base_local=self._get_v_from_sol()[0:6, node_idx].reshape(1, 6)
585
+ # if world_aligned:
586
+ # q_root_rhc = self._get_root_full_q_from_sol(node_idx=node_idx)[:, 0:4]
587
+ # r_base_rhc=Rotation.from_quat(q_root_rhc.flatten()).as_matrix()
588
+ # twist_base_local[:, 0:3] = r_base_rhc @ twist_base_local[0, 0:3]
589
+ # twist_base_local[:, 3:6] = r_base_rhc @ twist_base_local[0, 3:6]
590
+ return twist_base_local
591
+
592
+ def _get_root_a_from_sol(self, node_idx=0):
593
+ # provided in world frame
594
+ a_base_local=self._get_a_from_sol()[0:6, node_idx].reshape(1, 6)
595
+ # if world_aligned:
596
+ # q_root_rhc = self._get_root_full_q_from_sol(node_idx=node_idx)[:, 0:4]
597
+ # r_base_rhc=Rotation.from_quat(q_root_rhc.flatten()).as_matrix()
598
+ # a_base_local[:, 0:3] = r_base_rhc @ a_base_local[0, 0:3]
599
+ # a_base_local[:, 3:6] = r_base_rhc @ v[0, 3:6]
600
+ return a_base_local
601
+
602
+ def _get_jnt_q_from_sol(self, node_idx=0,
603
+ reduce: bool = True,
604
+ clamp: bool = True):
605
+
606
+ full_jnts_q=self._ti.solution['q'][7:, node_idx:node_idx+1].reshape(1,-1).astype(self._dtype)
607
+
608
+ np.nan_to_num(full_jnts_q, nan=1e3, posinf=1e3, neginf=-1e3, copy=False) # in place
609
+ np.clip(a=full_jnts_q, a_max=1e3, a_min=-1e3, out=full_jnts_q) # in place
610
+
611
+ if self._custom_opts["replace_continuous_joints"] or (not reduce):
612
+ if clamp:
613
+ return np.fmod(full_jnts_q, 2*np.pi)
614
+ else:
615
+ return full_jnts_q
616
+ else:
617
+ cos_sin=full_jnts_q[:,self._continuous_joints_idxs].reshape(-1,2)
618
+ # copy rev joint vals
619
+ self._jnts_q_reduced[:, self._rev_joints_idxs_red]=np.fmod(full_jnts_q[:, self._rev_joints_idxs], 2*np.pi).reshape(1, -1)
620
+ # and continuous
621
+ self._jnts_q_reduced[:, self._continuous_joints_idxs_red]=np.arctan2(cos_sin[:, 1], cos_sin[:, 0]).reshape(1,-1)
622
+ return self._jnts_q_reduced
623
+
624
+ def _get_jnt_v_from_sol(self, node_idx=1):
625
+
626
+ jnt_v_sol=self._get_v_from_sol()[6:, node_idx].reshape(1,
627
+ self._nv_jnts)
628
+ np.nan_to_num(jnt_v_sol, nan=1e5, posinf=1e5, neginf=-1e5, copy=False) # in place
629
+ # np.clip(a=jnt_v_sol, a_max=1e5, a_min=-1e5, out=jnt_v_sol) # in place
630
+
631
+ return jnt_v_sol
632
+
633
+ def _get_jnt_a_from_sol(self, node_idx=1):
634
+
635
+ return self._get_a_from_sol()[6:, node_idx].reshape(1,
636
+ self._nv_jnts)
637
+
638
+ def _get_jnt_eff_from_sol(self, node_idx=0):
639
+
640
+ efforts_on_node = self._ti.eval_efforts_on_node(node_idx=node_idx)
641
+
642
+ # if self._custom_opts["only_vel_wheels"]:
643
+
644
+ jnt_efforts=efforts_on_node[6:, 0]
645
+
646
+ if self._custom_opts["only_vel_wheels"] and self._wheels_idxs_v:
647
+ jnt_efforts[self._wheels_idxs_v]=0.0
648
+
649
+ return jnt_efforts.reshape(1,
650
+ self._nv_jnts).astype(self._dtype)
651
+
652
+ def _get_rhc_cost(self):
653
+
654
+ return self._ti.solution["opt_cost"]
655
+
656
+ def _get_rhc_constr_viol(self):
657
+
658
+ return self._ti.solution["residual_norm"]
659
+
660
+ def _get_rhc_nodes_cost(self):
661
+
662
+ cost = self._ti.solver_rti.getCostValOnNodes()
663
+ return cost.reshape((1, -1))
664
+
665
+ def _get_rhc_nodes_constr_viol(self):
666
+
667
+ constr_viol = self._ti.solver_rti.getConstrValOnNodes()
668
+ return constr_viol.reshape((1, -1))
669
+
670
+ def _get_rhc_niter_to_sol(self):
671
+
672
+ return self._ti.solution["n_iter2sol"]
673
+
674
+ def _set_ig(self):
675
+
676
+ shift_num = -1 # shift data by one node
677
+
678
+ x_opt = self._ti.solution['x_opt']
679
+ u_opt = self._ti.solution['u_opt']
680
+
681
+ # building ig for state
682
+ xig = np.roll(x_opt,
683
+ shift_num, axis=1) # rolling state sol.
684
+ for i in range(abs(shift_num)):
685
+ # state on last node is copied to the elements
686
+ # which are "lost" during the shift operation
687
+ xig[:, -1 - i] = x_opt[:, -1]
688
+ # building ig for inputs
689
+ uig = np.roll(u_opt,
690
+ shift_num, axis=1) # rolling state sol.
691
+ for i in range(abs(shift_num)):
692
+ # state on last node is copied to the elements
693
+ # which are "lost" during the shift operation
694
+ uig[:, -1 - i] = u_opt[:, -1]
695
+
696
+ # assigning ig
697
+ self._prb.getState().setInitialGuess(xig)
698
+ self._prb.getInput().setInitialGuess(uig)
699
+
700
+ return xig, uig
701
+
702
+ def _update_open_loop(self):
703
+
704
+ xig, _ = self._set_ig()
705
+
706
+ q_state, v_state, a_state=self._set_is_open()
707
+
708
+ # robot_state=xig[:, 0]
709
+ # # open loop update:
710
+ # self._prb.setInitialState(x0=robot_state) # (xig has been shifted, so node 0
711
+ # # is node 1 in the last opt solution)
712
+
713
+ return q_state, v_state, a_state
714
+
715
+ def _update_closed_loop(self):
716
+
717
+ # set initial guess for controller
718
+ xig, _ = self._set_ig()
719
+ # set initial state
720
+ q_state=None
721
+ v_state=None
722
+ a_state=None
723
+ if self._custom_opts["adaptive_is"]:
724
+ # adaptive closed loop
725
+ q_state, v_state, a_state=self._set_is_adaptive()
726
+ elif self._custom_opts["fully_closed"]:
727
+ q_state, v_state, a_state=self._set_is_full()
728
+ elif self._custom_opts["closed_partial"]:
729
+ q_state, v_state, a_state=self._set_is_partial()
730
+ else:
731
+ Journal.log(self.__class__.__name__,
732
+ "_update_closed_loop",
733
+ "Neither adaptive_is, fully_closed, or closed_partial.",
734
+ LogType.EXCEP,
735
+ throw_when_excep = False)
736
+ q_state, v_state, a_state=self._set_is()
737
+
738
+ return q_state, v_state, a_state
739
+
740
+ def _set_is_open(self):
741
+
742
+ # overriding states with rhc data
743
+ q_full_root=self._get_root_full_q_from_sol(node_idx=1).reshape(-1, 1)
744
+ q_jnts=self._get_jnt_q_from_sol(node_idx=1, reduce=False).reshape(-1, 1)
745
+
746
+ twist_root=self._get_root_twist_from_sol(node_idx=1).reshape(-1, 1)
747
+ v_jnts=self._get_jnt_v_from_sol(node_idx=1).reshape(-1, 1)
748
+
749
+ # rhc variables to be set
750
+ q=self._prb.getVariables("q") # .setBounds()
751
+ root_q_full_rhc=q[0:7] # root full q
752
+ jnts_q_rhc=q[7:] # jnts q
753
+ vel=self._prb.getVariables("v")
754
+ root_twist_rhc=vel[0:6] # lin v.
755
+ jnts_v_rhc=vel[6:] # jnts v
756
+
757
+ self.rhc_refs.set_alpha(alpha=1.0) # fully open
758
+
759
+ # close state on known quantities
760
+ root_q_full_rhc.setBounds(lb=q_full_root,
761
+ ub=q_full_root, nodes=0)
762
+ jnts_q_rhc.setBounds(lb=q_jnts,
763
+ ub=q_jnts, nodes=0)
764
+ root_twist_rhc.setBounds(lb=twist_root,
765
+ ub=twist_root, nodes=0)
766
+ jnts_v_rhc.setBounds(lb=v_jnts,
767
+ ub=v_jnts, nodes=0)
768
+
769
+ # return state used for feedback
770
+ q_state=np.concatenate((q_full_root, q_jnts),
771
+ axis=0)
772
+ v_state=np.concatenate((twist_root, v_jnts),
773
+ axis=0)
774
+
775
+ return (q_state, v_state, None)
776
+
777
+ def _set_is_full(self):
778
+
779
+ # measurements
780
+ q_full_root = self.robot_state.root_state.get(data_type="q_full", robot_idxs=self.controller_index_np).reshape(-1, 1)
781
+ v_root = self.robot_state.root_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
782
+ omega = self.robot_state.root_state.get(data_type="omega", robot_idxs=self.controller_index_np).reshape(-1, 1)
783
+ a_root = self.robot_state.root_state.get(data_type="a_full", robot_idxs=self.controller_index_np).reshape(-1, 1)
784
+
785
+ q_jnts = self.robot_state.jnts_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
786
+ v_jnts = self.robot_state.jnts_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
787
+ a_jnts = self.robot_state.jnts_state.get(data_type="a", robot_idxs=self.controller_index_np).reshape(-1, 1)
788
+
789
+ if (not len(self._continuous_joints)==0): # we need do expand some meas. rev jnts to So2
790
+ self._jnts_q_expanded[self._rev_joints_idxs, :]=q_jnts[self._rev_joints_idxs_red ,:]
791
+ self._jnts_q_expanded[self._continuous_joints_idxs_cos, :]=np.cos(q_jnts[self._continuous_joints_idxs_red, :]) # cos
792
+ self._jnts_q_expanded[self._continuous_joints_idxs_sin, :]=np.sin(q_jnts[self._continuous_joints_idxs_red, :]) # sin
793
+ q_jnts=self._jnts_q_expanded.reshape(-1,1)
794
+
795
+ # rhc variables to be set
796
+ q=self._prb.getVariables("q") # .setBounds()
797
+ root_full_q_rhc=q[0:7] # root p
798
+ jnts_q_rhc=q[7:] # jnts q
799
+ vel=self._prb.getVariables("v")
800
+ root_v_rhc=vel[0:3] # lin v.
801
+ root_omega_rhc=vel[3:6] # omega
802
+ jnts_v_rhc=vel[6:] # jnts v
803
+ acc=self._prb.getVariables("a")
804
+ lin_a_prb=acc[0:3] # lin acc
805
+
806
+ self.rhc_refs.set_alpha(alpha=0.0) # non-adaptive
807
+
808
+ root_full_q_rhc.setBounds(lb=q_full_root,
809
+ ub=q_full_root, nodes=0)
810
+ jnts_q_rhc.setBounds(lb=q_jnts,
811
+ ub=q_jnts, nodes=0)
812
+ root_v_rhc.setBounds(lb=v_root,
813
+ ub=v_root, nodes=0) # leaving lin v of the base free (estimated from constraints)
814
+ root_omega_rhc.setBounds(lb=omega,
815
+ ub=omega, nodes=0)
816
+ jnts_v_rhc.setBounds(lb=v_jnts,
817
+ ub=v_jnts, nodes=0)
818
+ if self._custom_opts["lin_a_feedback"]:
819
+ # write base lin 13793197 from meas
820
+ lin_a_prb.setBounds(lb=a_root[0:3, :],
821
+ ub=a_root[0:3, :],
822
+ nodes=0)
823
+
824
+ # return state used for feedback
825
+ q_state=np.concatenate((q_full_root, q_jnts),
826
+ axis=0)
827
+ v_state=np.concatenate((v_root, omega, v_jnts),
828
+ axis=0)
829
+ a_state=np.concatenate((a_root, a_jnts),
830
+ axis=0)
831
+
832
+ return (q_state, v_state, a_state)
833
+
834
+ def _set_is_partial(self):
835
+
836
+ # measurements
837
+ p_root = self.robot_state.root_state.get(data_type="p", robot_idxs=self.controller_index_np).reshape(-1, 1)
838
+ q_root = self.robot_state.root_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
839
+ v_root = self.robot_state.root_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
840
+ omega = self.robot_state.root_state.get(data_type="omega", robot_idxs=self.controller_index_np).reshape(-1, 1)
841
+ a_root = self.robot_state.root_state.get(data_type="a_full", robot_idxs=self.controller_index_np).reshape(-1, 1)
842
+
843
+ q_jnts = self.robot_state.jnts_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
844
+ v_jnts = self.robot_state.jnts_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
845
+ a_jnts = self.robot_state.jnts_state.get(data_type="a", robot_idxs=self.controller_index_np).reshape(-1, 1)
846
+
847
+ if (not len(self._continuous_joints)==0): # we need do expand some meas. rev jnts to So2
848
+ self._jnts_q_expanded[self._rev_joints_idxs, :]=q_jnts[self._rev_joints_idxs_red ,:]
849
+ self._jnts_q_expanded[self._continuous_joints_idxs_cos, :]=np.cos(q_jnts[self._continuous_joints_idxs_red, :]) # cos
850
+ self._jnts_q_expanded[self._continuous_joints_idxs_sin, :]=np.sin(q_jnts[self._continuous_joints_idxs_red, :]) # sin
851
+ q_jnts=self._jnts_q_expanded.reshape(-1,1)
852
+
853
+ # overriding states with rhc data (-> all overridden state are open looop)
854
+ root_q_full_from_rhc=self._get_root_full_q_from_sol(node_idx=1).reshape(-1, 1)
855
+ root_p_from_rhc=root_q_full_from_rhc[0:3, :]
856
+ p_root[:, :]=root_p_from_rhc # position is always open loop
857
+ if not self._custom_opts["estimate_v_root"]:
858
+ v_root[:, :]=self._get_root_twist_from_sol(node_idx=1)[:, 0:3].reshape(-1, 1)
859
+ # override v jnts with the ones from controller
860
+ if not self._custom_opts["use_jnt_v_feedback"]:
861
+ v_jnts[:, :]=self._get_jnt_v_from_sol(node_idx=1).reshape(-1, 1)
862
+ # v_jnts[:, :]=self._get_jnt_v_from_sol(node_idx=1).reshape(-1, 1)
863
+ # root_twist_from_rhc=self._get_root_twist_from_sol(node_idx=1)
864
+ # root_v_from_rhc=root_twist_from_rhc[:, 0:3].reshape(-1, 1)
865
+ # root_omega_from_rhc=root_twist_from_rhc[:, 3:6].reshape(-1, 1)
866
+ # jnt_q_from_rhc=self._get_jnt_q_from_sol(node_idx=1,reduce=False,clamp=False).reshape(-1, 1)
867
+ # jnt_v_from_rhc=self._get_jnt_v_from_sol(node_idx=1).reshape(-1, 1)
868
+
869
+ # rhc variables to be set
870
+ q=self._prb.getVariables("q") # .setBounds()
871
+ root_p_rhc=q[0:3] # root p
872
+ root_q_rhc=q[3:7] # root orientation
873
+ jnts_q_rhc=q[7:] # jnts q
874
+ vel=self._prb.getVariables("v")
875
+ root_v_rhc=vel[0:3] # lin v.
876
+ root_omega_rhc=vel[3:6] # omega
877
+ jnts_v_rhc=vel[6:] # jnts v
878
+ acc=self._prb.getVariables("a")
879
+ lin_a_prb=acc[0:3] # lin acc
880
+
881
+ self.rhc_refs.set_alpha(alpha=0.0) # non-adaptive
882
+
883
+ root_p_rhc.setBounds(lb=p_root,
884
+ ub=p_root, nodes=0)
885
+ root_q_rhc.setBounds(lb=q_root,
886
+ ub=q_root, nodes=0)
887
+ jnts_q_rhc.setBounds(lb=q_jnts,
888
+ ub=q_jnts, nodes=0)
889
+ if self._custom_opts["estimate_v_root"]:
890
+ root_v_rhc.setBounds(lb=-self._v_inf[0:3],
891
+ ub=self._v_inf[0:3], nodes=0) # leaving lin v of the base free (estimated from constraints)
892
+ else: # get it from controller
893
+ root_v_rhc.setBounds(lb=v_root,
894
+ ub=v_root, nodes=0)
895
+ root_omega_rhc.setBounds(lb=omega,
896
+ ub=omega, nodes=0)
897
+ jnts_v_rhc.setBounds(lb=v_jnts,
898
+ ub=v_jnts, nodes=0)
899
+ if self._custom_opts["lin_a_feedback"]:
900
+ # write base lin 13793197 from meas
901
+ lin_a_prb.setBounds(lb=a_root[0:3, :],
902
+ ub=a_root[0:3, :],
903
+ nodes=0)
904
+
905
+ # return state used for feedback
906
+ q_state=np.concatenate((p_root, q_root, q_jnts),
907
+ axis=0)
908
+ v_state=np.concatenate((v_root, omega, v_jnts),
909
+ axis=0)
910
+ a_state=np.concatenate((a_root, a_jnts),
911
+ axis=0)
912
+
913
+ return (q_state, v_state, a_state)
914
+
915
+ def _set_is_adaptive(self):
916
+
917
+ # measurements
918
+ p_root = self.robot_state.root_state.get(data_type="p", robot_idxs=self.controller_index_np).reshape(-1, 1)
919
+ q_root = self.robot_state.root_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
920
+ v_root = self.robot_state.root_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
921
+ omega = self.robot_state.root_state.get(data_type="omega", robot_idxs=self.controller_index_np).reshape(-1, 1)
922
+ a_root = self.robot_state.root_state.get(data_type="a_full", robot_idxs=self.controller_index_np).reshape(-1, 1)
923
+
924
+ q_jnts = self.robot_state.jnts_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
925
+ v_jnts = self.robot_state.jnts_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
926
+ a_jnts = self.robot_state.jnts_state.get(data_type="a", robot_idxs=self.controller_index_np).reshape(-1, 1)
927
+
928
+ # rhc variables to be set
929
+ q=self._prb.getVariables("q") # .setBounds()
930
+ root_p_rhc=q[0:3] # root p
931
+ root_q_rhc=q[3:7] # root orientation
932
+ jnts_q_rhc=q[7:] # jnts q
933
+ vel=self._prb.getVariables("v")
934
+ root_v_rhc=vel[0:3] # lin v.
935
+ root_omega_rhc=vel[3:6] # omega
936
+ jnts_v_rhc=vel[6:] # jnts v
937
+ acc=self._prb.getVariables("a")
938
+ lin_a_prb=acc[0:3] # lin acc
939
+
940
+ # getting prediction defects
941
+ root_q_delta=self.rhc_pred_delta.root_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
942
+ jnt_q_delta=self.rhc_pred_delta.jnts_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
943
+ jnt_v_delta=self.rhc_pred_delta.jnts_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
944
+ v_root_delta = self.rhc_pred_delta.root_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
945
+ omega_root_delta = self.rhc_pred_delta.root_state.get(data_type="omega", robot_idxs=self.controller_index_np).reshape(-1, 1)
946
+ a_root_delta = self.rhc_pred_delta.root_state.get(data_type="a_full", robot_idxs=self.controller_index_np).reshape(-1, 1)
947
+
948
+ # close state on known quantities, estimate some (e.g. lin vel) and
949
+ # open loop if thing start to explode
950
+ alpha_now=1.0
951
+ delta=0.0
952
+ if self._custom_opts["alpha_from_outside"]:
953
+ alpha_now=self.rhc_refs.get_alpha()
954
+ else: # "autotuned" alpha
955
+ if self._custom_opts["estimate_v_root"]: # we copmute delta based on jnt v (since we use meas.)
956
+ delta=np.max(np.abs(jnt_v_delta))
957
+ else:
958
+ delta=np.max(np.abs(omega_root_delta))
959
+ # fail_idx=self._get_failure_index()
960
+ # fail_idx=self._get_explosion_idx()/self._fail_idx_thresh
961
+ alpha_now=(np.tanh(2*self._alpha_half*(delta-self._alpha_half))+1)/2.0
962
+
963
+ bound_relaxation=-np.log(1-alpha_now+1e-16) # [~0, ~inf] if alpha_now [0, 1]
964
+ self.rhc_refs.set_alpha(alpha=alpha_now) # also writes on shared mem for db
965
+ self.rhc_refs.set_bound_relax(bound_relax=bound_relaxation) # also writes on shared mem for db
966
+
967
+ self._alphas_q_root[:]=alpha_now # for now single alpha for everything
968
+ self._alphas_q_jnts[:]=alpha_now
969
+ self._alphas_twist_root[:]=alpha_now
970
+ self._alphas_v_jnts[:]=alpha_now
971
+ self._alphas_a[:]=alpha_now
972
+ if not self._custom_opts["estimate_v_root"]:
973
+ self._alphas_twist_root[0:3]=1.0 # open
974
+ self._alphas_v_jnts[:]=1.0 # open
975
+
976
+ # position is always open loop
977
+ root_q_full_from_rhc=self._get_root_full_q_from_sol(node_idx=1).reshape(-1, 1)
978
+ root_p_from_rhc=root_q_full_from_rhc[0:3, :]
979
+ p_root[:, :]=root_p_from_rhc
980
+
981
+ # expaning meas q if continuous joints
982
+ if (not len(self._continuous_joints)==0): # we need do expand some meas. rev jnts to So2
983
+ self._jnts_q_expanded[self._rev_joints_idxs, :]=q_jnts[self._rev_joints_idxs_red ,:]
984
+ self._jnts_q_expanded[self._continuous_joints_idxs_cos, :]=np.cos(q_jnts[self._continuous_joints_idxs_red, :]) # cos
985
+ self._jnts_q_expanded[self._continuous_joints_idxs_sin, :]=np.sin(q_jnts[self._continuous_joints_idxs_red, :]) # sin
986
+
987
+ # continous joints position is always open loop, but we need a delta vector of matching dimension
988
+ q_jnts_from_rhc=self._get_jnt_q_from_sol(node_idx=1).reshape(-1, 1)
989
+
990
+ self._jnts_q_delta_expanded[self._rev_joints_idxs, :]=jnt_q_delta[self._rev_joints_idxs_red ,:]
991
+
992
+ self._jnts_q_delta_expanded[self._continuous_joints_idxs_cos, :]=\
993
+ np.cos(q_jnts_from_rhc[self._continuous_joints_idxs_red, :]) - \
994
+ np.cos(q_jnts[self._continuous_joints_idxs_red, :])
995
+ self._jnts_q_delta_expanded[self._continuous_joints_idxs_sin, :]=\
996
+ np.sin(q_jnts_from_rhc[self._continuous_joints_idxs_red, :]) - \
997
+ np.sin(q_jnts[self._continuous_joints_idxs_red, :])
998
+
999
+ q_jnts=self._jnts_q_expanded.reshape(-1,1) # overriting with expanded jnts
1000
+ jnt_q_delta=self._jnts_q_delta_expanded.reshape(-1, 1) # overriting with expanded jnts
1001
+
1002
+ self._alphas_q_jnts[self._continuous_joints_idxs_cos, :]=1.0 # open loop
1003
+ self._alphas_q_jnts[self._continuous_joints_idxs_sin, :]=1.0 # open loop
1004
+
1005
+ # self._alphas_v_jnts[self._continuous_joints_idxs_red, :]=0.0 # open loop
1006
+
1007
+ root_p_rhc.setBounds(lb=p_root,
1008
+ ub=p_root, nodes=0)
1009
+ root_q_rhc.setBounds(lb=q_root+self._alphas_q_root[3:7]*root_q_delta,
1010
+ ub=q_root+self._alphas_q_root[3:7]*root_q_delta, nodes=0)
1011
+ jnts_q_rhc.setBounds(lb=q_jnts+self._alphas_q_jnts*jnt_q_delta,
1012
+ ub=q_jnts+self._alphas_q_jnts*jnt_q_delta, nodes=0)
1013
+ if self._custom_opts["estimate_v_root"]:
1014
+ root_v_rhc.setBounds(lb=-self._v_inf[0:3],
1015
+ ub=self._v_inf[0:3], nodes=0)
1016
+ else:
1017
+ root_v_rhc.setBounds(lb=v_root+self._alphas_twist_root[0:3, :]*v_root_delta,
1018
+ ub=v_root+self._alphas_twist_root[0:3, :]*v_root_delta, nodes=0)
1019
+ root_omega_rhc.setBounds(lb=omega+self._alphas_twist_root[3:6, :]*omega_root_delta,
1020
+ ub=omega+self._alphas_twist_root[3:6, :]*omega_root_delta, nodes=0)
1021
+ jnts_v_rhc.setBounds(lb=v_jnts+self._alphas_v_jnts*jnt_v_delta,
1022
+ ub=v_jnts+self._alphas_v_jnts*jnt_v_delta, nodes=0)
1023
+ if self._custom_opts["lin_a_feedback"]:
1024
+ lin_a_prb.setBounds(lb=a_root[0:3, :]+self._alphas_a[0:3]*a_root_delta[0:3, :],
1025
+ ub=a_root[0:3, :]+self._alphas_a[0:3]*a_root_delta[0:3, :],
1026
+ nodes=0)
1027
+
1028
+ # return state used for feedback
1029
+ q_state=np.concatenate((p_root, q_root, q_jnts),
1030
+ axis=0)
1031
+ v_state=np.concatenate((v_root, omega, v_jnts),
1032
+ axis=0)
1033
+ a_state=np.concatenate((a_root, a_jnts),
1034
+ axis=0)
1035
+
1036
+ return (q_state, v_state, a_state)
1037
+
1038
+ def _solve(self):
1039
+
1040
+ if self._debug:
1041
+ return self._db_solve()
1042
+ else:
1043
+ return self._min_solve()
1044
+
1045
+ def _min_solve(self):
1046
+ # minimal solve version -> no debug
1047
+ robot_qstate=None
1048
+ robot_vstate=None
1049
+ robot_astate=None
1050
+ if self._open_loop:
1051
+ robot_qstate, robot_vstate, robot_astate = self._update_open_loop() # updates the TO ig and
1052
+ # initial conditions using data from the solution itself
1053
+ else:
1054
+ robot_qstate, robot_vstate, robot_astate = self._update_closed_loop() # updates the TO ig and
1055
+ # initial conditions using robot measurements
1056
+
1057
+ self._pm.shift() # shifts phases of one dt
1058
+ if self._refs_in_hor_frame:
1059
+ # q_base=self.robot_state.root_state.get(data_type="q",
1060
+ # robot_idxs=self.controller_index).reshape(-1, 1)
1061
+ # q_full=self._get_full_q_from_sol(node_idx=1).reshape(-1, 1)
1062
+ # using internal base pose from rhc. in case of closed loop, it will be the meas state
1063
+ force_norm=None
1064
+ if self._custom_opts["use_force_feedback"]:
1065
+ contact_forces=self.robot_state.contact_wrenches.get(data_type="f",
1066
+ robot_idxs=self.controller_index_np,
1067
+ contact_name=None).reshape(self.n_contacts,3)
1068
+ force_norm=np.linalg.norm(contact_forces, axis=1)
1069
+ self.rhc_refs.step(qstate=robot_qstate, vstate=robot_vstate,
1070
+ force_norm=force_norm)
1071
+ else:
1072
+ self.rhc_refs.step()
1073
+
1074
+ try:
1075
+ converged = self._ti.rti() # solves the problem
1076
+ self.sol_counter = self.sol_counter + 1
1077
+ return not self._check_rhc_failure()
1078
+ except Exception as e: # fail in case of exceptions
1079
+ return False
1080
+
1081
+ def _db_solve(self):
1082
+
1083
+ self._timer_start = time.perf_counter()
1084
+
1085
+ robot_qstate=None
1086
+ robot_vstate=None
1087
+ robot_astate=None
1088
+ if self._open_loop:
1089
+ robot_qstate, robot_vstate, robot_astate = self._update_open_loop() # updates the TO ig and
1090
+ # initial conditions using data from the solution itself
1091
+ else:
1092
+ robot_qstate, robot_vstate, robot_astate = self._update_closed_loop() # updates the TO ig and
1093
+ # initial conditions using robot measurements
1094
+
1095
+ self._prb_update_time = time.perf_counter()
1096
+ self._pm.shift() # shifts phases of one dt
1097
+ self._phase_shift_time = time.perf_counter()
1098
+
1099
+ if self._refs_in_hor_frame:
1100
+ # q_base=self.robot_state.root_state.get(data_type="q",
1101
+ # robot_idxs=self.controller_index).reshape(-1, 1)
1102
+ # q_full=self._get_full_q_from_sol(node_idx=1).reshape(-1, 1)
1103
+ # using internal base pose from rhc. in case of closed loop, it will be the meas state
1104
+ force_norm=None
1105
+ if self._custom_opts["use_force_feedback"]:
1106
+ contact_forces=self.robot_state.contact_wrenches.get(data_type="f",
1107
+ robot_idxs=self.controller_index_np,
1108
+ contact_name=None).reshape(self.n_contacts,3)
1109
+ force_norm=np.linalg.norm(contact_forces, axis=1)
1110
+ self.rhc_refs.step(qstate=robot_qstate, vstate=robot_vstate,
1111
+ force_norm=force_norm)
1112
+ else:
1113
+ self.rhc_refs.step()
1114
+
1115
+ self._task_ref_update_time = time.perf_counter()
1116
+
1117
+ try:
1118
+ converged = self._ti.rti() # solves the problem
1119
+ self._rti_time = time.perf_counter()
1120
+ self.sol_counter = self.sol_counter + 1
1121
+ self._update_db_data()
1122
+ return not self._check_rhc_failure()
1123
+ except Exception as e: # fail in case of exceptions
1124
+ if self._verbose:
1125
+ exception = f"Rti() for controller {self.controller_index} failed" + \
1126
+ f" with exception{type(e).__name__}"
1127
+ Journal.log(self.__class__.__name__,
1128
+ "solve",
1129
+ exception,
1130
+ LogType.EXCEP,
1131
+ throw_when_excep = False)
1132
+ self._update_db_data()
1133
+ return False
1134
+
1135
+ def _get_fail_idx(self):
1136
+
1137
+ self._explosion_idx_buffer[:, self._expl_idx_buffer_counter]=self._get_explosion_idx()
1138
+ self._expl_idx_buffer_counter+=1
1139
+ self._expl_idx_counter+=1
1140
+ if self._expl_idx_counter%self._expl_idx_window_size==0:
1141
+ self._expl_idx_buffer_counter=0 # restart from 0
1142
+
1143
+ running_avrg=np.mean(self._explosion_idx_buffer).item()
1144
+
1145
+ return running_avrg
1146
+
1147
+ def _get_explosion_idx(self):
1148
+ explosion_index = self._get_rhc_constr_viol() + self._get_rhc_cost()*self._fail_idx_scale
1149
+ return explosion_index
1150
+
1151
+ def _update_db_data(self):
1152
+
1153
+ self._profiling_data_dict["problem_update_dt"] = self._prb_update_time - self._timer_start
1154
+ self._profiling_data_dict["phases_shift_dt"] = self._phase_shift_time - self._prb_update_time
1155
+ self._profiling_data_dict["task_ref_update"] = self._task_ref_update_time - self._phase_shift_time
1156
+ self._profiling_data_dict["rti_solve_dt"] = self._rti_time - self._task_ref_update_time
1157
+ self.rhc_costs.update(self._ti.solver_rti.getCostsValues())
1158
+ self.rhc_constr.update(self._ti.solver_rti.getConstraintsValues())
1159
+
1160
+ def _reset(self):
1161
+
1162
+ # reset task interface (ig, solvers, etc..) +
1163
+ # phase manager and sets bootstap as solution
1164
+ self._gm.reset()
1165
+ self._explosion_idx_buffer[:, :]=self._get_explosion_idx() # reset with data from reset solution
1166
+ self._expl_idx_counter=0.0
1167
+ self._expl_idx_buffer_counter=0
1168
+
1169
+ def _get_cost_data(self):
1170
+
1171
+ cost_dict = self._ti.solver_rti.getCostsValues()
1172
+ cost_names = list(cost_dict.keys())
1173
+ cost_dims = [1] * len(cost_names) # costs are always scalar
1174
+ return cost_names, cost_dims
1175
+
1176
+ def _get_constr_data(self):
1177
+
1178
+ constr_dict = self._ti.solver_rti.getConstraintsValues()
1179
+ constr_names = list(constr_dict.keys())
1180
+ constr_dims = [-1] * len(constr_names)
1181
+ i = 0
1182
+ for constr in constr_dict:
1183
+ constr_val = constr_dict[constr]
1184
+ constr_shape = constr_val.shape
1185
+ constr_dims[i] = constr_shape[0]
1186
+ i+=1
1187
+ return constr_names, constr_dims
1188
+
1189
+ def _get_q_from_sol(self):
1190
+ full_q=self._ti.solution['q'].astype(self._dtype)
1191
+ if self._custom_opts["replace_continuous_joints"]:
1192
+ return full_q
1193
+ else:
1194
+ cont_jnts=full_q[self._continuous_joints_idxs_qfull, :]
1195
+ cos=cont_jnts[::2, :]
1196
+ sin=cont_jnts[1::2, :]
1197
+ # copy root
1198
+ self._full_q_reduced[0:7, :]=full_q[0:7, :]
1199
+ # copy rev joint vals
1200
+ self._full_q_reduced[self._rev_joints_idxs_red_qfull, :]=full_q[self._rev_joints_idxs_qfull, :]
1201
+ # and continuous
1202
+ angle=np.arctan2(sin, cos)
1203
+ self._full_q_reduced[self._continuous_joints_idxs_red_qfull, :]=angle
1204
+ return self._full_q_reduced
1205
+
1206
+ def _get_v_from_sol(self):
1207
+ return self._ti.solution['v'].astype(self._dtype)
1208
+
1209
+ def _get_a_from_sol(self):
1210
+ return self._ti.solution['a'].astype(self._dtype)
1211
+
1212
+ def _get_a_dot_from_sol(self):
1213
+ return None
1214
+
1215
+ def _get_f_from_sol(self):
1216
+ # to be overridden by child class
1217
+ contact_names =self._get_contacts() # we use controller-side names
1218
+ try:
1219
+ data=[]
1220
+ for key in contact_names:
1221
+ contact_f=self._ti.solution["f_" + key].astype(self._dtype)
1222
+ np.nan_to_num(contact_f, nan=1e6, posinf=1e6, neginf=-1e6, copy=False)
1223
+ np.clip(a=contact_f, a_max=1e6, a_min=-1e6, out=contact_f)
1224
+ data.append(contact_f)
1225
+ return np.concatenate(data, axis=0)
1226
+ except:
1227
+ return None
1228
+
1229
+ def _get_f_dot_from_sol(self):
1230
+ # to be overridden by child class
1231
+ return None
1232
+
1233
+ def _get_eff_from_sol(self):
1234
+ # to be overridden by child class
1235
+ return None
1236
+
1237
+ def _get_cost_from_sol(self,
1238
+ cost_name: str):
1239
+ return self.rhc_costs[cost_name]
1240
+
1241
+ def _get_constr_from_sol(self,
1242
+ constr_name: str):
1243
+ return self.rhc_constr[constr_name]
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/isaac_world_interface.py ADDED
The diff for this file is too large to render. See raw diff
 
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/jnt_imp_config.yaml ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ XBotInterface: # just used for retrieving homing in sim with xbot_mujoco
2
+ urdf_path: $PWD/xmj_env_files/centauro.urdf
3
+ srdf_path: $PWD/xmj_env_files/centauro.srdf
4
+
5
+ # defaults
6
+ motor_pd:
7
+ "j_arm*_1": [500, 10]
8
+ "j_arm*_2": [500, 10]
9
+ "j_arm*_3": [500, 10]
10
+ "j_arm*_4": [500, 10]
11
+ "j_arm*_5": [100, 5]
12
+ "j_arm*_6": [100, 5]
13
+ "j_arm*_7": [100, 5]
14
+ "hip_yaw_*": [3000, 30]
15
+ "hip_pitch_*": [3000, 30]
16
+ "knee_pitch_*": [3000, 30]
17
+ "ankle_pitch_*": [1000, 10]
18
+ "ankle_yaw_*": [300, 10]
19
+ "neck_pitch": [10, 1]
20
+ "neck_yaw": [10, 1]
21
+ "torso_yaw": [1000, 30]
22
+ "j_wheel_*": [0, 30]
23
+ "velodyne_*": [10, 1]
24
+ "d435_*": [10, 1]
25
+
26
+ startup_motor_pd: # iannis combo
27
+ "j_arm*_1": [500, 30]
28
+ "j_arm*_2": [500, 30]
29
+ "j_arm*_3": [500, 30]
30
+ "j_arm*_4": [500, 30]
31
+ "j_arm*_5": [100, 5]
32
+ "j_arm*_6": [100, 5]
33
+ "j_arm*_7": [100, 5]
34
+ "hip_yaw_*": [200, 60]
35
+ "hip_pitch_*": [200, 60]
36
+ "knee_pitch_*": [200, 60]
37
+ "ankle_pitch_*": [200, 60]
38
+ "ankle_yaw_*": [120, 30]
39
+ "neck_pitch": [10, 1]
40
+ "neck_yaw": [10, 1]
41
+ "torso_yaw": [1000, 30]
42
+ "j_wheel_*": [0, 30]
43
+ "velodyne_*": [10, 1]
44
+ "d435_*": [10, 1]
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/jnt_imp_config_no_yaw.yaml ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ XBotInterface: # just used for retrieving homing in sim with xbot_mujoco
2
+ urdf_path: $PWD/xmj_env_files/centauro.urdf
3
+ srdf_path: $PWD/xmj_env_files/centauro.srdf
4
+
5
+ # defaults
6
+ motor_pd:
7
+ "j_arm*_1": [500, 10]
8
+ "j_arm*_2": [500, 10]
9
+ "j_arm*_3": [500, 10]
10
+ "j_arm*_4": [500, 10]
11
+ "j_arm*_5": [100, 5]
12
+ "j_arm*_6": [100, 5]
13
+ "j_arm*_7": [100, 5]
14
+ "hip_yaw_*": [3000, 30]
15
+ "hip_pitch_*": [3000, 30]
16
+ "knee_pitch_*": [3000, 30]
17
+ "ankle_pitch_*": [1000, 10]
18
+ "ankle_yaw_*": [300, 10]
19
+ "neck_pitch": [10, 1]
20
+ "neck_yaw": [10, 1]
21
+ "torso_yaw": [1000, 30]
22
+ "j_wheel_*": [0, 30]
23
+ "velodyne_*": [10, 1]
24
+ "d435_*": [10, 1]
25
+
26
+ startup_motor_pd: # iannis combo
27
+ "j_arm*_1": [500, 30]
28
+ "j_arm*_2": [500, 30]
29
+ "j_arm*_3": [500, 30]
30
+ "j_arm*_4": [500, 30]
31
+ "j_arm*_5": [100, 5]
32
+ "j_arm*_6": [100, 5]
33
+ "j_arm*_7": [100, 5]
34
+ "hip_yaw_*": [200, 60]
35
+ "hip_pitch_*": [200, 60]
36
+ "knee_pitch_*": [200, 60]
37
+ "ankle_pitch_*": [200, 60]
38
+ "ankle_yaw_*": [600, 10]
39
+ "neck_pitch": [10, 1]
40
+ "neck_yaw": [10, 1]
41
+ "torso_yaw": [1000, 30]
42
+ "j_wheel_*": [0, 30]
43
+ "velodyne_*": [10, 1]
44
+ "d435_*": [10, 1]
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/launch_control_cluster.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import argparse
3
+ import multiprocessing as mp
4
+ import importlib.util
5
+ import inspect
6
+
7
+ from aug_mpc.utils.custom_arg_parsing import generate_custom_arg_dict
8
+
9
+ from EigenIPC.PyEigenIPC import Journal, LogType
10
+
11
+ this_script_name = os.path.splitext(os.path.basename(os.path.abspath(__file__)))[0]
12
+
13
+ # Function to dynamically import a module from a specific file path
14
+ def import_env_module(env_path):
15
+ spec = importlib.util.spec_from_file_location("env_module", env_path)
16
+ env_module = importlib.util.module_from_spec(spec)
17
+ spec.loader.exec_module(env_module)
18
+ return env_module
19
+
20
+ if __name__ == "__main__":
21
+
22
+ # Parse command line arguments for CPU affinity
23
+ parser = argparse.ArgumentParser(description="Set CPU affinity for the script.")
24
+ parser.add_argument('--ns', type=str, help='Namespace to be used for cluster shared memory')
25
+ parser.add_argument('--urdf_path', type=str, help='Robot description package path for URDF ')
26
+ parser.add_argument('--srdf_path', type=str, help='Robot description package path for SRDF ')
27
+ parser.add_argument('--size', type=int, help='cluster size', default=1)
28
+
29
+ # Replacing argparse.BooleanOptionalAction with 'store_true' and 'store_false' for compatibility with Python 3.8
30
+ parser.add_argument('--cloop',action='store_true', help='whether to use RHC controllers in closed loop mode')
31
+ parser.add_argument('--cluster_dt', type=float, default=0.05, help='dt used by MPC controllers for discretization')
32
+ parser.add_argument('--n_nodes', type=int, default=31, help='n nodes used by MPC controllers')
33
+
34
+ parser.add_argument('--verbose',action='store_true', help='run in verbose mode')
35
+
36
+ parser.add_argument('--enable_debug',action='store_true', help='enable debug mode for cluster client and all controllers')
37
+
38
+ parser.add_argument('--dmpdir', type=str, help='directory where data is dumped', default="/root/aux_data")
39
+
40
+ parser.add_argument('--no_mp_fork',action='store_true', help='whether to multiprocess with forkserver context')
41
+
42
+ parser.add_argument('--set_affinity',action='store_true', help='set affinity to a core for each controller')
43
+
44
+ parser.add_argument('--comment', type=str, help='Any useful comment associated with this run', default="")
45
+ parser.add_argument('--timeout_ms', type=int, help='connection timeout after which the script self-terminates', default=60000)
46
+ parser.add_argument('--codegen_override_dir', type=str, help='Path to base dir where codegen is to be loaded', default="")
47
+
48
+ parser.add_argument('--custom_args_names', nargs='+', default=None,
49
+ help='list of custom arguments names')
50
+ parser.add_argument('--custom_args_vals', nargs='+', default=None,
51
+ help='list of custom arguments values')
52
+ parser.add_argument('--custom_args_dtype', nargs='+', default=None,
53
+ help='list of custom arguments data types')
54
+
55
+ parser.add_argument('--cluster_client_fname', type=str,
56
+ default="aug_mpc.controllers.rhc.hybrid_quad_client",
57
+ help="cluster client file import pattern (without extension)")
58
+
59
+ args = parser.parse_args()
60
+
61
+ # Ensure custom_args_names and custom_args_vals have the same length
62
+ custom_opts = generate_custom_arg_dict(args=args)
63
+ custom_opts.update({"cloop": args.cloop,
64
+ "cluster_dt": args.cluster_dt,
65
+ "n_nodes": args.n_nodes,
66
+ "codegen_override_dir": args.codegen_override_dir})
67
+ if args.no_mp_fork: # this needs to be in the main
68
+ mp.set_start_method('spawn')
69
+ else:
70
+ # mp.set_start_method('forkserver')
71
+ mp.set_start_method('fork')
72
+
73
+ cluster_module=importlib.import_module(args.cluster_client_fname)
74
+ # Get all classes defined in the module
75
+ classes_in_module = [name for name, obj in inspect.getmembers(cluster_module, inspect.isclass)
76
+ if obj.__module__ == cluster_module.__name__]
77
+ if len(classes_in_module) == 1:
78
+ cluster_classname=classes_in_module[0]
79
+ ClusterClient = getattr(cluster_module, cluster_classname)
80
+ cluster_client = ClusterClient(namespace=args.ns,
81
+ cluster_size=args.size,
82
+ urdf_xacro_path=args.urdf_path,
83
+ srdf_xacro_path=args.srdf_path,
84
+ open_loop=not args.cloop,
85
+ use_mp_fork = not args.no_mp_fork,
86
+ verbose=args.verbose,
87
+ debug=args.enable_debug,
88
+ base_dump_dir=args.dmpdir,
89
+ timeout_ms=args.timeout_ms,
90
+ custom_opts=custom_opts,
91
+ codegen_override=args.codegen_override_dir,
92
+ set_affinity=args.set_affinity)
93
+ cluster_client.run()
94
+
95
+ else:
96
+ class_list_str = ", ".join(classes_in_module)
97
+ Journal.log("launch_control_cluster.py",
98
+ "",
99
+ f"Found more than one class in cluster client file {args.cluster_client_fname}. Found: {class_list_str}",
100
+ LogType.EXCEP,
101
+ throw_when_excep = False)
102
+ exit()
103
+
104
+ # control_cluster_client =
105
+ # control_cluster_client.run() # spawns the controllers on separate processes (blocking)
106
+
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/launch_train_env.py ADDED
@@ -0,0 +1,357 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from aug_mpc.utils.determinism import deterministic_run
2
+
3
+ from mpc_hive.utilities.shared_data.sim_data import SharedEnvInfo
4
+ from mpc_hive.utilities.shared_data.cluster_data import SharedClusterInfo
5
+
6
+ from EigenIPC.PyEigenIPC import VLevel, Journal, LogType
7
+ from EigenIPC.PyEigenIPC import StringTensorServer
8
+
9
+ import os, argparse, sys, types, inspect
10
+
11
+ from perf_sleep.pyperfsleep import PerfSleep
12
+
13
+ import importlib.util
14
+ import torch
15
+ import signal
16
+
17
+ algo = None # global to make it accessible by signal handler
18
+ exit_request=False
19
+ dummy_step_exit_req=False
20
+
21
+ def handle_sigint(signum, frame):
22
+ global exit_request, dummy_step_exit_req
23
+ Journal.log("launch_train_env.py",
24
+ "",
25
+ f"Received sigint. Will stop training.",
26
+ LogType.WARN)
27
+ exit_request=True
28
+ dummy_step_exit_req=True # in case dummy_step_loop was used
29
+
30
+ # Function to dynamically import a module from a specific file path
31
+ # def import_env_module(env_path):
32
+ # spec = importlib.util.spec_from_file_location("env_module", env_path)
33
+ # env_module = importlib.util.module_from_spec(spec)
34
+ # spec.loader.exec_module(env_module)
35
+ # return env_module
36
+
37
+ def import_env_module(env_path, local_env_root: str = None):
38
+ """
39
+ env_path: full path to the child env .py file to exec
40
+ local_env_root: directory where local copies of aug_mpc_envs.training_envs modules live
41
+ """
42
+ if local_env_root is not None:
43
+ local_env_root = os.path.abspath(local_env_root)
44
+ # override aug_mpc_envs.training_envs package to point to the local_env_root
45
+ pkg_name = "aug_mpc_envs.training_envs"
46
+ if pkg_name not in sys.modules:
47
+ mod = types.ModuleType(pkg_name)
48
+ mod.__path__ = [local_env_root] # tell Python to look here first
49
+ sys.modules[pkg_name] = mod
50
+ else:
51
+ existing = getattr(sys.modules[pkg_name], "__path__", None)
52
+ if existing is None:
53
+ sys.modules[pkg_name].__path__ = [local_env_root]
54
+ elif local_env_root not in existing:
55
+ existing.insert(0, local_env_root)
56
+
57
+ # load the module as usual
58
+ spec = importlib.util.spec_from_file_location("env_module", env_path)
59
+ env_module = importlib.util.module_from_spec(spec)
60
+ spec.loader.exec_module(env_module)
61
+ return env_module
62
+
63
+ def log_env_hierarchy(env_class, env_path, env_type="training"):
64
+ """
65
+ Logs the env class, its file, and full inheritance hierarchy with file paths.
66
+ env_class: the child env class
67
+ env_path: file path where the child class was loaded from
68
+ env_type: string label, e.g., "training", "evaluation", "resumed_training"
69
+ """
70
+ def get_bases_recursive(cls):
71
+ """Recursively get all base classes with their file paths."""
72
+ info = []
73
+ for base in cls.__bases__:
74
+ try:
75
+ file = inspect.getfile(base)
76
+ except TypeError:
77
+ file = "built-in or unknown"
78
+ info.append(f"{base.__name__} (from {file})")
79
+ # Recurse unless it's object
80
+ if base is not object:
81
+ info.extend(get_bases_recursive(base))
82
+ return info
83
+
84
+ hierarchy_info = get_bases_recursive(env_class)
85
+ hierarchy_str = " -> ".join(hierarchy_info) if hierarchy_info else "No parents"
86
+
87
+ Journal.log(
88
+ "launch_train_env.py",
89
+ "",
90
+ f"loading {env_type} env {env_class.__name__} (from {env_path}) "
91
+ f"with hierarchy: {hierarchy_str}",
92
+ LogType.INFO,
93
+ throw_when_excep=True
94
+ )
95
+
96
+ def dummy_step_loop(env):
97
+ global dummy_step_exit_req
98
+ while True:
99
+ if dummy_step_exit_req:
100
+ return True
101
+ step_ok=env.step(action=env.safe_action) # not a busy loop because of MPC in the step
102
+ if not step_ok:
103
+ return False
104
+
105
+ if __name__ == "__main__":
106
+
107
+ signal.signal(signal.SIGINT, handle_sigint)
108
+
109
+ # Parse command line arguments for CPU affinity
110
+ parser = argparse.ArgumentParser(description="Set CPU affinity for the script.")
111
+
112
+ parser.add_argument('--run_name', type=str, default=None, help='Name of training run')
113
+ parser.add_argument('--ns', type=str, help='Namespace to be used for shared memory')
114
+ parser.add_argument('--timeout_ms', type=int, help='Connection timeout after which the script self-terminates', default=60000)
115
+ parser.add_argument('--drop_dir', type=str, help='Directory root where all run data will be dumped')
116
+ parser.add_argument('--comment', type=str, help='Any useful comment associated with this run', default="")
117
+ parser.add_argument('--seed', type=int, help='Seed', default=1)
118
+ parser.add_argument('--use_cpu',action='store_true', help='If set, all the training (data included) will be performed on CPU')
119
+
120
+ parser.add_argument('--db',action='store_true', help='Whether to enable local data logging for the algorithm (reward metrics, etc.)')
121
+ parser.add_argument('--env_db',action='store_true', help='Whether to enable env db data logging on shared mem (e.g. reward metrics are not available for reading anymore)')
122
+ parser.add_argument('--full_env_db',action='store_true', help='Whether to enable detailed episodic data storage (data over single transitions)')
123
+ parser.add_argument('--rmdb',action='store_true', help='Whether to enable remote debug (e.g. data logging on remote servers)')
124
+
125
+ parser.add_argument('--tot_tsteps', type=int, help='Total number of timesteps to be collected', default=int(30e6))
126
+ parser.add_argument('--action_repeat', type=int, help='Frame skipping (1-> no skip)', default=1)
127
+ parser.add_argument('--discount_factor', type=float, help='', default=0.99)
128
+ parser.add_argument('--obs_norm',action='store_true', help='Whether to enable the use of running normalizer in agent')
129
+ parser.add_argument('--obs_rescale',action='store_true', help='Whether to rescale observation depending on their expected range')
130
+ parser.add_argument('--add_weight_norm',action='store_true', help='Whether to add weight normalization to agent interal llayers')
131
+ parser.add_argument('--add_layer_norm',action='store_true', help='Whether to add layer normalization to agent internal llayers')
132
+ parser.add_argument('--add_batch_norm',action='store_true', help='Whether to add batch normalization to agent internal llayers')
133
+
134
+ parser.add_argument('--act_rescale_critic',action='store_true', help='Whether to rescale actions provided to critic (if SAC) to be in range [-1, 1]')
135
+ parser.add_argument('--use_period_resets',action='store_true', help='')
136
+
137
+ parser.add_argument('--sac',action='store_true', help='Use SAC, otherwise PPO, unless dummy is set')
138
+ parser.add_argument('--dummy',action='store_true', help='Use dummy agent (useful for testing and debugging environments)')
139
+
140
+ parser.add_argument('--dump_checkpoints',action='store_true', help='Whether to dump model checkpoints during training')
141
+
142
+ parser.add_argument('--demo_envs_perc', type=float, help='[0, 1]', default=0.0)
143
+ parser.add_argument('--demo_stop_thresh', type=float, default=None,
144
+ help='Performance hreshold above which demonstration envs should be deactivated.')
145
+
146
+ parser.add_argument('--expl_envs_perc', type=float, help='[0, 1]', default=0)
147
+
148
+ parser.add_argument('--use_rnd',action='store_true', help='Whether to use RND for exploration')
149
+
150
+ parser.add_argument('--eval',action='store_true', help='Whether to perform an evaluation run')
151
+ parser.add_argument('--n_eval_timesteps', type=int, help='Total number of timesteps to be evaluated', default=int(1e6))
152
+ parser.add_argument('--det_eval',action='store_true', help='Whether to perform a deterministic eval (only action mean is used). Only valid if --eval.')
153
+ parser.add_argument('--allow_expl_during_eval',action='store_true', help='Whether to allow expl envs during evaluation (useful to tune exploration)')
154
+
155
+ parser.add_argument('--resume',action='store_true', help='Resume a previous training using a checkpoint')
156
+ parser.add_argument('--mpath', type=str, help='Model path to be used for policy evaluation', default=None)
157
+ parser.add_argument('--mname', type=str, help='Model name', default=None)
158
+ parser.add_argument('--override_env',action='store_true', help='Whether to override env when running evaluation')
159
+
160
+ parser.add_argument('--anomaly_detect',action='store_true', help='Whether to enable anomaly detection (useful for debug)')
161
+
162
+ parser.add_argument('--compression_ratio', type=float,
163
+ help='If e.g. 0.8, the fist layer will be of dimension [input_features_size x (input_features_size*compression_ratio)]', default=-1.0)
164
+ parser.add_argument('--actor_lwidth', type=int, help='Actor network layer width', default=128)
165
+ parser.add_argument('--critic_lwidth', type=int, help='Critic network layer width', default=256)
166
+ parser.add_argument('--actor_n_hlayers', type=int, help='Actor network size', default=3)
167
+ parser.add_argument('--critic_n_hlayers', type=int, help='Critic network size', default=4)
168
+
169
+ parser.add_argument('--env_fname', type=str, default="twist_tracking_env", help='Training env file name (without extension)')
170
+ parser.add_argument('--env_classname', type=str, default="TwistTrackingEnv", help='Training env class name')
171
+ parser.add_argument('--override_agent_actions',action='store_true', help='Whether to override agent actions with custom ones from shared mem (useful for db)')
172
+ parser.add_argument('--override_agent_refs',action='store_true', help='Whether to override automatically generated agent refs (useful for debug)')
173
+
174
+ parser.add_argument('--step_while_setup',action='store_true', help='Continue stepping env with default actions while setting up agent, etc..')
175
+
176
+ args = parser.parse_args()
177
+ args_dict = vars(args)
178
+
179
+ if args.eval and args.resume:
180
+ Journal.log("launch_train_env.py",
181
+ "",
182
+ f"Cannot set both --eval and --resume flags. Exiting.",
183
+ LogType.EXCEP,
184
+ throw_when_excep = True)
185
+
186
+ deterministic_run(seed=args.seed, torch_det_algos=False)
187
+
188
+ anomaly_detect=False
189
+ if args.anomaly_detect:
190
+ torch.autograd.set_detect_anomaly(True)
191
+
192
+ if (not args.mpath is None) and (not args.mname is None):
193
+ mpath_full = os.path.join(args.mpath, args.mname)
194
+ else:
195
+ mpath_full=None
196
+
197
+ env_fname=args.env_fname
198
+ env_classname = args.env_classname
199
+ env_path=""
200
+ env_module=None
201
+ if (not args.eval and not args.resume) or (args.override_env):
202
+ # if starting a fresh traning or overriding env, load from a fresh env from aug_mpc
203
+ env_path = f"aug_mpc_envs.training_envs.{env_fname}"
204
+ env_module = importlib.import_module(env_path)
205
+ else:
206
+ if args.mpath is None:
207
+ Journal.log("launch_train_env.py",
208
+ "",
209
+ f"no mpath provided! Cannot load env. Either provide a mpath or run with --override_env",
210
+ LogType.EXCEP,
211
+ throw_when_excep = True)
212
+
213
+ env_path = os.path.join(args.mpath, env_fname + ".py")
214
+ env_module = import_env_module(env_path, local_env_root=args.mpath)
215
+
216
+ EnvClass = getattr(env_module, env_classname)
217
+ env_type = "training" if not args.eval else "evaluation"
218
+ if args.resume:
219
+ env_type = "resumed_training"
220
+ log_env_hierarchy(EnvClass, env_path, env_type) # db print of env class
221
+
222
+ env = EnvClass(namespace=args.ns,
223
+ verbose=True,
224
+ vlevel=VLevel.V2,
225
+ use_gpu=not args.use_cpu,
226
+ debug=args.env_db,
227
+ override_agent_refs=args.override_agent_refs,
228
+ timeout_ms=args.timeout_ms,
229
+ env_opts=args_dict)
230
+ if not env.is_ready(): # something went wrong
231
+ exit()
232
+
233
+ dummy_step_thread = None
234
+ if args.step_while_setup:
235
+ import threading
236
+ # spawn step thread (we don't true parallelization, thread is fine)
237
+ # start the dummy stepping in a separate thread so setup can continue concurrently
238
+ dummy_step_thread = threading.Thread(target=dummy_step_loop, args=(env,), daemon=True)
239
+ dummy_step_thread.start()
240
+
241
+ # getting some sim info for debugging
242
+ sim_data = {}
243
+ sim_info_shared = SharedEnvInfo(namespace=args.ns,
244
+ is_server=False,
245
+ safe=False)
246
+ sim_info_shared.run()
247
+ sim_info_keys = sim_info_shared.param_keys
248
+ sim_info_data = sim_info_shared.get().flatten()
249
+ for i in range(len(sim_info_keys)):
250
+ sim_data[sim_info_keys[i]] = sim_info_data[i]
251
+
252
+ # getting come cluster info for debugging
253
+ cluster_data={}
254
+ cluste_info_shared = SharedClusterInfo(namespace=args.ns,
255
+ is_server=False,
256
+ safe=False)
257
+ cluste_info_shared.run()
258
+ cluster_info_keys = cluste_info_shared.param_keys
259
+ cluster_info_data = cluste_info_shared.get().flatten()
260
+ for i in range(len(cluster_info_keys)):
261
+ cluster_data[cluster_info_keys[i]] = cluster_info_data[i]
262
+
263
+ custom_args={}
264
+ custom_args["uname_host"]="user_host"
265
+ try:
266
+ username = os.getlogin() # add machine info to db data
267
+ hostname = os.uname().nodename
268
+ user_host = f"{username}@{hostname}"
269
+ custom_args["uname_host"]=user_host
270
+ except:
271
+ pass
272
+
273
+ algo=None
274
+ if not args.dummy:
275
+ if args.sac:
276
+ from aug_mpc.training_algs.sac.sac import SAC
277
+
278
+ algo = SAC(env=env,
279
+ debug=args.db,
280
+ remote_db=args.rmdb,
281
+ seed=args.seed)
282
+ else:
283
+ from aug_mpc.training_algs.ppo.ppo import PPO
284
+
285
+ algo = PPO(env=env,
286
+ debug=args.db,
287
+ remote_db=args.rmdb,
288
+ seed=args.seed)
289
+ else:
290
+ from aug_mpc.training_algs.dummy.dummy import Dummy
291
+
292
+ algo=Dummy(env=env,
293
+ debug=args.db,
294
+ remote_db=args.rmdb,
295
+ seed=args.seed)
296
+
297
+ custom_args.update(args_dict)
298
+ custom_args.update(cluster_data)
299
+ custom_args.update(sim_data)
300
+
301
+ run_name=env_classname if args.run_name is None else args.run_name
302
+ algo.setup(run_name=run_name,
303
+ ns=args.ns,
304
+ verbose=True,
305
+ drop_dir_name=args.drop_dir,
306
+ custom_args=custom_args,
307
+ comment=args.comment,
308
+ eval=args.eval,
309
+ resume=args.resume,
310
+ model_path=mpath_full,
311
+ n_eval_timesteps=args.n_eval_timesteps,
312
+ dump_checkpoints=args.dump_checkpoints,
313
+ norm_obs=args.obs_norm,
314
+ rescale_obs=args.obs_rescale)
315
+
316
+ full_drop_dir=algo.drop_dir()
317
+ shared_drop_dir = StringTensorServer(length=1,
318
+ basename="SharedTrainingDropDir",
319
+ name_space=args.ns,
320
+ verbose=True,
321
+ vlevel=VLevel.V2,
322
+ force_reconnection=True)
323
+ shared_drop_dir.run()
324
+
325
+ while True:
326
+ if not shared_drop_dir.write_vec([full_drop_dir], 0):
327
+ ns=1000000000
328
+ PerfSleep.thread_sleep(ns)
329
+ continue
330
+ else:
331
+ break
332
+
333
+ if args.step_while_setup:
334
+ # stop dummy step thread and give algo authority on step
335
+ dummy_step_exit_req=True
336
+ # wait for thread to join
337
+ if dummy_step_thread is not None:
338
+ dummy_step_thread.join()
339
+ Journal.log("launch_train_env.py",
340
+ "",
341
+ f"Dummy env step thread joined. Moving step authority to algo.",
342
+ LogType.INFO)
343
+
344
+ eval=args.eval
345
+ if args.override_agent_actions:
346
+ eval=True
347
+ if not eval:
348
+ while not exit_request:
349
+ if not algo.learn():
350
+ break
351
+ else: # eval phase
352
+ with torch.no_grad(): # no need for grad computation
353
+ while not exit_request:
354
+ if not algo.eval():
355
+ break
356
+
357
+ algo.done() # make sure to terminate training properly
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/launch_world_interface.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import argparse
3
+ import importlib.util
4
+ import inspect
5
+
6
+ from aug_mpc.utils.rt_factor import RtFactor
7
+ from aug_mpc.utils.custom_arg_parsing import generate_custom_arg_dict
8
+ from aug_mpc.utils.determinism import deterministic_run
9
+
10
+ from mpc_hive.utilities.shared_data.sim_data import SharedEnvInfo
11
+
12
+ from EigenIPC.PyEigenIPC import VLevel
13
+ from EigenIPC.PyEigenIPC import Journal, LogType
14
+
15
+ script_name = os.path.splitext(os.path.basename(os.path.abspath(__file__)))[0]
16
+
17
+ # Function to dynamically import a module from a specific file path
18
+ def import_env_module(env_path):
19
+ spec = importlib.util.spec_from_file_location("env_module", env_path)
20
+ env_module = importlib.util.module_from_spec(spec)
21
+ spec.loader.exec_module(env_module)
22
+ return env_module
23
+
24
+ if __name__ == '__main__':
25
+
26
+ parser = argparse.ArgumentParser(description="Sim. env launcher")
27
+ # Add arguments
28
+ parser.add_argument('--robot_name', type=str, help='Alias to be used for the robot and also shared memory')
29
+ parser.add_argument('--urdf_path', type=str, help='path to the URDF file description for each robot')
30
+ parser.add_argument('--srdf_path', type=str, help='path to the SRDF file description for each robot (used for homing)')
31
+ parser.add_argument('--jnt_imp_config_path', type=str, help='path to a valid YAML file containing information on jnt impedance gains')
32
+ parser.add_argument('--num_envs', type=int, default=1)
33
+ parser.add_argument('--n_contacts', type=int, default=4)
34
+ parser.add_argument('--cluster_dt', type=float, default=0.03, help='dt at which the control cluster runs')
35
+ parser.add_argument('--dmpdir', type=str, help='directory where data is dumped', default="/root/aux_data")
36
+ parser.add_argument('--remote_stepping',action='store_true',
37
+ help='Whether to use remote stepping for cluster triggering (to be set during training)')
38
+
39
+ # Replacing argparse.BooleanOptionalAction with 'store_true' and 'store_false' for compatibility with Python 3.8
40
+ parser.add_argument('--use_gpu',action='store_true', help='Whether to use gpu simulation')
41
+
42
+ parser.add_argument('--enable_debug',action='store_true', help='Whether to enable debug mode (may introduce significant overhead)')
43
+
44
+ parser.add_argument('--headless',action='store_true', help='Whether to run simulation in headless mode')
45
+
46
+ parser.add_argument('--verbose',action='store_true', help='Enable verbose mode')
47
+
48
+ parser.add_argument('--comment', type=str, help='Any useful comment associated with this run', default="")
49
+ parser.add_argument('--timeout_ms', type=int, help='connection timeout after which the script self-terminates', default=60000)
50
+ parser.add_argument('--physics_dt', type=float, default=5e-4, help='')
51
+
52
+ parser.add_argument('--use_custom_jnt_imp',action='store_true',
53
+ help='Whether to override the default PD controller with a custom one')
54
+
55
+ parser.add_argument('--diff_vels',action='store_true',
56
+ help='Whether to obtain velocities by differentiation or not')
57
+
58
+ parser.add_argument('--init_timesteps', type=int, help='initialization timesteps', default=None)
59
+ parser.add_argument('--seed', type=int, help='seed', default=0)
60
+
61
+ parser.add_argument('--custom_args_names', nargs='+', default=None,
62
+ help='list of custom arguments names')
63
+ parser.add_argument('--custom_args_vals', nargs='+', default=None,
64
+ help='list of custom arguments values')
65
+ parser.add_argument('--custom_args_dtype', nargs='+', default=None,
66
+ help='list of custom arguments data types')
67
+
68
+ parser.add_argument('--env_fname', type=str,
69
+ default="aug_mpc_envs.world_interfaces.isaac_world_interface",
70
+ help="env file import pattern (without extension)")
71
+
72
+ args = parser.parse_args()
73
+
74
+ deterministic_run(seed=args.seed, torch_det_algos=False)
75
+
76
+ default_init_duration=3.0 # [s]
77
+ default_init_tsteps=int(default_init_duration/args.physics_dt)
78
+ init_tsteps=args.init_timesteps
79
+ if init_tsteps is None:
80
+ init_tsteps=default_init_tsteps
81
+ # Ensure custom_args_names, custom_args_vals, and custom_args_dtype have the same length
82
+ custom_opt = generate_custom_arg_dict(args=args)
83
+
84
+ Journal.log("launch_world_interface.py",
85
+ "",
86
+ f"Will warmup world interface for {default_init_duration}s ({default_init_tsteps} physics steps)",
87
+ LogType.STAT)
88
+
89
+ robot_names = [args.robot_name]
90
+ robot_urdf_paths = [args.urdf_path]
91
+ robot_srdf_paths = [args.srdf_path]
92
+ control_clust_dts = [float(args.cluster_dt)]
93
+ use_remote_stepping = [args.remote_stepping]
94
+ n_contacts = [args.n_contacts]
95
+ jnt_imp_config_paths = [args.jnt_imp_config_path]
96
+ num_envs = args.num_envs
97
+ control_clust_dt = args.cluster_dt # [s]. Dt at which RHC controllers run
98
+ headless = args.headless
99
+
100
+ # simulation parameters
101
+ remote_env_params = {}
102
+ remote_env_params["physics_dt"] = args.physics_dt # physics_dt?
103
+ remote_env_params["n_envs"] = num_envs
104
+ remote_env_params["use_gpu"] = args.use_gpu
105
+ remote_env_params["substepping_dt"] = control_clust_dts[0]
106
+ remote_env_params["headless"] = headless
107
+ remote_env_params["debug_enabled"] = args.enable_debug
108
+ remote_env_params["seed"] = args.seed
109
+ remote_env_params.update(custom_opt)
110
+ # sim info to be broadcasted on shared memory
111
+ # adding some data to dict for debugging
112
+
113
+ shared_sim_infos = []
114
+ for i in range(len(robot_names)):
115
+ shared_sim_infos.append(SharedEnvInfo(
116
+ namespace=robot_names[i],
117
+ is_server=True,
118
+ env_params_dict=remote_env_params,
119
+ verbose=True,
120
+ vlevel=VLevel.V2,
121
+ force_reconnection=True))
122
+ shared_sim_infos[i].run()
123
+
124
+ env_module=importlib.import_module(args.env_fname)
125
+ classes_in_module = [name for name, obj in inspect.getmembers(env_module, inspect.isclass)
126
+ if obj.__module__ == env_module.__name__]
127
+ if len(classes_in_module) == 1:
128
+ cluster_classname=classes_in_module[0]
129
+ Env = getattr(env_module, cluster_classname)
130
+ else:
131
+ class_list_str = ", ".join(classes_in_module)
132
+ Journal.log("launch_world_interface.py",
133
+ "",
134
+ f"Found more than one class in env file {args.env_fname}. Found: {class_list_str}",
135
+ LogType.EXCEP,
136
+ throw_when_excep = False)
137
+ exit()
138
+
139
+ env = Env(robot_names=robot_names,
140
+ robot_urdf_paths=robot_urdf_paths,
141
+ robot_srdf_paths=robot_srdf_paths,
142
+ cluster_dt=control_clust_dts,
143
+ jnt_imp_config_paths=jnt_imp_config_paths,
144
+ n_contacts=n_contacts,
145
+ use_remote_stepping=use_remote_stepping,
146
+ name=classes_in_module[0],
147
+ num_envs=num_envs,
148
+ debug=args.enable_debug,
149
+ verbose=args.verbose,
150
+ vlevel=VLevel.V2,
151
+ n_init_step=init_tsteps,
152
+ timeout_ms=args.timeout_ms,
153
+ env_opts=remote_env_params,
154
+ use_gpu=args.use_gpu,
155
+ override_low_lev_controller=args.use_custom_jnt_imp) # create environment
156
+ env.reset(reset_sim=True)
157
+
158
+ rt_factor = RtFactor(dt_nom=env.physics_dt(),
159
+ window_size=100)
160
+
161
+ while True:
162
+
163
+ if rt_factor.reset_due():
164
+ rt_factor.reset()
165
+
166
+ step_ok=env.step()
167
+
168
+ if not step_ok:
169
+ break
170
+
171
+ rt_factor.update()
172
+
173
+ for i in range(len(robot_names)):
174
+ robot_name=robot_names[i]
175
+ n_steps = env.cluster_sim_step_counters[robot_name]
176
+ sol_counter = env.cluster_servers[robot_name].solution_counter()
177
+ trigger_counter = env.cluster_servers[robot_name].trigger_counter()
178
+ shared_sim_infos[i].write(dyn_info_name=["sim_rt_factor",
179
+ "total_rt_factor",
180
+ "env_stepping_dt",
181
+ "world_stepping_dt",
182
+ "time_to_get_states_from_env",
183
+ "cluster_state_update_dt",
184
+ "cluster_sol_time",
185
+ "n_sim_steps",
186
+ "n_cluster_trigger_steps",
187
+ "n_cluster_sol_steps",
188
+ "sim_time",
189
+ "cluster_time"],
190
+ val=[rt_factor.get(),
191
+ rt_factor.get() * num_envs,
192
+ rt_factor.get_avrg_step_time(),
193
+ env.debug_data["time_to_step_world"],
194
+ env.debug_data["time_to_get_states_from_env"],
195
+ env.debug_data["cluster_state_update_dt"][robot_name],
196
+ env.debug_data["cluster_sol_time"][robot_name],
197
+ n_steps,
198
+ trigger_counter,
199
+ sol_counter,
200
+ env.debug_data["sim_time"][robot_name],
201
+ sol_counter*env.cluster_servers[robot_name].cluster_dt()
202
+ ])
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/__init__.py ADDED
File without changes
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/augmpc_cluster_client.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mpc_hive.cluster_client.control_cluster_client import ControlClusterClient
2
+ from aug_mpc.utils.xrdf_gen import generate_srdf, generate_urdf
3
+ from aug_mpc.utils.hybrid_quad_xrdf_gen import get_xrdf_cmds
4
+ from aug_mpc.utils.custom_arg_parsing import extract_custom_xacro_args, merge_xacro_cmds
5
+
6
+ from EigenIPC.PyEigenIPC import Journal, LogType
7
+
8
+ from typing import List, Dict
9
+
10
+ import os
11
+
12
+ from abc import abstractmethod
13
+
14
+ class AugMpcClusterClient(ControlClusterClient):
15
+
16
+ def _import_aux_libs(self):
17
+ super()._import_aux_libs()
18
+
19
+ def __init__(self,
20
+ namespace: str,
21
+ urdf_xacro_path: str,
22
+ srdf_xacro_path: str,
23
+ cluster_size: int,
24
+ set_affinity: bool = False,
25
+ use_mp_fork: bool = False,
26
+ isolated_cores_only: bool = False,
27
+ core_ids_override_list: List[int] = None,
28
+ verbose: bool = False,
29
+ debug: bool = False,
30
+ codegen_base_dirname: str = "CodeGen",
31
+ base_dump_dir: str = "/tmp",
32
+ codegen_override: str = None,
33
+ custom_opts: Dict = {}):
34
+
35
+ self._base_dump_dir = base_dump_dir
36
+
37
+ self._temp_path = base_dump_dir + "/" + f"{self.__class__.__name__}" + f"_{namespace}"
38
+
39
+ self._codegen_base_dirname = codegen_base_dirname
40
+ self._codegen_basedir = self._temp_path + "/" + self._codegen_base_dirname
41
+
42
+ self._codegen_override = codegen_override # can be used to manually override
43
+ # the default codegen dir
44
+
45
+ if not os.path.exists(self._temp_path):
46
+ os.makedirs(self._temp_path)
47
+ if not os.path.exists(self._codegen_basedir):
48
+ os.makedirs(self._codegen_basedir)
49
+
50
+ self._urdf_xacro_path = urdf_xacro_path
51
+ self._srdf_xacro_path = srdf_xacro_path
52
+ self._urdf_path=""
53
+ self._srdf_path=""
54
+
55
+ super().__init__(namespace = namespace,
56
+ cluster_size=cluster_size,
57
+ isolated_cores_only = isolated_cores_only,
58
+ set_affinity = set_affinity,
59
+ use_mp_fork = use_mp_fork,
60
+ core_ids_override_list = core_ids_override_list,
61
+ verbose = verbose,
62
+ debug = debug,
63
+ custom_opts=custom_opts)
64
+ self._generate_srdf(namespace=namespace)
65
+
66
+ self._generate_urdf(namespace=namespace)
67
+
68
+
69
+ def codegen_dir(self):
70
+
71
+ return self._codegen_basedir
72
+
73
+ def codegen_dir_override(self):
74
+
75
+ return self._codegen_override
76
+
77
+ def _generate_srdf(self,namespace:str):
78
+
79
+ custom_xacro_args=extract_custom_xacro_args(self._custom_opts)
80
+ cmds=merge_xacro_cmds(prev_cmds=self._xrdf_cmds(),
81
+ new_cmds=custom_xacro_args)
82
+
83
+ self._urdf_path=generate_urdf(robot_name=namespace,
84
+ xacro_path=self._urdf_xacro_path,
85
+ dump_path=self._temp_path,
86
+ xrdf_cmds=cmds)
87
+
88
+ def _generate_urdf(self,namespace:str):
89
+
90
+ custom_xacro_args=extract_custom_xacro_args(self._custom_opts)
91
+ cmds=merge_xacro_cmds(prev_cmds=self._xrdf_cmds(),
92
+ new_cmds=custom_xacro_args)
93
+
94
+ self._srdf_path=generate_srdf(robot_name=namespace,
95
+ xacro_path=self._srdf_xacro_path,
96
+ dump_path=self._temp_path,
97
+ xrdf_cmds=cmds)
98
+
99
+ @abstractmethod
100
+ def _xrdf_cmds(self):
101
+
102
+ # to be implemented by parent class (
103
+ # for an example have a look at utils/centauro_xrdf_gen.py)
104
+
105
+ pass
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/augmpc_cluster_server.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mpc_hive.cluster_server.control_cluster_server import ControlClusterServer
2
+ from typing import List
3
+ from EigenIPC.PyEigenIPC import VLevel
4
+
5
+ class AugMpcClusterServer(ControlClusterServer):
6
+
7
+ def __init__(self,
8
+ robot_name: str,
9
+ cluster_size: int,
10
+ cluster_dt: float,
11
+ control_dt: float,
12
+ jnt_names: List[str],
13
+ n_contacts: int,
14
+ contact_linknames: List[str] = None,
15
+ verbose: bool = False,
16
+ vlevel: VLevel = VLevel.V1,
17
+ debug: bool = False,
18
+ use_gpu: bool = True,
19
+ force_reconnection: bool = True,
20
+ timeout_ms: int = 60000,
21
+ enable_height_sensor: bool = False,
22
+ height_grid_size: int = None,
23
+ height_grid_resolution: float = None):
24
+
25
+ self.robot_name = robot_name
26
+
27
+ super().__init__(
28
+ namespace=self.robot_name,
29
+ cluster_size=cluster_size,
30
+ cluster_dt=cluster_dt,
31
+ control_dt=control_dt,
32
+ jnt_names=jnt_names,
33
+ n_contacts = n_contacts,
34
+ contact_linknames = contact_linknames,
35
+ verbose=verbose,
36
+ vlevel=vlevel,
37
+ debug=debug,
38
+ use_gpu=use_gpu,
39
+ force_reconnection=force_reconnection,
40
+ timeout_ms=timeout_ms,
41
+ enable_height_sensor=enable_height_sensor,
42
+ height_grid_size=height_grid_size,
43
+ height_grid_resolution=height_grid_resolution)
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/__init__.py ADDED
File without changes
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/config/rhc_horizon_config.yaml ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # A dummy example of a rhc controller configuration built on top of horizon
2
+ # and iLQR
3
+
4
+ solver:
5
+ type: ilqr
6
+ ilqr.tol: 0.01
7
+ ilqr.constr_viol_tol: 0.01
8
+ ilqr.suppress_all_output: 'yes'
9
+ ilqr.codegen_enabled: true
10
+ # ilqr.codegen_workdir: /tmp/tyhio
11
+ ilqr.enable_gn: true
12
+ ilqr.hxx_reg_base: 0.0
13
+ ilqr.n_threads: 0
14
+ print_time: 0
15
+
16
+ constraints:
17
+ - ball_1_contact
18
+ - ball_2_contact
19
+ - ball_3_contact
20
+ - ball_4_contact
21
+ - z_ball_1
22
+ - z_ball_2
23
+ - z_ball_3
24
+ - z_ball_4
25
+
26
+ costs:
27
+ - joint_regularization
28
+ - joint_posture
29
+ - base_position
30
+ - base_orientation
31
+
32
+ .define:
33
+ - &w_base_pos 10
34
+ - &w_base_ori 1
35
+ - &w_ball_z 1
36
+ # - &w_postural 0.0001
37
+ - &w_jnt_v_reg 0.01
38
+ - &w_jnt_a_reg 0.001
39
+ - &w_jnt_f_reg 0.0001
40
+ - &wheel_radius 0.124
41
+
42
+ base_position:
43
+ type: Cartesian
44
+ distal_link: base_link
45
+ indices: [0, 1, 2]
46
+ nodes: ${N}
47
+ weight: *w_base_pos
48
+
49
+ base_orientation:
50
+ type: Cartesian
51
+ distal_link: base_link
52
+ indices: [3, 4, 5]
53
+ nodes: ${N}
54
+ weight: *w_base_ori
55
+
56
+ # ===============================
57
+
58
+ rolling_contact_1:
59
+ type: Rolling
60
+ frame: wheel_1
61
+ radius: *wheel_radius
62
+
63
+ rolling_contact_2:
64
+ type: Rolling
65
+ frame: wheel_2
66
+ radius: *wheel_radius
67
+
68
+ rolling_contact_3:
69
+ type: Rolling
70
+ frame: wheel_3
71
+ radius: *wheel_radius
72
+
73
+ rolling_contact_4:
74
+ type: Rolling
75
+ frame: wheel_4
76
+ radius: *wheel_radius
77
+
78
+ # contact_1:
79
+ # type: Cartesian
80
+ # distal_link: ball_1
81
+ # indices: [0, 1, 2]
82
+ # cartesian_type: velocity
83
+
84
+ # contact_2:
85
+ # type: Cartesian
86
+ # distal_link: ball_2
87
+ # indices: [0, 1, 2]
88
+ # cartesian_type: velocity
89
+
90
+ # contact_3:
91
+ # type: Cartesian
92
+ # distal_link: ball_3
93
+ # indices: [0, 1, 2]
94
+ # cartesian_type: velocity
95
+
96
+ # contact_4:
97
+ # type: Cartesian
98
+ # distal_link: ball_4
99
+ # indices: [0, 1, 2]
100
+ # cartesian_type: velocity
101
+
102
+ # ==================================
103
+
104
+ interaction_wheel_1:
105
+ type: VertexForce
106
+ frame: ball_1
107
+ fn_min: 10.0
108
+ enable_fc: true
109
+ friction_coeff: 0.5
110
+ vertex_frames:
111
+ - wheel_1
112
+
113
+ interaction_wheel_2:
114
+ type: VertexForce
115
+ frame: ball_2
116
+ fn_min: 10.0
117
+ enable_fc: true
118
+ friction_coeff: 0.5
119
+ vertex_frames:
120
+ - wheel_2
121
+
122
+ interaction_wheel_3:
123
+ type: VertexForce
124
+ frame: ball_3
125
+ fn_min: 10.0
126
+ enable_fc: true
127
+ friction_coeff: 0.5
128
+ vertex_frames:
129
+ - wheel_3
130
+
131
+ interaction_wheel_4:
132
+ type: VertexForce
133
+ frame: ball_4
134
+ fn_min: 10.0
135
+ enable_fc: true
136
+ friction_coeff: 0.5
137
+ vertex_frames:
138
+ - wheel_4
139
+
140
+ ball_1_contact:
141
+ type: Contact
142
+ subtask: [interaction_wheel_1, rolling_contact_1]
143
+
144
+ ball_2_contact:
145
+ type: Contact
146
+ subtask: [interaction_wheel_2, rolling_contact_2]
147
+
148
+ ball_3_contact:
149
+ type: Contact
150
+ subtask: [interaction_wheel_3, rolling_contact_3]
151
+
152
+ ball_4_contact:
153
+ type: Contact
154
+ subtask: [interaction_wheel_4, rolling_contact_4]
155
+
156
+ # joint_posture:
157
+ # type: Postural
158
+ # weight: *w_postural
159
+ # indices: [0, 1, 2,
160
+ # 4, 5, 6,
161
+ # 8, 9, 10,
162
+ # 12, 13, 14]
163
+ # nodes: ${range(N-8, N)}
164
+
165
+ # todo: wrong, as the order COUNTS. If I add the contacts after the joint regularization, they wont get considered.
166
+ joint_regularization:
167
+ type: Regularization
168
+ nodes: all # maybe not on first nodes??
169
+ weight:
170
+ velocity: *w_jnt_v_reg
171
+ acceleration: *w_jnt_a_reg
172
+ force: *w_jnt_f_reg
173
+
174
+ z_ball_1:
175
+ type: Cartesian
176
+ distal_link: ball_1
177
+ indices: [2]
178
+ cartesian_type: position
179
+ weight: *w_ball_z
180
+
181
+ z_ball_2:
182
+ type: Cartesian
183
+ distal_link: ball_2
184
+ indices: [2]
185
+ cartesian_type: position
186
+ weight: *w_ball_z
187
+
188
+ z_ball_3:
189
+ type: Cartesian
190
+ distal_link: ball_3
191
+ indices: [2]
192
+ cartesian_type: position
193
+ weight: *w_ball_z
194
+
195
+ z_ball_4:
196
+ type: Cartesian
197
+ distal_link: ball_4
198
+ indices: [2]
199
+ cartesian_type: position
200
+ weight: *w_ball_z
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/gait_manager.py ADDED
@@ -0,0 +1,565 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ from aug_mpc.controllers.rhc.horizon_based.horizon_imports import *
4
+
5
+ from EigenIPC.PyEigenIPC import VLevel
6
+ from EigenIPC.PyEigenIPC import Journal, LogType
7
+
8
+ from typing import Dict
9
+
10
+ class GaitManager:
11
+
12
+ def __init__(self,
13
+ task_interface: TaskInterface,
14
+ phase_manager: pymanager.PhaseManager,
15
+ injection_node: int = None,
16
+ keep_yaw_vert: bool = False,
17
+ yaw_vertical_weight: float = None,
18
+ vertical_landing: bool = False,
19
+ landing_vert_weight: float = None,
20
+ phase_force_reg: float = None,
21
+ flight_duration: int = 15,
22
+ post_flight_stance: int = 3,
23
+ step_height: float = 0.1,
24
+ dh: float = 0.0,
25
+ custom_opts: Dict = {}):
26
+
27
+ self._custom_opts=custom_opts
28
+
29
+ self._is_open_loop=False
30
+ if "is_open_loop" in self._custom_opts:
31
+ self._is_open_loop=self._custom_opts["is_open_loop"]
32
+
33
+ self.task_interface = task_interface
34
+ self._n_nodes_prb=self.task_interface.prb.getNNodes()
35
+
36
+ self._phase_manager = phase_manager
37
+ self._model=self.task_interface.model
38
+ self._q0=self._model.q0
39
+ self._kin_dyn=self.task_interface.model.kd
40
+
41
+ # phase weights and regs
42
+ self._keep_yaw_vert=keep_yaw_vert
43
+ self._yaw_vertical_weight=yaw_vertical_weight
44
+ self._vertical_landing=vertical_landing
45
+ self._landing_vert_weight=landing_vert_weight
46
+ self._phase_force_reg=phase_force_reg
47
+ self._total_weight = np.atleast_2d(np.array([0, 0, self._kin_dyn.mass() * 9.81])).T
48
+
49
+ self._f_reg_ref={}
50
+
51
+ # flight parameters
52
+ self._post_flight_stance=post_flight_stance
53
+ self._flight_info_now=None
54
+ self._flight_duration_max=self._n_nodes_prb-(injection_node+1)
55
+ self._flight_duration_min=3
56
+ self._flight_duration_default=flight_duration
57
+ # apex bounds/defaults
58
+ self._step_height_default=step_height
59
+ self._step_height_min=0.0
60
+ self._step_height_max=0.5
61
+ # end height bounds/defaults
62
+ self._dh_default=dh
63
+ self._dh_min=0.0
64
+ self._dh_max=0.5
65
+ # landing dx, dy bounds/defaults
66
+ self._land_dx_default=0.0
67
+ self._land_dx_min=-0.5
68
+ self._land_dx_max=0.5
69
+ self._land_dy_default=0.0
70
+ self._land_dy_min=-0.5
71
+ self._land_dy_max=0.5
72
+
73
+ # timeline data
74
+ self._contact_timelines = dict()
75
+ self.timeline_names=[]
76
+
77
+ self._flight_phases = {}
78
+ self._touchdown_phases = {}
79
+ self._contact_phases = {}
80
+ self._fk_contacts = {}
81
+ self._fkd_contacts = {}
82
+ self._f_reg_ref = {}
83
+
84
+ # reference traj
85
+ self._tg = trajectoryGenerator.TrajectoryGenerator()
86
+ self._traj_der= [None, 0, 0]
87
+ self._traj_second_der=[None, 0, 0]
88
+ self._third_traj_der=[None, None, 0]
89
+
90
+ self._ref_trjs = {}
91
+ self._ref_vtrjs = {}
92
+
93
+ if injection_node is None:
94
+ self._injection_node = round(self.task_interface.prb.getNNodes()/2.0)
95
+ else:
96
+ self._injection_node = injection_node
97
+
98
+ self._init_contact_timelines()
99
+
100
+ self._reset_contact_timelines()
101
+
102
+ def _init_contact_timelines(self):
103
+
104
+ short_stance_duration=1
105
+ flight_phase_short_duration=1
106
+
107
+ self.n_contacts=len(self._model.cmap.keys())
108
+ self._dt=float(self.task_interface.prb.getDt())
109
+
110
+ self._name_to_idx_map={}
111
+
112
+ j=0
113
+ for contact in self._model.cmap.keys():
114
+
115
+ self._fk_contacts[contact]=self._kin_dyn.fk(contact)
116
+ self._fkd_contacts[contact]=self._kin_dyn.frameVelocity(contact, self._model.kd_frame)
117
+ self.timeline_names.append(contact)
118
+ self._contact_timelines[contact]=self._phase_manager.createTimeline(f'{contact}_timeline')
119
+ # stances
120
+ self._contact_phases[contact] = self._contact_timelines[contact].createPhase(short_stance_duration,
121
+ f'stance_{contact}_short')
122
+
123
+ if self.task_interface.getTask(f'{contact}') is not None:
124
+ self._contact_phases[contact].addItem(self.task_interface.getTask(f'{contact}'))
125
+ else:
126
+ Journal.log(self.__class__.__name__,
127
+ "_init_contact_timelines",
128
+ f"contact task {contact} not found",
129
+ LogType.EXCEP,
130
+ throw_when_excep=True)
131
+ i=0
132
+ self._f_reg_ref[contact]=[]
133
+ for force in self._model.cmap[contact]:
134
+ f_ref=self.task_interface.prb.createParameter(name=f"{contact}_force_reg_f{i}_ref",
135
+ dim=3)
136
+ force_reg=self.task_interface.prb.createResidual(f'{contact}_force_reg_f{i}', self._phase_force_reg * (force - f_ref),
137
+ nodes=[])
138
+ self._f_reg_ref[contact].append(f_ref)
139
+ self.set_f_reg(contact_name=contact, scale=4)
140
+ self._contact_phases[contact].addCost(force_reg, nodes=list(range(0, short_stance_duration)))
141
+ i+=1
142
+
143
+ # flights
144
+ self._flight_phases[contact]=self._contact_timelines[contact].createPhase(flight_phase_short_duration,
145
+ f'flight_{contact}_short')
146
+
147
+ # sanity checks (z trajectory)
148
+ self._zpos_task_found=True
149
+ self._zvel_task_found=True
150
+ self._xypos_task_found=True
151
+ self._xyvel_task_found=True
152
+ if self.task_interface.getTask(f'z_{contact}') is None:
153
+ self._zpos_task_found=False
154
+ if self.task_interface.getTask(f'vz_{contact}') is None:
155
+ self._zvel_task_found=False
156
+ if self.task_interface.getTask(f'xy_{contact}') is None:
157
+ self._xypos_task_found=False
158
+ if self.task_interface.getTask(f'vxy_{contact}') is None:
159
+ self._xyvel_task_found=False
160
+ if (not self._zpos_task_found) and (not self._zvel_task_found):
161
+ Journal.log(self.__class__.__name__,
162
+ "_init_contact_timelines",
163
+ f"neither pos or vel task for contacts were found! Aborting.",
164
+ LogType.EXCEP,
165
+ throw_when_excep=True)
166
+ if (not self._zpos_task_found) and self._is_open_loop:
167
+ Journal.log(self.__class__.__name__,
168
+ "_init_contact_timelines",
169
+ f"Running in open loop, but no contact pos task found. Aborting.",
170
+ LogType.EXCEP,
171
+ throw_when_excep=True)
172
+ if self._zpos_task_found and self._xyvel_task_found:
173
+ Journal.log(self.__class__.__name__,
174
+ "_init_contact_timelines",
175
+ f"Both pos and vel task for contact {contact} found! This is not allowed, aborting.",
176
+ LogType.EXCEP,
177
+ throw_when_excep=True)
178
+ if self._zvel_task_found and self._xypos_task_found:
179
+ Journal.log(self.__class__.__name__,
180
+ "_init_contact_timelines",
181
+ f"Both pos and vel task for contact {contact} found! This is not allowed, aborting.",
182
+ LogType.EXCEP,
183
+ throw_when_excep=True)
184
+ if (not self._xypos_task_found) and (not self._xyvel_task_found):
185
+ Journal.log(self.__class__.__name__,
186
+ "_init_contact_timelines",
187
+ f"neither pos or vel task for contact {contact} xy were found! Will proceed without xy landing constraints.",
188
+ LogType.WARN)
189
+ # if (not self._zvel_task_found) and (not self._is_open_loop):
190
+ # Journal.log(self.__class__.__name__,
191
+ # "_init_contact_timelines",
192
+ # f"Running in closed loop, but contact vel task not found. Aborting",
193
+ # LogType.EXCEP,
194
+ # throw_when_excep=True)
195
+
196
+ self._ref_trjs[contact]=None
197
+ self._ref_vtrjs[contact]=None
198
+ if self._zpos_task_found: # we use pos trajectory
199
+ self._ref_trjs[contact]=np.zeros(shape=[7, self.task_interface.prb.getNNodes()])
200
+ init_z_foot = self._fk_contacts[contact](q=self._q0)['ee_pos'].elements()[2]
201
+ if self._is_open_loop:
202
+ self._ref_trjs[contact][2, :] = np.atleast_2d(init_z_foot)
203
+ else:
204
+ self._ref_trjs[contact][2, :] = 0.0 # place foot at ground level initially ()
205
+
206
+ if self._xypos_task_found: # xy
207
+ self._flight_phases[contact].addItemReference(self.task_interface.getTask(f'xy_{contact}'),
208
+ self._ref_trjs[contact][0:2, 0:1],
209
+ nodes=list(range(0, flight_phase_short_duration)))
210
+ self._touchdown_phases[contact]=None
211
+ # z
212
+ self._flight_phases[contact].addItemReference(self.task_interface.getTask(f'z_{contact}'),
213
+ self._ref_trjs[contact][2, 0:1],
214
+ nodes=list(range(0, flight_phase_short_duration)))
215
+
216
+ self._touchdown_phases[contact]=None
217
+
218
+ else: # foot traj in velocity
219
+ # ref vel traj
220
+ self._ref_vtrjs[contact]=np.zeros(shape=[7, self.task_interface.prb.getNNodes()]) # allocate traj
221
+ # of max length eual to number of nodes
222
+ self._ref_vtrjs[contact][2, :] = np.atleast_2d(0)
223
+
224
+ if self._xyvel_task_found: # xy
225
+ self._flight_phases[contact].addItemReference(self.task_interface.getTask(f'vxy_{contact}'),
226
+ self._ref_vtrjs[contact][0:2, 0:1],
227
+ nodes=list(range(0, flight_phase_short_duration)))
228
+ # z
229
+ self._flight_phases[contact].addItemReference(self.task_interface.getTask(f'vz_{contact}'),
230
+ self._ref_vtrjs[contact][2, 0:1],
231
+ nodes=list(range(0, flight_phase_short_duration)))
232
+ self._touchdown_phases[contact]=self._contact_timelines[contact].createPhase(flight_phase_short_duration,
233
+ f'touchdown_{contact}_short')
234
+ # self._touchdown_phases[contact].addItemReference(self.task_interface.getTask(f'vz_{contact}'),
235
+ # self._ref_vtrjs[contact][2, 0:1],
236
+ # nodes=list(range(0, flight_phase_short_duration)))
237
+
238
+ # ee_vel=self._fkd_contacts[contact](q=self._model.q,
239
+ # qdot=self._model.v)['ee_vel_linear']
240
+ # cstr = self.task_interface.prb.createConstraint(f'{contact}_vert', ee_vel[0:2], [])
241
+ # self._flight_phases[contact].addConstraint(cstr, nodes=[0, flight_phase_short_duration-1])
242
+ if self._keep_yaw_vert:
243
+ # keep ankle vertical
244
+ c_ori = self._model.kd.fk(contact)(q=self._model.q)['ee_rot'][2, :]
245
+ cost_ori = self.task_interface.prb.createResidual(f'{contact}_ori', self._yaw_vertical_weight * (c_ori.T - np.array([0, 0, 1])))
246
+ # flight_phase.addCost(cost_ori, nodes=list(range(0, flight_duration+post_landing_stance)))
247
+
248
+ if self._vertical_landing and self._touchdown_phases[contact] is not None:
249
+ v_xy=self._fkd_contacts[contact](q=self._model.q, qdot=self._model.v)['ee_vel_linear'][0:2]
250
+ vertical_landing=self.task_interface.prb.createResidual(f'{contact}_only_vert_v',
251
+ self._landing_vert_weight * v_xy,
252
+ nodes=[])
253
+ self._touchdown_phases[contact].addCost(vertical_landing, nodes=list(range(0, short_stance_duration)))
254
+
255
+ self._name_to_idx_map[contact]=j
256
+
257
+ j+=1
258
+
259
+ # current pos [c0, c1, ....], current length, nominal length, nom. apex, no. landing height, landing dx, landing dy (local world aligned)
260
+ self._flight_info_now=np.zeros(shape=(7*self.n_contacts))
261
+
262
+ self.update()
263
+
264
+ def _reset_contact_timelines(self):
265
+
266
+ for contact in self._model.cmap.keys():
267
+
268
+ idx=self._name_to_idx_map[contact]
269
+ # we follow same order as in shm for more efficient writing
270
+ self._flight_info_now[idx]= -1.0 # pos [nodes]
271
+ self._flight_info_now[idx+1*self.n_contacts]= -1.0 # duration (remaining) [nodes]
272
+ self._flight_info_now[idx+2*self.n_contacts]=self._flight_duration_default # [nodes]
273
+ self._flight_info_now[idx+3*self.n_contacts]=self._step_height_default
274
+ self._flight_info_now[idx+4*self.n_contacts]=self._dh_default
275
+ self._flight_info_now[idx+5*self.n_contacts]=self._land_dx_default
276
+ self._flight_info_now[idx+6*self.n_contacts]=self._land_dy_default
277
+ # fill timeline with stances
278
+ contact_timeline=self._contact_timelines[contact]
279
+ contact_timeline.clear() # remove phases
280
+ short_stance_phase = contact_timeline.getRegisteredPhase(f'stance_{contact}_short')
281
+ while contact_timeline.getEmptyNodes() > 0:
282
+ contact_timeline.addPhase(short_stance_phase)
283
+
284
+ self.update()
285
+
286
+ def reset(self):
287
+ # self.phase_manager.clear()
288
+ self.task_interface.reset()
289
+ self._reset_contact_timelines()
290
+
291
+ def set_f_reg(self,
292
+ contact_name,
293
+ scale: float = 4.0):
294
+ f_refs=self._f_reg_ref[contact_name]
295
+ for force in f_refs:
296
+ ref=self._total_weight/(scale*len(f_refs))
297
+ force.assign(ref)
298
+
299
+ def set_flight_duration(self, contact_name, val: float):
300
+ self._flight_info_now[self._name_to_idx_map[contact_name]+2*self.n_contacts]=val
301
+
302
+ def get_flight_duration(self, contact_name):
303
+ return self._flight_info_now[self._name_to_idx_map[contact_name]+2*self.n_contacts]
304
+
305
+ def set_step_apexdh(self, contact_name, val: float):
306
+ self._flight_info_now[self._name_to_idx_map[contact_name]+3*self.n_contacts]=val
307
+
308
+ def get_step_apexdh(self, contact_name):
309
+ return self._flight_info_now[self._name_to_idx_map[contact_name]+3*self.n_contacts]
310
+
311
+ def set_step_enddh(self, contact_name, val: float):
312
+ self._flight_info_now[self._name_to_idx_map[contact_name]+4*self.n_contacts]=val
313
+
314
+ def get_step_enddh(self, contact_name):
315
+ return self._flight_info_now[self._name_to_idx_map[contact_name]+4*self.n_contacts]
316
+
317
+ def get_step_landing_dx(self, contact_name):
318
+ return self._flight_info_now[self._name_to_idx_map[contact_name]+5*self.n_contacts]
319
+
320
+ def set_step_landing_dx(self, contact_name, val: float):
321
+ self._flight_info_now[self._name_to_idx_map[contact_name]+5*self.n_contacts]=val
322
+
323
+ def get_step_landing_dy(self, contact_name):
324
+ return self._flight_info_now[self._name_to_idx_map[contact_name]+6*self.n_contacts]
325
+
326
+ def set_step_landing_dy(self, contact_name, val: float):
327
+ self._flight_info_now[self._name_to_idx_map[contact_name]+6*self.n_contacts]=val
328
+
329
+ def add_stand(self, contact_name):
330
+ # always add stand at the end of the horizon
331
+ timeline = self._contact_timelines[contact_name]
332
+ if timeline.getEmptyNodes() > 0:
333
+ timeline.addPhase(timeline.getRegisteredPhase(f'stance_{contact_name}_short'))
334
+
335
+ def add_flight(self, contact_name,
336
+ robot_q: np.ndarray):
337
+
338
+ timeline = self._contact_timelines[contact_name]
339
+
340
+ flights_on_horizon=self._contact_timelines[contact_name].getPhaseIdx(self._flight_phases[contact_name])
341
+
342
+ last_flight_idx=self._injection_node-1 # default to make things work
343
+ if not len(flights_on_horizon)==0: # some flight phases are there
344
+ last_flight_idx=flights_on_horizon[-1]+self._post_flight_stance
345
+
346
+ if last_flight_idx<self._injection_node: # allow injecting
347
+
348
+ flight_duration_req=int(self.get_flight_duration(contact_name=contact_name))
349
+ flight_apex_req=self.get_step_apexdh(contact_name=contact_name)
350
+ flight_enddh_req=self.get_step_enddh(contact_name=contact_name)
351
+ flight_land_dx_req=self.get_step_landing_dx(contact_name=contact_name)
352
+ flight_land_dy_req=self.get_step_landing_dy(contact_name=contact_name)
353
+ if not flight_duration_req>1:
354
+ Journal.log(self.__class__.__name__,
355
+ "add_flight",
356
+ f"Got flight duration {flight_duration_req} < 1!",
357
+ LogType.WARN,
358
+ throw_when_excep=True)
359
+
360
+ # process requests to ensure flight params are valid
361
+ # duration
362
+ if flight_duration_req<self._flight_duration_min:
363
+ flight_duration_req=self._flight_duration_min
364
+ if flight_duration_req>self._flight_duration_max:
365
+ flight_duration_req=self._flight_duration_max
366
+ # apex height
367
+ if flight_apex_req<self._step_height_min:
368
+ flight_apex_req=self._step_height_min
369
+ if flight_apex_req>self._step_height_max:
370
+ flight_apex_req=self._step_height_max
371
+ # landing height
372
+ if flight_enddh_req<self._dh_min:
373
+ flight_enddh_req=self._dh_min
374
+ if flight_enddh_req>self._dh_max:
375
+ flight_enddh_req=self._dh_max
376
+ # landing dx
377
+ if flight_land_dx_req<self._land_dx_min:
378
+ flight_land_dx_req=self._land_dx_min
379
+ if flight_land_dx_req>self._land_dx_max:
380
+ flight_land_dx_req=self._land_dx_max
381
+ # landing dy
382
+ if flight_land_dy_req<self._land_dy_min:
383
+ flight_land_dy_req=self._land_dy_min
384
+ if flight_land_dy_req>self._land_dy_max:
385
+ flight_land_dy_req=self._land_dy_max
386
+
387
+ land_dx_w = flight_land_dx_req
388
+ land_dy_w = flight_land_dy_req
389
+ if self._xypos_task_found or self._xyvel_task_found:
390
+ # landing dx/dy are specified in horizontal frame; rotate into world aligned frame
391
+ q_base = robot_q[3:7]
392
+ if q_base.ndim == 1:
393
+ q_base = q_base.reshape(-1, 1)
394
+ q_w = q_base[3, 0]
395
+ q_x = q_base[0, 0]
396
+ q_y = q_base[1, 0]
397
+ q_z = q_base[2, 0]
398
+ r11 = 1 - 2 * (q_y * q_y + q_z * q_z)
399
+ r21 = 2 * (q_x * q_y + q_z * q_w)
400
+ norm = np.hypot(r11, r21)
401
+ if norm > 0.0:
402
+ cos_yaw = r11 / norm
403
+ sin_yaw = r21 / norm
404
+ else:
405
+ cos_yaw = 1.0
406
+ sin_yaw = 0.
407
+
408
+ land_dx_w = flight_land_dx_req * cos_yaw - flight_land_dy_req * sin_yaw
409
+ land_dy_w = flight_land_dx_req * sin_yaw + flight_land_dy_req * cos_yaw
410
+
411
+ if self._ref_vtrjs[contact_name] is not None and \
412
+ self._ref_trjs[contact_name] is not None: # only allow one mode (pos/velocity traj)
413
+ Journal.log(self.__class__.__name__,
414
+ "add_flight",
415
+ f"Both pos and vel traj for contact {contact_name} are not None! This is not allowed, aborting.",
416
+ LogType.EXCEP,
417
+ throw_when_excep=True)
418
+
419
+ # inject pos traj if pos mode
420
+ if self._ref_trjs[contact_name] is not None:
421
+ # recompute trajectory online (just needed if using pos traj)
422
+ foot_pos=self._fk_contacts[contact_name](q=robot_q)['ee_pos'].elements()
423
+ starting_pos=foot_pos[2] # compute foot traj (local world aligned)
424
+ starting_x_pos=foot_pos[0]
425
+ starting_y_pos=foot_pos[1]
426
+ # starting_pos=0.0
427
+ self._ref_trjs[contact_name][2, 0:flight_duration_req]=np.atleast_2d(self._tg.from_derivatives(
428
+ flight_duration_req,
429
+ p_start=starting_pos,
430
+ p_goal=starting_pos+flight_enddh_req,
431
+ clearance=flight_apex_req,
432
+ derivatives=self._traj_der,
433
+ second_der=self._traj_second_der,
434
+ third_der=self._third_traj_der)
435
+ )
436
+ if self._xypos_task_found:
437
+ self._ref_trjs[contact_name][0, 0:flight_duration_req]=starting_x_pos+land_dx_w
438
+ self._ref_trjs[contact_name][1, 0:flight_duration_req]=starting_y_pos+land_dy_w
439
+
440
+ for i in range(flight_duration_req):
441
+ res, phase_token=timeline.addPhase(self._flight_phases[contact_name],
442
+ pos=self._injection_node+i,
443
+ absolute_position=True)
444
+ phase_token.setItemReference(f'z_{contact_name}',
445
+ self._ref_trjs[contact_name][:, i])
446
+ if self._xypos_task_found:
447
+ phase_token.setItemReference(f'xy_{contact_name}',
448
+ self._ref_trjs[contact_name][:, i])
449
+ if self._touchdown_phases[contact_name] is not None:
450
+ # add touchdown phase for forcing vertical landing
451
+ res, phase_token=timeline.addPhase(self._touchdown_phases[contact_name],
452
+ pos=self._injection_node+flight_duration_req,
453
+ absolute_position=True)
454
+
455
+ # inject vel traj if vel mode
456
+ if self._ref_vtrjs[contact_name] is not None:
457
+ self._ref_vtrjs[contact_name][2, 0:flight_duration_req]=np.atleast_2d(self._tg.derivative_of_trajectory(
458
+ flight_duration_req,
459
+ p_start=0.0,
460
+ p_goal=flight_enddh_req,
461
+ clearance=flight_apex_req,
462
+ derivatives=self._traj_der,
463
+ second_der=self._traj_second_der,
464
+ third_der=self._third_traj_der))
465
+ if self._xyvel_task_found: # compute vel reference using problem dt and flight length
466
+ flight_duration_sec=float(flight_duration_req)*self._dt
467
+ self._ref_vtrjs[contact_name][0, 0:flight_duration_req]=land_dx_w/flight_duration_sec
468
+ self._ref_vtrjs[contact_name][1, 0:flight_duration_req]=land_dy_w/flight_duration_sec
469
+
470
+ for i in range(flight_duration_req):
471
+ res, phase_token=timeline.addPhase(self._flight_phases[contact_name],
472
+ pos=self._injection_node+i,
473
+ absolute_position=True)
474
+ phase_token.setItemReference(f'vz_{contact_name}',
475
+ self._ref_vtrjs[contact_name][2:3, i:i+1])
476
+ if self._touchdown_phases[contact_name] is not None:
477
+ # add touchdown phase for forcing vertical landing
478
+ res, phase_token=timeline.addPhase(self._touchdown_phases[contact_name],
479
+ pos=self._injection_node+flight_duration_req,
480
+ absolute_position=True)
481
+
482
+ if timeline.getEmptyNodes() > 0: # fill empty nodes at the end of the horizon, if any, with stance
483
+ timeline.addPhase(timeline.getRegisteredPhase(f'stance_{contact_name}_short'))
484
+
485
+ def update(self):
486
+ self._phase_manager.update()
487
+
488
+ def update_flight_info(self, timeline_name):
489
+
490
+ # get current position and remaining duration of flight phases over the horizon for a single contact
491
+
492
+ # phase indexes over timeline
493
+ phase_idxs=self._contact_timelines[timeline_name].getPhaseIdx(self._flight_phases[timeline_name])
494
+
495
+ if not len(phase_idxs)==0: # at least one flight phase on horizon -> read info from timeline
496
+
497
+ # all active phases on timeline
498
+ active_phases=self._contact_timelines[timeline_name].getActivePhases()
499
+
500
+ phase_idx_start=phase_idxs[0]
501
+ # active_nodes_start=active_phases[phase_idx_start].getActiveNodes()
502
+ pos_start=active_phases[phase_idx_start].getPosition()
503
+ # n_nodes_start=active_phases[phase_idx_start].getNNodes()
504
+
505
+ phase_idx_last=phase_idxs[-1] # just get info for last phase on the horizon
506
+ # active_nodes_last=active_phases[phase_idx_last].getActiveNodes()
507
+ pos_last=active_phases[phase_idx_last].getPosition()
508
+ # n_nodes_last=active_phases[phase_idx_last].getNNodes()
509
+
510
+ # write to info
511
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+0*self.n_contacts]=pos_last
512
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+1*self.n_contacts]=pos_last - pos_start
513
+
514
+ return True
515
+
516
+ return False
517
+
518
+ def get_flight_info(self, timeline_name):
519
+ return (self._flight_info_now[self._name_to_idx_map[timeline_name]+0*self.n_contacts],
520
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+1*self.n_contacts],
521
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+2*self.n_contacts],
522
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+3*self.n_contacts],
523
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+4*self.n_contacts],
524
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+5*self.n_contacts],
525
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+6*self.n_contacts])
526
+
527
+ def get_flight_info_all(self):
528
+ return self._flight_info_now
529
+
530
+ def set_ref_pos(self,
531
+ timeline_name:str,
532
+ ref_height: np.array = None,
533
+ threshold: float = 0.05):
534
+
535
+ if ref_height is not None:
536
+ self._ref_trjs[timeline_name][2, :]=ref_height
537
+ if ref_height>threshold:
538
+ self.add_flight(timeline_name=timeline_name)
539
+ this_flight_token_idx=self._contact_timelines[timeline_name].getPhaseIdx(self._flight_phases[timeline_name])[-1]
540
+ active_phases=self._contact_timelines[timeline_name].getActivePhases()
541
+ active_phases[this_flight_token_idx].setItemReference(f'z_{timeline_name}',
542
+ self._ref_trjs[timeline_name])
543
+ else:
544
+ self.add_stand(timeline_name=timeline_name)
545
+
546
+ def set_force_feedback(self,
547
+ timeline_name: str,
548
+ force_norm: float):
549
+
550
+ flight_tokens=self._contact_timelines[timeline_name].getPhaseIdx(self._flight_phases[timeline_name])
551
+ contact_tokens=self._contact_phases[timeline_name].getPhaseIdx(self._contact_phases[timeline_name])
552
+ if not len(flight_tokens)==0:
553
+ first_flight=flight_tokens[0]
554
+ first_flight
555
+
556
+ def check_horizon_full(self,
557
+ timeline_name):
558
+ timeline = self._contact_timelines[timeline_name]
559
+ if timeline.getEmptyNodes() > 0:
560
+ error = f"Empty nodes detected over the horizon for timeline {timeline}! Make sure to fill the whole horizon with valid phases!!"
561
+ Journal.log(self.__class__.__name__,
562
+ "check_horizon_full",
563
+ error,
564
+ LogType.EXCEP,
565
+ throw_when_excep = True)
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/horizon_imports.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # robot modeling and automatic differentiation
2
+ import casadi_kin_dyn.py3casadi_kin_dyn as casadi_kin_dyn
3
+ import casadi as cs
4
+
5
+ # horizon stuff
6
+ import horizon.utils.kin_dyn as kd
7
+ from horizon.problem import Problem
8
+ from horizon.rhc.model_description import FullModelInverseDynamics
9
+ from horizon.rhc.taskInterface import TaskInterface
10
+ from horizon.rhc.tasks.interactionTask import VertexContact
11
+ from horizon.rhc.tasks.contactTask import ContactTask
12
+ from horizon.utils import trajectoryGenerator, utils
13
+
14
+ # phase managing
15
+ import phase_manager.pymanager as pymanager
16
+ import phase_manager.pyphase as pyphase
17
+ import phase_manager.pytimeline as pytimeline
18
+
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/horizon_imports_glob.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Dynamically import all necessary Horizon and related dependencies.
3
+ This function is intended to be used within the import_child_lib method
4
+ of a class, enabling the parent process to load all required libraries.
5
+ """
6
+ def import_horizon_global():
7
+ # Global imports to make modules accessible in child processes
8
+ global casadi_kin_dyn, cs, kd, Problem, FullModelInverseDynamics
9
+ global TaskInterface, VertexContact, ContactTask, trajectoryGenerator, utils
10
+ global pymanager, pyphase, pytimeline
11
+
12
+ # robot modeling and automatic differentiation
13
+ import casadi_kin_dyn.py3casadi_kin_dyn as casadi_kin_dyn
14
+ import casadi as cs
15
+
16
+ # horizon stuff
17
+ import horizon.utils.kin_dyn as kd
18
+ from horizon.problem import Problem
19
+ from horizon.rhc.model_description import FullModelInverseDynamics
20
+ from horizon.rhc.taskInterface import TaskInterface
21
+ from horizon.rhc.tasks.interactionTask import VertexContact
22
+ from horizon.rhc.tasks.contactTask import ContactTask
23
+ from horizon.utils import trajectoryGenerator, utils
24
+
25
+ # phase managing
26
+ import phase_manager.pymanager as pymanager
27
+ import phase_manager.pyphase as pyphase
28
+ import phase_manager.pytimeline as pytimeline
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/hybrid_quad_rhc.py ADDED
@@ -0,0 +1,1243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mpc_hive.controllers.rhc import RHController
2
+ # from perf_sleep.pyperfsleep import PerfSleep
3
+ # from mpc_hive.utilities.cpu_utils.core_utils import get_memory_usage
4
+
5
+ from aug_mpc.controllers.rhc.horizon_based.horizon_imports import *
6
+
7
+ from aug_mpc.controllers.rhc.horizon_based.hybrid_quad_rhc_refs import HybridQuadRhcRefs
8
+ from aug_mpc.controllers.rhc.horizon_based.gait_manager import GaitManager
9
+
10
+ from EigenIPC.PyEigenIPC import VLevel
11
+ from EigenIPC.PyEigenIPC import Journal, LogType
12
+
13
+ import numpy as np
14
+
15
+ import os
16
+ # import shutil
17
+
18
+ import time
19
+ from abc import ABC, abstractmethod
20
+
21
+ from typing import Dict, List
22
+ import re
23
+
24
+ class HybridQuadRhc(RHController):
25
+
26
+ def __init__(self,
27
+ srdf_path: str,
28
+ urdf_path: str,
29
+ config_path: str,
30
+ robot_name: str, # used for shared memory namespaces
31
+ codegen_dir: str,
32
+ n_nodes:float = 25,
33
+ injection_node:int = 10,
34
+ dt: float = 0.02,
35
+ max_solver_iter = 1, # defaults to rt-iteration
36
+ open_loop: bool = True,
37
+ close_loop_all: bool = False,
38
+ dtype = np.float32,
39
+ verbose = False,
40
+ debug = False,
41
+ refs_in_hor_frame = True,
42
+ timeout_ms: int = 60000,
43
+ custom_opts: Dict = {}):
44
+
45
+ self._refs_in_hor_frame = refs_in_hor_frame
46
+
47
+ self._injection_node = injection_node
48
+
49
+ self._open_loop = open_loop
50
+ self._close_loop_all = close_loop_all
51
+
52
+ self._codegen_dir = codegen_dir
53
+ if not os.path.exists(self._codegen_dir):
54
+ os.makedirs(self._codegen_dir)
55
+ # else:
56
+ # # Directory already exists, delete it and recreate
57
+ # shutil.rmtree(self._codegen_dir)
58
+ # os.makedirs(self._codegen_dir)
59
+
60
+ self.step_counter = 0
61
+ self.sol_counter = 0
62
+
63
+ self.max_solver_iter = max_solver_iter
64
+
65
+ self._timer_start = time.perf_counter()
66
+ self._prb_update_time = time.perf_counter()
67
+ self._phase_shift_time = time.perf_counter()
68
+ self._task_ref_update_time = time.perf_counter()
69
+ self._rti_time = time.perf_counter()
70
+
71
+ self.robot_name = robot_name
72
+
73
+ self.config_path = config_path
74
+
75
+ self.urdf_path = urdf_path
76
+ # read urdf and srdf files
77
+ with open(self.urdf_path, 'r') as file:
78
+ self.urdf = file.read()
79
+ self._base_init = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0])
80
+
81
+ self._c_timelines = dict()
82
+ self._f_reg_timelines = dict()
83
+
84
+ self._custom_opts={"replace_continuous_joints": False,
85
+ "use_force_feedback": False,
86
+ "lin_a_feedback": False,
87
+ "is_open_loop": self._open_loop, # fully open (just for db)
88
+ "fully_closed": False, # closed loop with full feedback (just for db)
89
+ "closed_partial": False, # closed loop with partial feedback
90
+ "adaptive_is": True, # closed loop with adaptation
91
+ "estimate_v_root": False, # when adaptive_is or closed_partial, estimate vbase
92
+ "alpha_from_outside": False, # alpha set ext. from shared memory
93
+ "alpha_half": 1.0,
94
+ "only_vel_wheels": True, # whether wheels (if present) are just vel controlled
95
+ "use_jnt_v_feedback": False
96
+ }
97
+
98
+ self._custom_opts.update(custom_opts)
99
+
100
+ self._alpha_half=self._custom_opts["alpha_half"]
101
+
102
+ if self._open_loop:
103
+ self._custom_opts["fully_closed"]=False
104
+ self._custom_opts["adaptive_is"]=False
105
+ self._custom_opts["closed_partial"]=False
106
+ else:
107
+ self._custom_opts["is_open_loop"]=False
108
+ if self._custom_opts["fully_closed"]:
109
+ self._custom_opts["adaptive_is"]=False
110
+ self._custom_opts["closed_partial"]=False
111
+ self._custom_opts["lin_a_feedback"]=False
112
+ if self._custom_opts["closed_partial"]:
113
+ self._custom_opts["adaptive_is"]=False
114
+ self._custom_opts["fully_closed"]=False
115
+ self._custom_opts["lin_a_feedback"]=False
116
+ if self._custom_opts["adaptive_is"]:
117
+ self._custom_opts["closed_partial"]=False
118
+ self._custom_opts["fully_closed"]=False
119
+
120
+ super().__init__(srdf_path=srdf_path,
121
+ n_nodes=n_nodes,
122
+ dt=dt,
123
+ namespace=self.robot_name,
124
+ dtype=dtype,
125
+ verbose=verbose,
126
+ debug=debug,
127
+ timeout_ms=timeout_ms)
128
+
129
+ self._rhc_fpaths.append(self.config_path)
130
+
131
+ def _config_override(self):
132
+ pass
133
+
134
+ def _post_problem_init(self):
135
+
136
+ self.rhc_costs={}
137
+ self.rhc_constr={}
138
+
139
+ self._fail_idx_scale=0.0
140
+ self._expl_idx_window_size=int(1*self._n_nodes)
141
+ self._explosion_idx_buffer=np.zeros((1,self._expl_idx_window_size))
142
+ self._expl_idx_counter=0
143
+ self._expl_idx_buffer_counter=0
144
+
145
+ self._pred_node_idx=self._n_nodes-1
146
+
147
+ self._nq=self.nq()
148
+ self._nq_jnts=self._nq-7# assuming floating base
149
+ self._nv=self.nv()
150
+ self._nv_jnts=self._nv-6
151
+
152
+ self._alphas_q_root=np.zeros((7, 1), dtype=self._dtype)
153
+ self._alphas_q_jnts=np.zeros((self._nq_jnts, 1), dtype=self._dtype)
154
+ self._alphas_twist_root=np.zeros((6, 1), dtype=self._dtype)
155
+ self._alphas_v_jnts=np.zeros((self._nv_jnts, 1), dtype=self._dtype)
156
+ self._alphas_a=np.zeros((self._nv, 1), dtype=self._dtype)
157
+ self._alphas_q_root[:, :]=1.0 # default to all open
158
+ self._alphas_q_jnts[:, :]=1.0
159
+ self._alphas_twist_root[:, :]=1.0
160
+ self._alphas_v_jnts[:, :]=1.0
161
+ self._alphas_a[:, :]=1.0
162
+
163
+ def _init_problem(self,
164
+ fixed_jnt_patterns: List[str] = None,
165
+ wheels_patterns: List[str] = None,
166
+ foot_linkname: str = None,
167
+ flight_duration: int = 10,
168
+ post_flight_stance: int = 3,
169
+ step_height: float = 0.12,
170
+ keep_yaw_vert: bool = False,
171
+ yaw_vertical_weight: float = 2.0,
172
+ vertical_landing: bool = False,
173
+ vertical_land_weight: float = 1.0,
174
+ phase_force_reg: float = 1e-2,
175
+ vel_bounds_weight: float = 1.0):
176
+
177
+ self._fixed_jnt_patterns=fixed_jnt_patterns
178
+
179
+ self._config_override()
180
+
181
+ Journal.log(self.__class__.__name__,
182
+ "_init_problem",
183
+ f" Will use horizon config file at {self.config_path}",
184
+ LogType.INFO,
185
+ throw_when_excep=True)
186
+
187
+ self._vel_bounds_weight=vel_bounds_weight
188
+ self._phase_force_reg=phase_force_reg
189
+ self._yaw_vertical_weight=yaw_vertical_weight
190
+ self._vertical_land_weight=vertical_land_weight
191
+ # overrides parent
192
+ self._prb = Problem(self._n_intervals,
193
+ receding=True,
194
+ casadi_type=cs.SX)
195
+ self._prb.setDt(self._dt)
196
+
197
+ if "replace_continuous_joints" in self._custom_opts:
198
+ # continous joints are parametrized in So2
199
+ if self._custom_opts["replace_continuous_joints"]:
200
+ self.urdf = self.urdf.replace('continuous', 'revolute')
201
+ else:
202
+ self.urdf = self.urdf.replace('continuous', 'revolute')
203
+
204
+ self._kin_dyn = casadi_kin_dyn.CasadiKinDyn(self.urdf) # used for getting joint names
205
+ self._assign_controller_side_jnt_names(jnt_names=self._get_robot_jnt_names())
206
+
207
+ self._init_robot_homer()
208
+
209
+ # handle fixed joints
210
+ fixed_joint_map={}
211
+ if self._fixed_jnt_patterns is not None:
212
+ for jnt_name in self._get_robot_jnt_names():
213
+ for fixed_jnt_pattern in self._fixed_jnt_patterns:
214
+ if fixed_jnt_pattern in jnt_name:
215
+ fixed_joint_map.update({f"{jnt_name}":
216
+ self._homer.get_homing_val(jnt_name=jnt_name)})
217
+ break # do not search for other pattern matches
218
+
219
+ if not len(fixed_joint_map)==0: # we need to recreate kin dyn and homers
220
+ Journal.log(self.__class__.__name__,
221
+ "_init_problem",
222
+ f"Will fix following joints: \n{str(fixed_joint_map)}",
223
+ LogType.INFO,
224
+ throw_when_excep=True)
225
+ # with the fixed joint map
226
+ self._kin_dyn = casadi_kin_dyn.CasadiKinDyn(self.urdf,fixed_joints=fixed_joint_map)
227
+ # assign again controlled joints names
228
+ self._assign_controller_side_jnt_names(jnt_names=self._get_robot_jnt_names())
229
+ # updated robot homer for controlled joints
230
+ self._init_robot_homer()
231
+
232
+ # handle continuous joints (need to change homing and retrieve
233
+ # cont jnts indexes) and homing
234
+ self._continuous_joints=self._get_continuous_jnt_names()
235
+ # reduced
236
+ self._continuous_joints_idxs=[]
237
+ self._continuous_joints_idxs_cos=[]
238
+ self._continuous_joints_idxs_sin=[]
239
+ self._continuous_joints_idxs_red=[]
240
+ self._rev_joints_idxs=[]
241
+ self._rev_joints_idxs_red=[]
242
+ # qfull
243
+ self._continuous_joints_idxs_qfull=[]
244
+ self._continuous_joints_idxs_cos_qfull=[]
245
+ self._continuous_joints_idxs_sin_qfull=[]
246
+ self._continuous_joints_idxs_red_qfull=[]
247
+ self._rev_joints_idxs_qfull=[]
248
+ self._rev_joints_idxs_red_qfull=[]
249
+ jnt_homing=[""]*(len(self._homer.get_homing())+len(self._continuous_joints))
250
+ jnt_names=self._get_robot_jnt_names()
251
+ for i in range(len(jnt_names)):
252
+ jnt=jnt_names[i]
253
+ index=self._get_jnt_id(jnt)# accounting for floating joint
254
+ homing_idx=index-7 # homing is only for actuated joints
255
+ homing_value=self._homer.get_homing_val(jnt)
256
+ if jnt in self._continuous_joints:
257
+ jnt_homing[homing_idx]=np.cos(homing_value).item()
258
+ jnt_homing[homing_idx+1]=np.sin(homing_value).item()
259
+ # just actuated joints
260
+ self._continuous_joints_idxs.append(homing_idx) # cos
261
+ self._continuous_joints_idxs.append(homing_idx+1) # sin
262
+ self._continuous_joints_idxs_cos.append(homing_idx)
263
+ self._continuous_joints_idxs_sin.append(homing_idx+1)
264
+ self._continuous_joints_idxs_red.append(i)
265
+ # q full
266
+ self._continuous_joints_idxs_qfull.append(index) # cos
267
+ self._continuous_joints_idxs_qfull.append(index+1) # sin
268
+ self._continuous_joints_idxs_cos_qfull.append(index)
269
+ self._continuous_joints_idxs_sin_qfull.append(index+1)
270
+ self._continuous_joints_idxs_red_qfull.append(i+7)
271
+ else:
272
+ jnt_homing[homing_idx]=homing_value
273
+ # just actuated joints
274
+ self._rev_joints_idxs.append(homing_idx)
275
+ self._rev_joints_idxs_red.append(i)
276
+ # q full
277
+ self._rev_joints_idxs_qfull.append(index)
278
+ self._rev_joints_idxs_red_qfull.append(i+7)
279
+
280
+ self._jnts_q_reduced=None
281
+ if not len(self._continuous_joints)==0:
282
+ cont_joints=", ".join(self._continuous_joints)
283
+
284
+ Journal.log(self.__class__.__name__,
285
+ "_init_problem",
286
+ f"The following continuous joints were found: \n{cont_joints}",
287
+ LogType.INFO,
288
+ throw_when_excep=True)
289
+ # preallocating data
290
+ self._jnts_q_reduced=np.zeros((1,self.nv()-6),dtype=self._dtype)
291
+ self._jnts_q_expanded=np.zeros((self.nq()-7,1),dtype=self._dtype)
292
+ self._full_q_reduced=np.zeros((7+len(jnt_names), self._n_nodes),dtype=self._dtype)
293
+ self._jnts_q_delta_expanded=np.zeros((self.nq()-7,1),dtype=self._dtype)
294
+ else:
295
+ self._custom_opts["replace_continuous_joints"]=True
296
+ Journal.log(self.__class__.__name__,
297
+ "_init_problem",
298
+ f"No continuous joints were found.",
299
+ LogType.INFO,
300
+ throw_when_excep=True)
301
+
302
+ # retrieve wheels indexes (not considering continuous joints,
303
+ # ok just for v, eff, etc..)
304
+ self._wheel_patterns=wheels_patterns
305
+ self._wheels_idxs_v=self._get_wheels_jnt_v_idxs(wheel_patterns=self._wheel_patterns)
306
+ self._f0 = [0, 0, self._kin_dyn.mass()/4*9.81]
307
+
308
+ # we can create an init for the base
309
+ init = self._base_init.tolist() + jnt_homing
310
+
311
+ if foot_linkname is not None:
312
+ FK = self._kin_dyn.fk(foot_linkname) # just to get robot reference height
313
+ ground_level = FK(q=init)['ee_pos']
314
+ self._base_init[2] = -ground_level[2] # override init
315
+
316
+ self._model = FullModelInverseDynamics(problem=self._prb,
317
+ kd=self._kin_dyn,
318
+ q_init=self._homer.get_homing_map(),
319
+ base_init=self._base_init)
320
+
321
+ self._ti = TaskInterface(prb=self._prb,
322
+ model=self._model,
323
+ max_solver_iter=self.max_solver_iter,
324
+ debug = self._debug,
325
+ verbose = self._verbose,
326
+ codegen_workdir = self._codegen_dir)
327
+ self._ti.setTaskFromYaml(self.config_path)
328
+
329
+ # setting initial base pos ref if exists
330
+ base_pos = self._ti.getTask('base_height')
331
+ if base_pos is not None:
332
+ base_pos.setRef(np.atleast_2d(self._base_init).T)
333
+
334
+ self._pm = pymanager.PhaseManager(self._n_nodes, debug=False) # intervals or nodes?????
335
+
336
+ self._gm = GaitManager(self._ti,
337
+ self._pm,
338
+ self._injection_node,
339
+ keep_yaw_vert=keep_yaw_vert,
340
+ yaw_vertical_weight=self._yaw_vertical_weight,
341
+ vertical_landing=vertical_landing,
342
+ landing_vert_weight=self._vertical_land_weight,
343
+ phase_force_reg=self._phase_force_reg,
344
+ custom_opts=self._custom_opts,
345
+ flight_duration=flight_duration,
346
+ post_flight_stance=post_flight_stance,
347
+ step_height=step_height,
348
+ dh=0.0)
349
+
350
+ self._ti.model.q.setBounds(self._ti.model.q0, self._ti.model.q0, nodes=0)
351
+ self._ti.model.v.setBounds(self._ti.model.v0, self._ti.model.v0, nodes=0)
352
+ self._ti.model.q.setInitialGuess(self._ti.model.q0)
353
+ self._ti.model.v.setInitialGuess(self._ti.model.v0)
354
+ for _, cforces in self._ti.model.cmap.items():
355
+ n_contact_f=len(cforces)
356
+ for c in cforces:
357
+ c.setInitialGuess(np.array(self._f0)/n_contact_f)
358
+
359
+ vel_lims = self._model.kd.velocityLimits()
360
+ import horizon.utils as utils
361
+ self._prb.createResidual('vel_lb_barrier', self._vel_bounds_weight*utils.utils.barrier(vel_lims[7:] - self._model.v[7:]))
362
+ self._prb.createResidual('vel_ub_barrier', self._vel_bounds_weight*utils.utils.barrier1(-1 * vel_lims[7:] - self._model.v[7:]))
363
+
364
+ self._meas_lin_a_par=None
365
+ # if self._custom_opts["lin_a_feedback"]:
366
+ # # acceleration feedback on first node
367
+ # self._meas_lin_a_par=self._prb.createParameter(name="lin_a_feedback",
368
+ # dim=3, nodes=0)
369
+ # base_lin_a_prb=self._prb.getInput().getVars()[0:3]
370
+ # self._prb.createConstraint('lin_acceleration_feedback', base_lin_a_prb - self._meas_lin_a_par,
371
+ # nodes=[0])
372
+
373
+ # if not self._open_loop:
374
+ # # we create a residual cost to be used as an attractor to the measured state on the first node
375
+ # # hard constraints injecting meas. states are pure EVIL!
376
+ # prb_state=self._prb.getState()
377
+ # full_state=prb_state.getVars()
378
+ # state_dim=prb_state.getBounds()[0].shape[0]
379
+ # meas_state=self._prb.createParameter(name="measured_state",
380
+ # dim=state_dim, nodes=0)
381
+ # self._prb.createResidual('meas_state_attractor', meas_state_attractor_weight * (full_state - meas_state),
382
+ # nodes=[0])
383
+
384
+ self._ti.finalize()
385
+ self._ti.bootstrap()
386
+
387
+ self._ti.init_inv_dyn_for_res() # we initialize some objects for sol. postprocessing purposes
388
+ self._ti.load_initial_guess()
389
+
390
+ self.n_dofs = self._get_ndofs() # after loading the URDF and creating the controller we
391
+ # know n_dofs -> we assign it (by default = None)
392
+
393
+ self.n_contacts = len(self._model.cmap.keys())
394
+
395
+ # remove variables bounds (before they were necessary to have a good
396
+ # quality bootstrap solution)
397
+ self._q_inf=np.zeros((self.nq(), 1))
398
+ self._q_inf[:, :]=np.inf
399
+ self._v_inf=np.zeros((self.nv(), 1))
400
+ self._v_inf[:, :]=np.inf
401
+ self._ti.model.q.setBounds(-self._q_inf, self._q_inf, nodes=0)
402
+ self._ti.model.v.setBounds(-self._v_inf, self._v_inf, nodes=0)
403
+
404
+ # self.horizon_anal = analyzer.ProblemAnalyzer(self._prb)
405
+
406
+ def get_file_paths(self):
407
+ # can be overriden by child
408
+ paths = super().get_file_paths()
409
+ return paths
410
+
411
+ def _get_quat_remap(self):
412
+ # overrides parent
413
+ return [1, 2, 3, 0] # mapping from robot quat. to Horizon's quaternion convention
414
+
415
+ def _zmp(self, model):
416
+
417
+ num = cs.SX([0, 0])
418
+ den = cs.SX([0])
419
+ pos_contact = dict()
420
+ force_val = dict()
421
+
422
+ q = cs.SX.sym('q', model.nq)
423
+ v = cs.SX.sym('v', model.nv)
424
+ a = cs.SX.sym('a', model.nv)
425
+
426
+ com = model.kd.centerOfMass()(q=q, v=v, a=a)['com']
427
+
428
+ n = cs.SX([0, 0, 1])
429
+ for c in model.fmap.keys():
430
+ pos_contact[c] = model.kd.fk(c)(q=q)['ee_pos']
431
+ force_val[c] = cs.SX.sym('force_val', 3)
432
+ num += (pos_contact[c][0:2] - com[0:2]) * cs.dot(force_val[c], n)
433
+ den += cs.dot(force_val[c], n)
434
+
435
+ zmp = com[0:2] + (num / den)
436
+ input_list = []
437
+ input_list.append(q)
438
+ input_list.append(v)
439
+ input_list.append(a)
440
+
441
+ for elem in force_val.values():
442
+ input_list.append(elem)
443
+
444
+ f = cs.Function('zmp', input_list, [zmp])
445
+ return f
446
+
447
+ def _add_zmp(self):
448
+
449
+ input_zmp = []
450
+
451
+ input_zmp.append(self._model.q)
452
+ input_zmp.append(self._model.v)
453
+ input_zmp.append(self._model.a)
454
+
455
+ for f_var in self._model.fmap.values():
456
+ input_zmp.append(f_var)
457
+
458
+ c_mean = cs.SX([0, 0, 0])
459
+ for c_name, f_var in self._model.fmap.items():
460
+ fk_c_pos = self._kin_dyn.fk(c_name)(q=self._model.q)['ee_pos']
461
+ c_mean += fk_c_pos
462
+
463
+ c_mean /= len(self._model.cmap.keys())
464
+
465
+ zmp_nominal_weight = 10.
466
+ zmp_fun = self._zmp(self._model)(*input_zmp)
467
+
468
+ if 'wheel_joint_1' in self._model.kd.joint_names():
469
+ zmp_residual = self._prb.createIntermediateResidual('zmp', zmp_nominal_weight * (zmp_fun[0:2] - c_mean[0:2]))
470
+
471
+ def _quaternion_multiply(self,
472
+ q1, q2):
473
+ x1, y1, z1, w1 = q1
474
+ x2, y2, z2, w2 = q2
475
+ w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2
476
+ x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
477
+ y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2
478
+ z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2
479
+ return np.array([x, y, z, w])
480
+
481
+ def _get_continuous_jnt_names(self):
482
+ import xml.etree.ElementTree as ET
483
+ root = ET.fromstring(self.urdf)
484
+ continuous_joints = []
485
+ for joint in root.findall('joint'):
486
+ joint_type = joint.get('type')
487
+ if joint_type == 'continuous':
488
+ joint_name = joint.get('name')
489
+ continuous_joints.append(joint_name)
490
+ return continuous_joints
491
+
492
+ def _get_wheels_jnt_v_idxs(self, wheel_patterns: List[str]):
493
+ jnt_names=self._get_robot_jnt_names()
494
+ wheels_idxs=[]
495
+ for i in range(len(jnt_names)):
496
+ jnt_name=jnt_names[i]
497
+ for wheel_pattern in wheel_patterns:
498
+ if wheel_pattern in jnt_name:
499
+ wheels_idxs.append(i)
500
+ break
501
+ return wheels_idxs
502
+
503
+ def _get_jnt_id(self, jnt_name):
504
+ return self._kin_dyn.joint_iq(jnt_name)
505
+
506
+ def _init_rhc_task_cmds(self):
507
+
508
+ rhc_refs = HybridQuadRhcRefs(gait_manager=self._gm,
509
+ robot_index_shm=self.controller_index,
510
+ robot_index_view=0, # when using optimize_mem the view if always of shape 1x...
511
+ namespace=self.namespace,
512
+ safe=False,
513
+ verbose=self._verbose,
514
+ vlevel=VLevel.V2,
515
+ use_force_feedback=self._custom_opts["use_force_feedback"],
516
+ optimize_mem=True)
517
+
518
+ rhc_refs.run()
519
+
520
+ rhc_refs.rob_refs.set_jnts_remapping(jnts_remapping=self._to_controller)
521
+ rhc_refs.rob_refs.set_q_remapping(q_remapping=self._get_quat_remap())
522
+
523
+ rhc_refs.set_default_refs(p_ref=np.atleast_2d(self._base_init)[:, 0:3],
524
+ q_ref=np.atleast_2d(self._base_init)[:, 3:7])
525
+
526
+ return rhc_refs
527
+
528
+ def get_vertex_fnames_from_ti(self):
529
+ tasks=self._ti.task_list
530
+ contact_f_names=[]
531
+ for task in tasks:
532
+ if isinstance(task, ContactTask):
533
+ interaction_task=task.dynamics_tasks[0]
534
+ contact_f_names.append(interaction_task.vertex_frames[0])
535
+ return contact_f_names
536
+
537
+ def _get_contact_names(self):
538
+ # should get contact names from vertex frames
539
+ # list(self._ti.model.cmap.keys())
540
+ return self.get_vertex_fnames_from_ti()
541
+
542
+ def _get_robot_jnt_names(self):
543
+
544
+ joints_names = self._kin_dyn.joint_names()
545
+ to_be_removed = ["universe",
546
+ "reference",
547
+ "world",
548
+ "floating",
549
+ "floating_base"]
550
+ for name in to_be_removed:
551
+ if name in joints_names:
552
+ joints_names.remove(name)
553
+
554
+ return joints_names
555
+
556
+ def _get_ndofs(self):
557
+ return len(self._model.joint_names)
558
+
559
+ def nq(self):
560
+ return self._kin_dyn.nq()
561
+
562
+ def nv(self):
563
+ return self._kin_dyn.nv()
564
+
565
+ def _get_robot_mass(self):
566
+
567
+ return self._kin_dyn.mass()
568
+
569
+ def _get_root_full_q_from_sol(self, node_idx=1):
570
+
571
+ root_q_full=self._ti.solution['q'][0:7, node_idx].reshape(1, 7).astype(self._dtype)
572
+
573
+ np.nan_to_num(root_q_full, nan=1e3, posinf=1e3, neginf=-1e3, copy=False)
574
+ np.clip(a=root_q_full, a_min=-1e3, a_max=1e3, out=root_q_full)
575
+
576
+ return root_q_full
577
+
578
+ def _get_full_q_from_sol(self, node_idx=1):
579
+
580
+ return self._ti.solution['q'][:, node_idx].reshape(1, -1).astype(self._dtype)
581
+
582
+ def _get_root_twist_from_sol(self, node_idx=1):
583
+ # provided in world frame
584
+ twist_base_local=self._get_v_from_sol()[0:6, node_idx].reshape(1, 6)
585
+ # if world_aligned:
586
+ # q_root_rhc = self._get_root_full_q_from_sol(node_idx=node_idx)[:, 0:4]
587
+ # r_base_rhc=Rotation.from_quat(q_root_rhc.flatten()).as_matrix()
588
+ # twist_base_local[:, 0:3] = r_base_rhc @ twist_base_local[0, 0:3]
589
+ # twist_base_local[:, 3:6] = r_base_rhc @ twist_base_local[0, 3:6]
590
+ return twist_base_local
591
+
592
+ def _get_root_a_from_sol(self, node_idx=0):
593
+ # provided in world frame
594
+ a_base_local=self._get_a_from_sol()[0:6, node_idx].reshape(1, 6)
595
+ # if world_aligned:
596
+ # q_root_rhc = self._get_root_full_q_from_sol(node_idx=node_idx)[:, 0:4]
597
+ # r_base_rhc=Rotation.from_quat(q_root_rhc.flatten()).as_matrix()
598
+ # a_base_local[:, 0:3] = r_base_rhc @ a_base_local[0, 0:3]
599
+ # a_base_local[:, 3:6] = r_base_rhc @ v[0, 3:6]
600
+ return a_base_local
601
+
602
+ def _get_jnt_q_from_sol(self, node_idx=0,
603
+ reduce: bool = True,
604
+ clamp: bool = True):
605
+
606
+ full_jnts_q=self._ti.solution['q'][7:, node_idx:node_idx+1].reshape(1,-1).astype(self._dtype)
607
+
608
+ np.nan_to_num(full_jnts_q, nan=1e3, posinf=1e3, neginf=-1e3, copy=False) # in place
609
+ np.clip(a=full_jnts_q, a_max=1e3, a_min=-1e3, out=full_jnts_q) # in place
610
+
611
+ if self._custom_opts["replace_continuous_joints"] or (not reduce):
612
+ if clamp:
613
+ return np.fmod(full_jnts_q, 2*np.pi)
614
+ else:
615
+ return full_jnts_q
616
+ else:
617
+ cos_sin=full_jnts_q[:,self._continuous_joints_idxs].reshape(-1,2)
618
+ # copy rev joint vals
619
+ self._jnts_q_reduced[:, self._rev_joints_idxs_red]=np.fmod(full_jnts_q[:, self._rev_joints_idxs], 2*np.pi).reshape(1, -1)
620
+ # and continuous
621
+ self._jnts_q_reduced[:, self._continuous_joints_idxs_red]=np.arctan2(cos_sin[:, 1], cos_sin[:, 0]).reshape(1,-1)
622
+ return self._jnts_q_reduced
623
+
624
+ def _get_jnt_v_from_sol(self, node_idx=1):
625
+
626
+ jnt_v_sol=self._get_v_from_sol()[6:, node_idx].reshape(1,
627
+ self._nv_jnts)
628
+ np.nan_to_num(jnt_v_sol, nan=1e5, posinf=1e5, neginf=-1e5, copy=False) # in place
629
+ # np.clip(a=jnt_v_sol, a_max=1e5, a_min=-1e5, out=jnt_v_sol) # in place
630
+
631
+ return jnt_v_sol
632
+
633
+ def _get_jnt_a_from_sol(self, node_idx=1):
634
+
635
+ return self._get_a_from_sol()[6:, node_idx].reshape(1,
636
+ self._nv_jnts)
637
+
638
+ def _get_jnt_eff_from_sol(self, node_idx=0):
639
+
640
+ efforts_on_node = self._ti.eval_efforts_on_node(node_idx=node_idx)
641
+
642
+ # if self._custom_opts["only_vel_wheels"]:
643
+
644
+ jnt_efforts=efforts_on_node[6:, 0]
645
+
646
+ if self._custom_opts["only_vel_wheels"] and self._wheels_idxs_v:
647
+ jnt_efforts[self._wheels_idxs_v]=0.0
648
+
649
+ return jnt_efforts.reshape(1,
650
+ self._nv_jnts).astype(self._dtype)
651
+
652
+ def _get_rhc_cost(self):
653
+
654
+ return self._ti.solution["opt_cost"]
655
+
656
+ def _get_rhc_constr_viol(self):
657
+
658
+ return self._ti.solution["residual_norm"]
659
+
660
+ def _get_rhc_nodes_cost(self):
661
+
662
+ cost = self._ti.solver_rti.getCostValOnNodes()
663
+ return cost.reshape((1, -1))
664
+
665
+ def _get_rhc_nodes_constr_viol(self):
666
+
667
+ constr_viol = self._ti.solver_rti.getConstrValOnNodes()
668
+ return constr_viol.reshape((1, -1))
669
+
670
+ def _get_rhc_niter_to_sol(self):
671
+
672
+ return self._ti.solution["n_iter2sol"]
673
+
674
+ def _set_ig(self):
675
+
676
+ shift_num = -1 # shift data by one node
677
+
678
+ x_opt = self._ti.solution['x_opt']
679
+ u_opt = self._ti.solution['u_opt']
680
+
681
+ # building ig for state
682
+ xig = np.roll(x_opt,
683
+ shift_num, axis=1) # rolling state sol.
684
+ for i in range(abs(shift_num)):
685
+ # state on last node is copied to the elements
686
+ # which are "lost" during the shift operation
687
+ xig[:, -1 - i] = x_opt[:, -1]
688
+ # building ig for inputs
689
+ uig = np.roll(u_opt,
690
+ shift_num, axis=1) # rolling state sol.
691
+ for i in range(abs(shift_num)):
692
+ # state on last node is copied to the elements
693
+ # which are "lost" during the shift operation
694
+ uig[:, -1 - i] = u_opt[:, -1]
695
+
696
+ # assigning ig
697
+ self._prb.getState().setInitialGuess(xig)
698
+ self._prb.getInput().setInitialGuess(uig)
699
+
700
+ return xig, uig
701
+
702
+ def _update_open_loop(self):
703
+
704
+ xig, _ = self._set_ig()
705
+
706
+ q_state, v_state, a_state=self._set_is_open()
707
+
708
+ # robot_state=xig[:, 0]
709
+ # # open loop update:
710
+ # self._prb.setInitialState(x0=robot_state) # (xig has been shifted, so node 0
711
+ # # is node 1 in the last opt solution)
712
+
713
+ return q_state, v_state, a_state
714
+
715
+ def _update_closed_loop(self):
716
+
717
+ # set initial guess for controller
718
+ xig, _ = self._set_ig()
719
+ # set initial state
720
+ q_state=None
721
+ v_state=None
722
+ a_state=None
723
+ if self._custom_opts["adaptive_is"]:
724
+ # adaptive closed loop
725
+ q_state, v_state, a_state=self._set_is_adaptive()
726
+ elif self._custom_opts["fully_closed"]:
727
+ q_state, v_state, a_state=self._set_is_full()
728
+ elif self._custom_opts["closed_partial"]:
729
+ q_state, v_state, a_state=self._set_is_partial()
730
+ else:
731
+ Journal.log(self.__class__.__name__,
732
+ "_update_closed_loop",
733
+ "Neither adaptive_is, fully_closed, or closed_partial.",
734
+ LogType.EXCEP,
735
+ throw_when_excep = False)
736
+ q_state, v_state, a_state=self._set_is()
737
+
738
+ return q_state, v_state, a_state
739
+
740
+ def _set_is_open(self):
741
+
742
+ # overriding states with rhc data
743
+ q_full_root=self._get_root_full_q_from_sol(node_idx=1).reshape(-1, 1)
744
+ q_jnts=self._get_jnt_q_from_sol(node_idx=1, reduce=False).reshape(-1, 1)
745
+
746
+ twist_root=self._get_root_twist_from_sol(node_idx=1).reshape(-1, 1)
747
+ v_jnts=self._get_jnt_v_from_sol(node_idx=1).reshape(-1, 1)
748
+
749
+ # rhc variables to be set
750
+ q=self._prb.getVariables("q") # .setBounds()
751
+ root_q_full_rhc=q[0:7] # root full q
752
+ jnts_q_rhc=q[7:] # jnts q
753
+ vel=self._prb.getVariables("v")
754
+ root_twist_rhc=vel[0:6] # lin v.
755
+ jnts_v_rhc=vel[6:] # jnts v
756
+
757
+ self.rhc_refs.set_alpha(alpha=1.0) # fully open
758
+
759
+ # close state on known quantities
760
+ root_q_full_rhc.setBounds(lb=q_full_root,
761
+ ub=q_full_root, nodes=0)
762
+ jnts_q_rhc.setBounds(lb=q_jnts,
763
+ ub=q_jnts, nodes=0)
764
+ root_twist_rhc.setBounds(lb=twist_root,
765
+ ub=twist_root, nodes=0)
766
+ jnts_v_rhc.setBounds(lb=v_jnts,
767
+ ub=v_jnts, nodes=0)
768
+
769
+ # return state used for feedback
770
+ q_state=np.concatenate((q_full_root, q_jnts),
771
+ axis=0)
772
+ v_state=np.concatenate((twist_root, v_jnts),
773
+ axis=0)
774
+
775
+ return (q_state, v_state, None)
776
+
777
+ def _set_is_full(self):
778
+
779
+ # measurements
780
+ q_full_root = self.robot_state.root_state.get(data_type="q_full", robot_idxs=self.controller_index_np).reshape(-1, 1)
781
+ v_root = self.robot_state.root_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
782
+ omega = self.robot_state.root_state.get(data_type="omega", robot_idxs=self.controller_index_np).reshape(-1, 1)
783
+ a_root = self.robot_state.root_state.get(data_type="a_full", robot_idxs=self.controller_index_np).reshape(-1, 1)
784
+
785
+ q_jnts = self.robot_state.jnts_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
786
+ v_jnts = self.robot_state.jnts_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
787
+ a_jnts = self.robot_state.jnts_state.get(data_type="a", robot_idxs=self.controller_index_np).reshape(-1, 1)
788
+
789
+ if (not len(self._continuous_joints)==0): # we need do expand some meas. rev jnts to So2
790
+ self._jnts_q_expanded[self._rev_joints_idxs, :]=q_jnts[self._rev_joints_idxs_red ,:]
791
+ self._jnts_q_expanded[self._continuous_joints_idxs_cos, :]=np.cos(q_jnts[self._continuous_joints_idxs_red, :]) # cos
792
+ self._jnts_q_expanded[self._continuous_joints_idxs_sin, :]=np.sin(q_jnts[self._continuous_joints_idxs_red, :]) # sin
793
+ q_jnts=self._jnts_q_expanded.reshape(-1,1)
794
+
795
+ # rhc variables to be set
796
+ q=self._prb.getVariables("q") # .setBounds()
797
+ root_full_q_rhc=q[0:7] # root p
798
+ jnts_q_rhc=q[7:] # jnts q
799
+ vel=self._prb.getVariables("v")
800
+ root_v_rhc=vel[0:3] # lin v.
801
+ root_omega_rhc=vel[3:6] # omega
802
+ jnts_v_rhc=vel[6:] # jnts v
803
+ acc=self._prb.getVariables("a")
804
+ lin_a_prb=acc[0:3] # lin acc
805
+
806
+ self.rhc_refs.set_alpha(alpha=0.0) # non-adaptive
807
+
808
+ root_full_q_rhc.setBounds(lb=q_full_root,
809
+ ub=q_full_root, nodes=0)
810
+ jnts_q_rhc.setBounds(lb=q_jnts,
811
+ ub=q_jnts, nodes=0)
812
+ root_v_rhc.setBounds(lb=v_root,
813
+ ub=v_root, nodes=0) # leaving lin v of the base free (estimated from constraints)
814
+ root_omega_rhc.setBounds(lb=omega,
815
+ ub=omega, nodes=0)
816
+ jnts_v_rhc.setBounds(lb=v_jnts,
817
+ ub=v_jnts, nodes=0)
818
+ if self._custom_opts["lin_a_feedback"]:
819
+ # write base lin 13793197 from meas
820
+ lin_a_prb.setBounds(lb=a_root[0:3, :],
821
+ ub=a_root[0:3, :],
822
+ nodes=0)
823
+
824
+ # return state used for feedback
825
+ q_state=np.concatenate((q_full_root, q_jnts),
826
+ axis=0)
827
+ v_state=np.concatenate((v_root, omega, v_jnts),
828
+ axis=0)
829
+ a_state=np.concatenate((a_root, a_jnts),
830
+ axis=0)
831
+
832
+ return (q_state, v_state, a_state)
833
+
834
+ def _set_is_partial(self):
835
+
836
+ # measurements
837
+ p_root = self.robot_state.root_state.get(data_type="p", robot_idxs=self.controller_index_np).reshape(-1, 1)
838
+ q_root = self.robot_state.root_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
839
+ v_root = self.robot_state.root_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
840
+ omega = self.robot_state.root_state.get(data_type="omega", robot_idxs=self.controller_index_np).reshape(-1, 1)
841
+ a_root = self.robot_state.root_state.get(data_type="a_full", robot_idxs=self.controller_index_np).reshape(-1, 1)
842
+
843
+ q_jnts = self.robot_state.jnts_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
844
+ v_jnts = self.robot_state.jnts_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
845
+ a_jnts = self.robot_state.jnts_state.get(data_type="a", robot_idxs=self.controller_index_np).reshape(-1, 1)
846
+
847
+ if (not len(self._continuous_joints)==0): # we need do expand some meas. rev jnts to So2
848
+ self._jnts_q_expanded[self._rev_joints_idxs, :]=q_jnts[self._rev_joints_idxs_red ,:]
849
+ self._jnts_q_expanded[self._continuous_joints_idxs_cos, :]=np.cos(q_jnts[self._continuous_joints_idxs_red, :]) # cos
850
+ self._jnts_q_expanded[self._continuous_joints_idxs_sin, :]=np.sin(q_jnts[self._continuous_joints_idxs_red, :]) # sin
851
+ q_jnts=self._jnts_q_expanded.reshape(-1,1)
852
+
853
+ # overriding states with rhc data (-> all overridden state are open looop)
854
+ root_q_full_from_rhc=self._get_root_full_q_from_sol(node_idx=1).reshape(-1, 1)
855
+ root_p_from_rhc=root_q_full_from_rhc[0:3, :]
856
+ p_root[:, :]=root_p_from_rhc # position is always open loop
857
+ if not self._custom_opts["estimate_v_root"]:
858
+ v_root[:, :]=self._get_root_twist_from_sol(node_idx=1)[:, 0:3].reshape(-1, 1)
859
+ # override v jnts with the ones from controller
860
+ if not self._custom_opts["use_jnt_v_feedback"]:
861
+ v_jnts[:, :]=self._get_jnt_v_from_sol(node_idx=1).reshape(-1, 1)
862
+ # v_jnts[:, :]=self._get_jnt_v_from_sol(node_idx=1).reshape(-1, 1)
863
+ # root_twist_from_rhc=self._get_root_twist_from_sol(node_idx=1)
864
+ # root_v_from_rhc=root_twist_from_rhc[:, 0:3].reshape(-1, 1)
865
+ # root_omega_from_rhc=root_twist_from_rhc[:, 3:6].reshape(-1, 1)
866
+ # jnt_q_from_rhc=self._get_jnt_q_from_sol(node_idx=1,reduce=False,clamp=False).reshape(-1, 1)
867
+ # jnt_v_from_rhc=self._get_jnt_v_from_sol(node_idx=1).reshape(-1, 1)
868
+
869
+ # rhc variables to be set
870
+ q=self._prb.getVariables("q") # .setBounds()
871
+ root_p_rhc=q[0:3] # root p
872
+ root_q_rhc=q[3:7] # root orientation
873
+ jnts_q_rhc=q[7:] # jnts q
874
+ vel=self._prb.getVariables("v")
875
+ root_v_rhc=vel[0:3] # lin v.
876
+ root_omega_rhc=vel[3:6] # omega
877
+ jnts_v_rhc=vel[6:] # jnts v
878
+ acc=self._prb.getVariables("a")
879
+ lin_a_prb=acc[0:3] # lin acc
880
+
881
+ self.rhc_refs.set_alpha(alpha=0.0) # non-adaptive
882
+
883
+ root_p_rhc.setBounds(lb=p_root,
884
+ ub=p_root, nodes=0)
885
+ root_q_rhc.setBounds(lb=q_root,
886
+ ub=q_root, nodes=0)
887
+ jnts_q_rhc.setBounds(lb=q_jnts,
888
+ ub=q_jnts, nodes=0)
889
+ if self._custom_opts["estimate_v_root"]:
890
+ root_v_rhc.setBounds(lb=-self._v_inf[0:3],
891
+ ub=self._v_inf[0:3], nodes=0) # leaving lin v of the base free (estimated from constraints)
892
+ else: # get it from controller
893
+ root_v_rhc.setBounds(lb=v_root,
894
+ ub=v_root, nodes=0)
895
+ root_omega_rhc.setBounds(lb=omega,
896
+ ub=omega, nodes=0)
897
+ jnts_v_rhc.setBounds(lb=v_jnts,
898
+ ub=v_jnts, nodes=0)
899
+ if self._custom_opts["lin_a_feedback"]:
900
+ # write base lin 13793197 from meas
901
+ lin_a_prb.setBounds(lb=a_root[0:3, :],
902
+ ub=a_root[0:3, :],
903
+ nodes=0)
904
+
905
+ # return state used for feedback
906
+ q_state=np.concatenate((p_root, q_root, q_jnts),
907
+ axis=0)
908
+ v_state=np.concatenate((v_root, omega, v_jnts),
909
+ axis=0)
910
+ a_state=np.concatenate((a_root, a_jnts),
911
+ axis=0)
912
+
913
+ return (q_state, v_state, a_state)
914
+
915
+ def _set_is_adaptive(self):
916
+
917
+ # measurements
918
+ p_root = self.robot_state.root_state.get(data_type="p", robot_idxs=self.controller_index_np).reshape(-1, 1)
919
+ q_root = self.robot_state.root_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
920
+ v_root = self.robot_state.root_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
921
+ omega = self.robot_state.root_state.get(data_type="omega", robot_idxs=self.controller_index_np).reshape(-1, 1)
922
+ a_root = self.robot_state.root_state.get(data_type="a_full", robot_idxs=self.controller_index_np).reshape(-1, 1)
923
+
924
+ q_jnts = self.robot_state.jnts_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
925
+ v_jnts = self.robot_state.jnts_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
926
+ a_jnts = self.robot_state.jnts_state.get(data_type="a", robot_idxs=self.controller_index_np).reshape(-1, 1)
927
+
928
+ # rhc variables to be set
929
+ q=self._prb.getVariables("q") # .setBounds()
930
+ root_p_rhc=q[0:3] # root p
931
+ root_q_rhc=q[3:7] # root orientation
932
+ jnts_q_rhc=q[7:] # jnts q
933
+ vel=self._prb.getVariables("v")
934
+ root_v_rhc=vel[0:3] # lin v.
935
+ root_omega_rhc=vel[3:6] # omega
936
+ jnts_v_rhc=vel[6:] # jnts v
937
+ acc=self._prb.getVariables("a")
938
+ lin_a_prb=acc[0:3] # lin acc
939
+
940
+ # getting prediction defects
941
+ root_q_delta=self.rhc_pred_delta.root_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
942
+ jnt_q_delta=self.rhc_pred_delta.jnts_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
943
+ jnt_v_delta=self.rhc_pred_delta.jnts_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
944
+ v_root_delta = self.rhc_pred_delta.root_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
945
+ omega_root_delta = self.rhc_pred_delta.root_state.get(data_type="omega", robot_idxs=self.controller_index_np).reshape(-1, 1)
946
+ a_root_delta = self.rhc_pred_delta.root_state.get(data_type="a_full", robot_idxs=self.controller_index_np).reshape(-1, 1)
947
+
948
+ # close state on known quantities, estimate some (e.g. lin vel) and
949
+ # open loop if thing start to explode
950
+ alpha_now=1.0
951
+ delta=0.0
952
+ if self._custom_opts["alpha_from_outside"]:
953
+ alpha_now=self.rhc_refs.get_alpha()
954
+ else: # "autotuned" alpha
955
+ if self._custom_opts["estimate_v_root"]: # we copmute delta based on jnt v (since we use meas.)
956
+ delta=np.max(np.abs(jnt_v_delta))
957
+ else:
958
+ delta=np.max(np.abs(omega_root_delta))
959
+ # fail_idx=self._get_failure_index()
960
+ # fail_idx=self._get_explosion_idx()/self._fail_idx_thresh
961
+ alpha_now=(np.tanh(2*self._alpha_half*(delta-self._alpha_half))+1)/2.0
962
+
963
+ bound_relaxation=-np.log(1-alpha_now+1e-16) # [~0, ~inf] if alpha_now [0, 1]
964
+ self.rhc_refs.set_alpha(alpha=alpha_now) # also writes on shared mem for db
965
+ self.rhc_refs.set_bound_relax(bound_relax=bound_relaxation) # also writes on shared mem for db
966
+
967
+ self._alphas_q_root[:]=alpha_now # for now single alpha for everything
968
+ self._alphas_q_jnts[:]=alpha_now
969
+ self._alphas_twist_root[:]=alpha_now
970
+ self._alphas_v_jnts[:]=alpha_now
971
+ self._alphas_a[:]=alpha_now
972
+ if not self._custom_opts["estimate_v_root"]:
973
+ self._alphas_twist_root[0:3]=1.0 # open
974
+ self._alphas_v_jnts[:]=1.0 # open
975
+
976
+ # position is always open loop
977
+ root_q_full_from_rhc=self._get_root_full_q_from_sol(node_idx=1).reshape(-1, 1)
978
+ root_p_from_rhc=root_q_full_from_rhc[0:3, :]
979
+ p_root[:, :]=root_p_from_rhc
980
+
981
+ # expaning meas q if continuous joints
982
+ if (not len(self._continuous_joints)==0): # we need do expand some meas. rev jnts to So2
983
+ self._jnts_q_expanded[self._rev_joints_idxs, :]=q_jnts[self._rev_joints_idxs_red ,:]
984
+ self._jnts_q_expanded[self._continuous_joints_idxs_cos, :]=np.cos(q_jnts[self._continuous_joints_idxs_red, :]) # cos
985
+ self._jnts_q_expanded[self._continuous_joints_idxs_sin, :]=np.sin(q_jnts[self._continuous_joints_idxs_red, :]) # sin
986
+
987
+ # continous joints position is always open loop, but we need a delta vector of matching dimension
988
+ q_jnts_from_rhc=self._get_jnt_q_from_sol(node_idx=1).reshape(-1, 1)
989
+
990
+ self._jnts_q_delta_expanded[self._rev_joints_idxs, :]=jnt_q_delta[self._rev_joints_idxs_red ,:]
991
+
992
+ self._jnts_q_delta_expanded[self._continuous_joints_idxs_cos, :]=\
993
+ np.cos(q_jnts_from_rhc[self._continuous_joints_idxs_red, :]) - \
994
+ np.cos(q_jnts[self._continuous_joints_idxs_red, :])
995
+ self._jnts_q_delta_expanded[self._continuous_joints_idxs_sin, :]=\
996
+ np.sin(q_jnts_from_rhc[self._continuous_joints_idxs_red, :]) - \
997
+ np.sin(q_jnts[self._continuous_joints_idxs_red, :])
998
+
999
+ q_jnts=self._jnts_q_expanded.reshape(-1,1) # overriting with expanded jnts
1000
+ jnt_q_delta=self._jnts_q_delta_expanded.reshape(-1, 1) # overriting with expanded jnts
1001
+
1002
+ self._alphas_q_jnts[self._continuous_joints_idxs_cos, :]=1.0 # open loop
1003
+ self._alphas_q_jnts[self._continuous_joints_idxs_sin, :]=1.0 # open loop
1004
+
1005
+ # self._alphas_v_jnts[self._continuous_joints_idxs_red, :]=0.0 # open loop
1006
+
1007
+ root_p_rhc.setBounds(lb=p_root,
1008
+ ub=p_root, nodes=0)
1009
+ root_q_rhc.setBounds(lb=q_root+self._alphas_q_root[3:7]*root_q_delta,
1010
+ ub=q_root+self._alphas_q_root[3:7]*root_q_delta, nodes=0)
1011
+ jnts_q_rhc.setBounds(lb=q_jnts+self._alphas_q_jnts*jnt_q_delta,
1012
+ ub=q_jnts+self._alphas_q_jnts*jnt_q_delta, nodes=0)
1013
+ if self._custom_opts["estimate_v_root"]:
1014
+ root_v_rhc.setBounds(lb=-self._v_inf[0:3],
1015
+ ub=self._v_inf[0:3], nodes=0)
1016
+ else:
1017
+ root_v_rhc.setBounds(lb=v_root+self._alphas_twist_root[0:3, :]*v_root_delta,
1018
+ ub=v_root+self._alphas_twist_root[0:3, :]*v_root_delta, nodes=0)
1019
+ root_omega_rhc.setBounds(lb=omega+self._alphas_twist_root[3:6, :]*omega_root_delta,
1020
+ ub=omega+self._alphas_twist_root[3:6, :]*omega_root_delta, nodes=0)
1021
+ jnts_v_rhc.setBounds(lb=v_jnts+self._alphas_v_jnts*jnt_v_delta,
1022
+ ub=v_jnts+self._alphas_v_jnts*jnt_v_delta, nodes=0)
1023
+ if self._custom_opts["lin_a_feedback"]:
1024
+ lin_a_prb.setBounds(lb=a_root[0:3, :]+self._alphas_a[0:3]*a_root_delta[0:3, :],
1025
+ ub=a_root[0:3, :]+self._alphas_a[0:3]*a_root_delta[0:3, :],
1026
+ nodes=0)
1027
+
1028
+ # return state used for feedback
1029
+ q_state=np.concatenate((p_root, q_root, q_jnts),
1030
+ axis=0)
1031
+ v_state=np.concatenate((v_root, omega, v_jnts),
1032
+ axis=0)
1033
+ a_state=np.concatenate((a_root, a_jnts),
1034
+ axis=0)
1035
+
1036
+ return (q_state, v_state, a_state)
1037
+
1038
+ def _solve(self):
1039
+
1040
+ if self._debug:
1041
+ return self._db_solve()
1042
+ else:
1043
+ return self._min_solve()
1044
+
1045
+ def _min_solve(self):
1046
+ # minimal solve version -> no debug
1047
+ robot_qstate=None
1048
+ robot_vstate=None
1049
+ robot_astate=None
1050
+ if self._open_loop:
1051
+ robot_qstate, robot_vstate, robot_astate = self._update_open_loop() # updates the TO ig and
1052
+ # initial conditions using data from the solution itself
1053
+ else:
1054
+ robot_qstate, robot_vstate, robot_astate = self._update_closed_loop() # updates the TO ig and
1055
+ # initial conditions using robot measurements
1056
+
1057
+ self._pm.shift() # shifts phases of one dt
1058
+ if self._refs_in_hor_frame:
1059
+ # q_base=self.robot_state.root_state.get(data_type="q",
1060
+ # robot_idxs=self.controller_index).reshape(-1, 1)
1061
+ # q_full=self._get_full_q_from_sol(node_idx=1).reshape(-1, 1)
1062
+ # using internal base pose from rhc. in case of closed loop, it will be the meas state
1063
+ force_norm=None
1064
+ if self._custom_opts["use_force_feedback"]:
1065
+ contact_forces=self.robot_state.contact_wrenches.get(data_type="f",
1066
+ robot_idxs=self.controller_index_np,
1067
+ contact_name=None).reshape(self.n_contacts,3)
1068
+ force_norm=np.linalg.norm(contact_forces, axis=1)
1069
+ self.rhc_refs.step(qstate=robot_qstate, vstate=robot_vstate,
1070
+ force_norm=force_norm)
1071
+ else:
1072
+ self.rhc_refs.step()
1073
+
1074
+ try:
1075
+ converged = self._ti.rti() # solves the problem
1076
+ self.sol_counter = self.sol_counter + 1
1077
+ return not self._check_rhc_failure()
1078
+ except Exception as e: # fail in case of exceptions
1079
+ return False
1080
+
1081
+ def _db_solve(self):
1082
+
1083
+ self._timer_start = time.perf_counter()
1084
+
1085
+ robot_qstate=None
1086
+ robot_vstate=None
1087
+ robot_astate=None
1088
+ if self._open_loop:
1089
+ robot_qstate, robot_vstate, robot_astate = self._update_open_loop() # updates the TO ig and
1090
+ # initial conditions using data from the solution itself
1091
+ else:
1092
+ robot_qstate, robot_vstate, robot_astate = self._update_closed_loop() # updates the TO ig and
1093
+ # initial conditions using robot measurements
1094
+
1095
+ self._prb_update_time = time.perf_counter()
1096
+ self._pm.shift() # shifts phases of one dt
1097
+ self._phase_shift_time = time.perf_counter()
1098
+
1099
+ if self._refs_in_hor_frame:
1100
+ # q_base=self.robot_state.root_state.get(data_type="q",
1101
+ # robot_idxs=self.controller_index).reshape(-1, 1)
1102
+ # q_full=self._get_full_q_from_sol(node_idx=1).reshape(-1, 1)
1103
+ # using internal base pose from rhc. in case of closed loop, it will be the meas state
1104
+ force_norm=None
1105
+ if self._custom_opts["use_force_feedback"]:
1106
+ contact_forces=self.robot_state.contact_wrenches.get(data_type="f",
1107
+ robot_idxs=self.controller_index_np,
1108
+ contact_name=None).reshape(self.n_contacts,3)
1109
+ force_norm=np.linalg.norm(contact_forces, axis=1)
1110
+ self.rhc_refs.step(qstate=robot_qstate, vstate=robot_vstate,
1111
+ force_norm=force_norm)
1112
+ else:
1113
+ self.rhc_refs.step()
1114
+
1115
+ self._task_ref_update_time = time.perf_counter()
1116
+
1117
+ try:
1118
+ converged = self._ti.rti() # solves the problem
1119
+ self._rti_time = time.perf_counter()
1120
+ self.sol_counter = self.sol_counter + 1
1121
+ self._update_db_data()
1122
+ return not self._check_rhc_failure()
1123
+ except Exception as e: # fail in case of exceptions
1124
+ if self._verbose:
1125
+ exception = f"Rti() for controller {self.controller_index} failed" + \
1126
+ f" with exception{type(e).__name__}"
1127
+ Journal.log(self.__class__.__name__,
1128
+ "solve",
1129
+ exception,
1130
+ LogType.EXCEP,
1131
+ throw_when_excep = False)
1132
+ self._update_db_data()
1133
+ return False
1134
+
1135
+ def _get_fail_idx(self):
1136
+
1137
+ self._explosion_idx_buffer[:, self._expl_idx_buffer_counter]=self._get_explosion_idx()
1138
+ self._expl_idx_buffer_counter+=1
1139
+ self._expl_idx_counter+=1
1140
+ if self._expl_idx_counter%self._expl_idx_window_size==0:
1141
+ self._expl_idx_buffer_counter=0 # restart from 0
1142
+
1143
+ running_avrg=np.mean(self._explosion_idx_buffer).item()
1144
+
1145
+ return running_avrg
1146
+
1147
+ def _get_explosion_idx(self):
1148
+ explosion_index = self._get_rhc_constr_viol() + self._get_rhc_cost()*self._fail_idx_scale
1149
+ return explosion_index
1150
+
1151
+ def _update_db_data(self):
1152
+
1153
+ self._profiling_data_dict["problem_update_dt"] = self._prb_update_time - self._timer_start
1154
+ self._profiling_data_dict["phases_shift_dt"] = self._phase_shift_time - self._prb_update_time
1155
+ self._profiling_data_dict["task_ref_update"] = self._task_ref_update_time - self._phase_shift_time
1156
+ self._profiling_data_dict["rti_solve_dt"] = self._rti_time - self._task_ref_update_time
1157
+ self.rhc_costs.update(self._ti.solver_rti.getCostsValues())
1158
+ self.rhc_constr.update(self._ti.solver_rti.getConstraintsValues())
1159
+
1160
+ def _reset(self):
1161
+
1162
+ # reset task interface (ig, solvers, etc..) +
1163
+ # phase manager and sets bootstap as solution
1164
+ self._gm.reset()
1165
+ self._explosion_idx_buffer[:, :]=self._get_explosion_idx() # reset with data from reset solution
1166
+ self._expl_idx_counter=0.0
1167
+ self._expl_idx_buffer_counter=0
1168
+
1169
+ def _get_cost_data(self):
1170
+
1171
+ cost_dict = self._ti.solver_rti.getCostsValues()
1172
+ cost_names = list(cost_dict.keys())
1173
+ cost_dims = [1] * len(cost_names) # costs are always scalar
1174
+ return cost_names, cost_dims
1175
+
1176
+ def _get_constr_data(self):
1177
+
1178
+ constr_dict = self._ti.solver_rti.getConstraintsValues()
1179
+ constr_names = list(constr_dict.keys())
1180
+ constr_dims = [-1] * len(constr_names)
1181
+ i = 0
1182
+ for constr in constr_dict:
1183
+ constr_val = constr_dict[constr]
1184
+ constr_shape = constr_val.shape
1185
+ constr_dims[i] = constr_shape[0]
1186
+ i+=1
1187
+ return constr_names, constr_dims
1188
+
1189
+ def _get_q_from_sol(self):
1190
+ full_q=self._ti.solution['q'].astype(self._dtype)
1191
+ if self._custom_opts["replace_continuous_joints"]:
1192
+ return full_q
1193
+ else:
1194
+ cont_jnts=full_q[self._continuous_joints_idxs_qfull, :]
1195
+ cos=cont_jnts[::2, :]
1196
+ sin=cont_jnts[1::2, :]
1197
+ # copy root
1198
+ self._full_q_reduced[0:7, :]=full_q[0:7, :]
1199
+ # copy rev joint vals
1200
+ self._full_q_reduced[self._rev_joints_idxs_red_qfull, :]=full_q[self._rev_joints_idxs_qfull, :]
1201
+ # and continuous
1202
+ angle=np.arctan2(sin, cos)
1203
+ self._full_q_reduced[self._continuous_joints_idxs_red_qfull, :]=angle
1204
+ return self._full_q_reduced
1205
+
1206
+ def _get_v_from_sol(self):
1207
+ return self._ti.solution['v'].astype(self._dtype)
1208
+
1209
+ def _get_a_from_sol(self):
1210
+ return self._ti.solution['a'].astype(self._dtype)
1211
+
1212
+ def _get_a_dot_from_sol(self):
1213
+ return None
1214
+
1215
+ def _get_f_from_sol(self):
1216
+ # to be overridden by child class
1217
+ contact_names =self._get_contacts() # we use controller-side names
1218
+ try:
1219
+ data=[]
1220
+ for key in contact_names:
1221
+ contact_f=self._ti.solution["f_" + key].astype(self._dtype)
1222
+ np.nan_to_num(contact_f, nan=1e6, posinf=1e6, neginf=-1e6, copy=False)
1223
+ np.clip(a=contact_f, a_max=1e6, a_min=-1e6, out=contact_f)
1224
+ data.append(contact_f)
1225
+ return np.concatenate(data, axis=0)
1226
+ except:
1227
+ return None
1228
+
1229
+ def _get_f_dot_from_sol(self):
1230
+ # to be overridden by child class
1231
+ return None
1232
+
1233
+ def _get_eff_from_sol(self):
1234
+ # to be overridden by child class
1235
+ return None
1236
+
1237
+ def _get_cost_from_sol(self,
1238
+ cost_name: str):
1239
+ return self.rhc_costs[cost_name]
1240
+
1241
+ def _get_constr_from_sol(self,
1242
+ constr_name: str):
1243
+ return self.rhc_constr[constr_name]
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/hybrid_quad_rhc_refs.py ADDED
@@ -0,0 +1,381 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from aug_mpc.controllers.rhc.horizon_based.gait_manager import GaitManager
2
+ from aug_mpc.controllers.rhc.horizon_based.utils.math_utils import hor2w_frame
3
+
4
+ from mpc_hive.utilities.shared_data.rhc_data import RhcRefs
5
+
6
+ from EigenIPC.PyEigenIPC import VLevel
7
+ from EigenIPC.PyEigenIPC import LogType
8
+ from EigenIPC.PyEigenIPC import Journal
9
+
10
+ from typing import Union
11
+
12
+ import numpy as np
13
+
14
+ class HybridQuadRhcRefs(RhcRefs):
15
+
16
+ def __init__(self,
17
+ gait_manager: GaitManager,
18
+ robot_index_shm: int,
19
+ robot_index_view: int,
20
+ namespace: str, # namespace used for shared mem
21
+ verbose: bool = True,
22
+ vlevel: bool = VLevel.V2,
23
+ safe: bool = True,
24
+ use_force_feedback: bool = False,
25
+ optimize_mem: bool = False):
26
+
27
+ self.robot_index = robot_index_shm
28
+ self.robot_index_view = robot_index_view
29
+ self.robot_index_np_view = np.array(self.robot_index_view)
30
+
31
+ self._step_idx = 0
32
+ self._print_frequency = 100
33
+
34
+ self._verbose = verbose
35
+
36
+ self._use_force_feedback=use_force_feedback
37
+
38
+ if optimize_mem:
39
+ super().__init__(
40
+ is_server=False,
41
+ with_gpu_mirror=False,
42
+ namespace=namespace,
43
+ safe=safe,
44
+ verbose=verbose,
45
+ vlevel=vlevel,
46
+ optimize_mem=optimize_mem,
47
+ n_robots=1, # we just need the row corresponding to this controller
48
+ n_jnts=None, # got from server
49
+ n_contacts=None # got from server
50
+ )
51
+ else:
52
+ super().__init__(
53
+ is_server=False,
54
+ with_gpu_mirror=False,
55
+ namespace=namespace,
56
+ safe=safe,
57
+ verbose=verbose,
58
+ vlevel=vlevel)
59
+
60
+ if not isinstance(gait_manager, GaitManager):
61
+ exception = f"Provided gait_manager argument should be of GaitManager type!"
62
+ Journal.log(self.__class__.__name__,
63
+ "__init__",
64
+ exception,
65
+ LogType.EXCEP,
66
+ throw_when_excep = True)
67
+
68
+ self.gait_manager = gait_manager
69
+
70
+ self.timeline_names = self.gait_manager.timeline_names
71
+
72
+ # task interfaces from horizon for setting commands to rhc
73
+ self._get_tasks()
74
+
75
+ self._p_ref_default=np.zeros((1, 3))
76
+ self._q_ref_default=np.zeros((1, 4))
77
+ self._q_ref_default[0, 0]=1
78
+
79
+ def _get_tasks(self):
80
+ # can be overridden by child
81
+ # cartesian tasks are in LOCAL_WORLD_ALIGNED (frame centered at distal link, oriented as WORLD)
82
+ self.base_lin_velxy = self.gait_manager.task_interface.getTask('base_lin_velxy')
83
+ self.base_lin_velz = self.gait_manager.task_interface.getTask('base_lin_velz')
84
+ self.base_omega = self.gait_manager.task_interface.getTask('base_omega')
85
+ self.base_height = self.gait_manager.task_interface.getTask('base_height')
86
+
87
+ def run(self):
88
+
89
+ super().run()
90
+ if not (self.robot_index < self.rob_refs.n_robots()):
91
+ exception = f"Provided \(0-based\) robot index {self.robot_index} exceeds number of " + \
92
+ " available robots {self.rob_refs.n_robots()}."
93
+ Journal.log(self.__class__.__name__,
94
+ "run",
95
+ exception,
96
+ LogType.EXCEP,
97
+ throw_when_excep = True)
98
+ contact_names = list(self.gait_manager.task_interface.model.cmap.keys())
99
+ if not (self.n_contacts() == len(contact_names)):
100
+ exception = f"N of contacts within problem {len(contact_names)} does not match n of contacts {self.n_contacts()}"
101
+ Journal.log(self.__class__.__name__,
102
+ "run",
103
+ exception,
104
+ LogType.EXCEP,
105
+ throw_when_excep = True)
106
+
107
+ # set some defaults from gait manager
108
+ for i in range(self.n_contacts()):
109
+ self.flight_settings_req.set(data=self.gait_manager.get_flight_duration(contact_name=contact_names[i]),
110
+ data_type="len_remain",
111
+ robot_idxs=self.robot_index_np_view,
112
+ contact_idx=i)
113
+ self.flight_settings_req.set(data=self.gait_manager.get_step_apexdh(contact_name=contact_names[i]),
114
+ data_type="apex_dpos",
115
+ robot_idxs=self.robot_index_np_view,
116
+ contact_idx=i)
117
+ self.flight_settings_req.set(data=self.gait_manager.get_step_enddh(contact_name=contact_names[i]),
118
+ data_type="end_dpos",
119
+ robot_idxs=self.robot_index_np_view,
120
+ contact_idx=i)
121
+
122
+ self.flight_settings_req.synch_retry(row_index=self.robot_index,
123
+ col_index=0,
124
+ row_index_view=self.robot_index_view,
125
+ n_rows=1,
126
+ n_cols=self.flight_settings_req.n_cols,
127
+ read=False)
128
+
129
+ def step(self, qstate: np.ndarray = None,
130
+ vstate: np.ndarray = None,
131
+ force_norm: np.ndarray = None):
132
+
133
+ if self.is_running():
134
+
135
+ # updates robot refs from shared mem
136
+ self.rob_refs.synch_from_shared_mem(robot_idx=self.robot_index, robot_idx_view=self.robot_index_view)
137
+ self.phase_id.synch_all(read=True, retry=True,
138
+ row_index=self.robot_index,
139
+ row_index_view=self.robot_index_view)
140
+ self.contact_flags.synch_all(read=True, retry=True,
141
+ row_index=self.robot_index,
142
+ row_index_view=self.robot_index_view)
143
+ self.flight_settings_req.synch_all(read=True, retry=True,
144
+ row_index=self.robot_index,
145
+ row_index_view=self.robot_index_view)
146
+ self._set_contact_phases(q_full=qstate)
147
+
148
+ # updated internal references with latest available ones
149
+ q_base=qstate[3:7,0:1] # quaternion
150
+ self._apply_refs_to_tasks(q_base=q_base)
151
+
152
+ # if self._use_force_feedback:
153
+ # self._set_force_feedback(force_norm=force_norm)
154
+
155
+ self._step_idx +=1
156
+
157
+ else:
158
+ exception = f"{self.__class__.__name__} is not running"
159
+ Journal.log(self.__class__.__name__,
160
+ "step",
161
+ exception,
162
+ LogType.EXCEP,
163
+ throw_when_excep = True)
164
+
165
+ def _set_contact_phases(self,
166
+ q_full: np.ndarray):
167
+
168
+ # phase_id = self.phase_id.read_retry(row_index=self.robot_index,
169
+ # col_index=0)[0]
170
+
171
+ contact_flags_refs = self.contact_flags.get_numpy_mirror()[self.robot_index_np_view, :]
172
+ target_n_limbs_in_contact=np.sum(contact_flags_refs).item()
173
+ if target_n_limbs_in_contact==0:
174
+ target_n_limbs_in_contact=4
175
+
176
+ is_contact = contact_flags_refs.flatten().tolist()
177
+ n_contacts=len(is_contact)
178
+
179
+ for i in range(n_contacts): # loop through contact timelines
180
+ timeline_name = self.timeline_names[i]
181
+
182
+ self.gait_manager.set_f_reg(contact_name=timeline_name,
183
+ scale=target_n_limbs_in_contact)
184
+
185
+ if is_contact[i]==False: # release contact
186
+
187
+ # flight parameters requests are set only when inserting a flight phase
188
+ len_req_now=int(self.flight_settings_req.get(data_type="len_remain",
189
+ robot_idxs=self.robot_index_np_view,
190
+ contact_idx=i).item())
191
+ apex_req_now=self.flight_settings_req.get(data_type="apex_dpos",
192
+ robot_idxs=self.robot_index_np_view,
193
+ contact_idx=i).item()
194
+ end_req_now=self.flight_settings_req.get(data_type="end_dpos",
195
+ robot_idxs=self.robot_index_np_view,
196
+ contact_idx=i).item()
197
+ landing_dx_req_now=self.flight_settings_req.get(data_type="land_dx",
198
+ robot_idxs=self.robot_index_np_view,
199
+ contact_idx=i).item()
200
+ landing_dy_req_now=self.flight_settings_req.get(data_type="land_dy",
201
+ robot_idxs=self.robot_index_np_view,
202
+ contact_idx=i).item()
203
+
204
+ # set flight phase properties depending on last value on shared memory
205
+ self.gait_manager.set_flight_duration(contact_name=timeline_name,
206
+ val=len_req_now)
207
+ self.gait_manager.set_step_apexdh(contact_name=timeline_name,
208
+ val=apex_req_now)
209
+ self.gait_manager.set_step_enddh(contact_name=timeline_name,
210
+ val=end_req_now)
211
+ self.gait_manager.set_step_landing_dx(contact_name=timeline_name,
212
+ val=landing_dx_req_now)
213
+ self.gait_manager.set_step_landing_dy(contact_name=timeline_name,
214
+ val=landing_dy_req_now)
215
+ # insert flight phase over the horizon
216
+ self.gait_manager.add_flight(contact_name=timeline_name,
217
+ robot_q=q_full)
218
+
219
+ else: # contact phase
220
+ self.gait_manager.add_stand(contact_name=timeline_name)
221
+
222
+ at_least_one_flight=self.gait_manager.update_flight_info(timeline_name)
223
+ # flight_info=self.gait_manager.get_flight_info(timeline_name)
224
+
225
+ self.gait_manager.check_horizon_full(timeline_name=timeline_name)
226
+
227
+ # write flight info to shared mem for all contacts in one shot (we follow same order as in flight_info shm)
228
+ all_flight_info=self.gait_manager.get_flight_info_all()
229
+ flight_info_shared=self.flight_info.get_numpy_mirror()
230
+ flight_info_shared[self.robot_index_np_view, :]=all_flight_info
231
+ self.flight_info.synch_retry(row_index=self.robot_index,
232
+ col_index=0,
233
+ row_index_view=self.robot_index_np_view,
234
+ n_rows=1, n_cols=self.flight_info.n_cols,
235
+ read=False)
236
+
237
+ self.gait_manager.update()
238
+
239
+ def _apply_refs_to_tasks(self, q_base = None):
240
+ # overrides parent
241
+ if q_base is not None: # rhc refs are assumed to be specified in the so called "horizontal"
242
+ # frame, i.e. a vertical frame, with the x axis aligned with the projection of the base x axis
243
+ # onto the plane
244
+ root_pose = self.rob_refs.root_state.get(data_type = "q_full",
245
+ robot_idxs=self.robot_index_np_view).reshape(-1, 1) # this should also be
246
+ # rotated into the horizontal frame (however, for now only the z componet is used, so it's ok)
247
+
248
+ root_twist_ref = self.rob_refs.root_state.get(data_type="twist",
249
+ robot_idxs=self.robot_index_np_view).reshape(-1, 1)
250
+
251
+ root_twist_ref_h = root_twist_ref.copy()
252
+
253
+ hor2w_frame(root_twist_ref, q_base, root_twist_ref_h) # horizon works in local world aligned frame
254
+
255
+ if self.base_lin_velxy is not None:
256
+ self.base_lin_velxy.setRef(root_twist_ref_h[0:2, :])
257
+ if self.base_omega is not None:
258
+ self.base_omega.setRef(root_twist_ref_h[3:, :])
259
+ if self.base_lin_velz is not None:
260
+ self.base_lin_velz.setRef(root_twist_ref_h[2:3, :])
261
+ if self.base_height is not None:
262
+ self.base_height.setRef(root_pose)
263
+ else:
264
+ root_pose = self.rob_refs.root_state.get(data_type = "q_full",
265
+ robot_idxs=self.robot_index_np_view).reshape(-1, 1)
266
+ root_twist_ref = self.rob_refs.root_state.get(data_type="twist",
267
+ robot_idxs=self.robot_index_np_view).reshape(-1, 1)
268
+
269
+ if self.base_lin_velxy is not None:
270
+ self.base_lin_velxy.setRef(root_twist_ref[0:2, :])
271
+ if self.base_omega is not None:
272
+ self.base_omega.setRef(root_twist_ref[3:, :])
273
+ if self.base_lin_velz is not None:
274
+ self.base_lin_velz.setRef(root_twist_ref[2:3, :])
275
+ if self.base_height is not None:
276
+ self.base_height.setRef(root_pose)
277
+
278
+ # def _set_force_feedback(self,
279
+ # force_norm: np.ndarray = None):
280
+
281
+ # is_contact=force_norm>1.0
282
+
283
+ # for i in range(len(is_contact)):
284
+ # timeline_name = self._timeline_names[i]
285
+ # self.gait_manager.set_force_feedback(timeline_name=timeline_name,
286
+ # force_norm=force_norm[i])
287
+
288
+ # if not is_contact[i]:
289
+
290
+
291
+ def set_default_refs(self,
292
+ p_ref: np.ndarray,
293
+ q_ref: np.ndarray):
294
+
295
+ self._p_ref_default[:, :]=p_ref
296
+ self._q_ref_default[:, :]=q_ref
297
+
298
+ def set_alpha(self, alpha:float):
299
+ # set provided value
300
+ alpha_shared=self.alpha.get_numpy_mirror()
301
+ alpha_shared[self.robot_index_np_view, :] = alpha
302
+ self.alpha.synch_retry(row_index=self.robot_index, col_index=0,
303
+ row_index_view=self.robot_index_view,
304
+ n_rows=1, n_cols=self.alpha.n_cols,
305
+ read=False)
306
+
307
+ def get_alpha(self):
308
+ self.alpha.synch_retry(row_index=self.robot_index, col_index=0,
309
+ row_index_view=self.robot_index_view,
310
+ n_rows=1, n_cols=self.alpha.n_cols,
311
+ read=True)
312
+ alpha=self.alpha.get_numpy_mirror()[self.robot_index_np_view, :].item()
313
+ return alpha
314
+
315
+ def set_bound_relax(self, bound_relax:float):
316
+ # set provided value
317
+ bound_rel_shared=self.bound_rel.get_numpy_mirror()
318
+ bound_rel_shared[self.robot_index_np_view, :] = bound_relax
319
+ self.bound_rel.synch_retry(row_index=self.robot_index, col_index=0, n_rows=1,
320
+ row_index_view=self.robot_index_view,
321
+ n_cols=self.alpha.n_cols,
322
+ read=False)
323
+
324
+ def reset(self):
325
+
326
+ if self.is_running():
327
+
328
+ # resets shared mem
329
+ contact_flags_current = self.contact_flags.get_numpy_mirror()
330
+ phase_id_current = self.phase_id.get_numpy_mirror()
331
+ contact_flags_current[self.robot_index_np_view, :] = np.full((1, self.n_contacts()), dtype=np.bool_, fill_value=True)
332
+ phase_id_current[self.robot_index_np_view, :] = -1 # defaults to custom phase id
333
+
334
+ contact_pos_current=self.rob_refs.contact_pos.get_numpy_mirror()
335
+ contact_pos_current[self.robot_index_np_view, :] = 0.0
336
+
337
+ flight_info_current=self.flight_info.get_numpy_mirror()
338
+ flight_info_current[self.robot_index_np_view, :] = 0.0
339
+
340
+ alpha=self.alpha.get_numpy_mirror()
341
+ alpha[self.robot_index_np_view, :] = 0.0
342
+
343
+ self.rob_refs.root_state.set(data_type="p", data=self._p_ref_default, robot_idxs=self.robot_index_np_view)
344
+ self.rob_refs.root_state.set(data_type="q", data=self._q_ref_default, robot_idxs=self.robot_index_np_view)
345
+ self.rob_refs.root_state.set(data_type="twist", data=np.zeros((1, 6)), robot_idxs=self.robot_index_np_view)
346
+
347
+ self.contact_flags.synch_retry(row_index=self.robot_index, col_index=0,
348
+ row_index_view=self.robot_index_view,
349
+ n_rows=1, n_cols=self.contact_flags.n_cols,
350
+ read=False)
351
+ self.phase_id.synch_retry(row_index=self.robot_index, col_index=0,
352
+ row_index_view=self.robot_index_view,
353
+ n_rows=1, n_cols=self.phase_id.n_cols,
354
+ read=False)
355
+ self.rob_refs.root_state.synch_retry(row_index=self.robot_index, col_index=0,
356
+ row_index_view=self.robot_index_view,
357
+ n_rows=1, n_cols=self.rob_refs.root_state.n_cols,
358
+ read=False)
359
+
360
+ self.rob_refs.contact_pos.synch_retry(row_index=self.robot_index, col_index=0,
361
+ row_index_view=self.robot_index_view,
362
+ n_rows=1, n_cols=self.rob_refs.contact_pos.n_cols,
363
+ read=False)
364
+
365
+ self.flight_info.synch_retry(row_index=self.robot_index,
366
+ col_index=0,
367
+ row_index_view=self.robot_index_view,
368
+ n_rows=1, n_cols=self.flight_info.n_cols,
369
+ read=False)
370
+
371
+ # should also empty the timeline for stepping phases
372
+ self._step_idx = 0
373
+
374
+ else:
375
+ exception = f"Cannot call reset() since run() was not called!"
376
+ Journal.log(self.__class__.__name__,
377
+ "reset",
378
+ exception,
379
+ LogType.EXCEP,
380
+ throw_when_excep = True)
381
+
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/utils/__init__.py ADDED
File without changes
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/horizon_based/utils/math_utils.py ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import numpy as np
3
+
4
+ def w2hor_frame(t_w: np.ndarray,
5
+ q_b: np.ndarray,
6
+ t_out: np.ndarray):
7
+ """
8
+ Transforms a twist vector expressed in WORLD frame to
9
+ an "horizontal" frame (z aligned as world, x aligned as the projection
10
+ of the x-axis of the base frame described by q_b). This is useful for specifying locomotion
11
+ references in a "game"-like fashion.
12
+ t_out will hold the result
13
+ """
14
+ # q_b = q_b / q_b.norm(dim=1, keepdim=True)
15
+ q_w, q_i, q_j, q_k = q_b[3, :], q_b[0, :], q_b[1, :], q_b[2, :]
16
+
17
+ R_11 = 1 - 2 * (q_j ** 2 + q_k ** 2)
18
+ R_21 = 2 * (q_i * q_j + q_k * q_w)
19
+
20
+ norm = np.sqrt(R_11 ** 2 + R_21 ** 2)
21
+ x_proj_x = R_11 / norm
22
+ x_proj_y = R_21 / norm
23
+
24
+ y_proj_x = -x_proj_y
25
+ y_proj_y = x_proj_x
26
+
27
+ t_out[0, :] = t_w[0, :] * x_proj_x + t_w[1, :] * x_proj_y
28
+ t_out[1, :] = t_w[0, :] * y_proj_x + t_w[1, :] * y_proj_y
29
+ t_out[2, :] = t_w[2, :] # z-component remains the same
30
+ t_out[3, :] = t_w[3, :] * x_proj_x + t_w[4, :] * x_proj_y
31
+ t_out[4, :] = t_w[3, :] * y_proj_x + t_w[4, :] * y_proj_y
32
+ t_out[5, :] = t_w[5, :] # z-component remains the same
33
+
34
+ def hor2w_frame(t_h: np.ndarray,
35
+ q_b: np.ndarray,
36
+ t_out: np.ndarray):
37
+ """
38
+ Transforms a velocity vector expressed in "horizontal" frame to WORLD
39
+ v_out will hold the result
40
+ """
41
+
42
+ # Extract quaternion components
43
+ q_w, q_i, q_j, q_k = q_b[3, :], q_b[0, :], q_b[1, :], q_b[2, :]
44
+
45
+ # Compute rotation matrix elements
46
+ R_11 = 1 - 2 * (q_j ** 2 + q_k ** 2)
47
+ R_21 = 2 * (q_i * q_j + q_k * q_w)
48
+
49
+ # Normalize to get projection components
50
+ norm = np.sqrt(R_11 ** 2 + R_21 ** 2)
51
+ x_proj_x = R_11 / norm
52
+ x_proj_y = R_21 / norm
53
+
54
+ # Orthogonal vector components
55
+ y_proj_x = -x_proj_y
56
+ y_proj_y = x_proj_x
57
+
58
+ # Transform velocity vector components from horizontal to world frame
59
+ t_out[0, :] = t_h[0, :] * x_proj_x + t_h[1, :] * y_proj_x
60
+ t_out[1, :] = t_h[0, :] * x_proj_y + t_h[1, :] * y_proj_y
61
+ t_out[2, :] = t_h[2, :] # z-component remains the same
62
+ t_out[3, :] = t_h[3, :] * x_proj_x + t_h[4, :] * y_proj_x
63
+ t_out[4, :] = t_h[3, :] * x_proj_y + t_h[4, :] * y_proj_y
64
+ t_out[5, :] = t_h[5, :] # z-component remains the same
65
+
66
+ def base2world_frame(t_b: np.ndarray,
67
+ q_b: np.ndarray,
68
+ t_out: np.ndarray):
69
+ """
70
+ Transforms a velocity vector expressed in the base frame to
71
+ the WORLD frame using the given quaternion that describes the orientation
72
+ of the base with respect to the world frame. The result is written in v_out.
73
+ """
74
+ # q_b = q_b / q_b.norm(dim=1, keepdim=True)
75
+ q_w, q_i, q_j, q_k = q_b[3, :], q_b[0, :], q_b[1, :], q_b[2, :]
76
+
77
+ R_00 = 1 - 2 * (q_j ** 2 + q_k ** 2)
78
+ R_01 = 2 * (q_i * q_j - q_k * q_w)
79
+ R_02 = 2 * (q_i * q_k + q_j * q_w)
80
+
81
+ R_10 = 2 * (q_i * q_j + q_k * q_w)
82
+ R_11 = 1 - 2 * (q_i ** 2 + q_k ** 2)
83
+ R_12 = 2 * (q_j * q_k - q_i * q_w)
84
+
85
+ R_20 = 2 * (q_i * q_k - q_j * q_w)
86
+ R_21 = 2 * (q_j * q_k + q_i * q_w)
87
+ R_22 = 1 - 2 * (q_i ** 2 + q_j ** 2)
88
+
89
+ # Transform the velocity to the world frame
90
+ t_out[0, :] = t_b[0, :] * R_00 + t_b[1, :] * R_01 + t_b[2, :] * R_02
91
+ t_out[1, :] = t_b[0, :] * R_10 + t_b[1, :] * R_11 + t_b[2, :] * R_12
92
+ t_out[2, :] = t_b[0, :] * R_20 + t_b[1, :] * R_21 + t_b[2, :] * R_22
93
+ t_out[3, :] = t_b[3, :] * R_00 + t_b[4, :] * R_01 + t_b[5, :] * R_02
94
+ t_out[4, :] = t_b[3, :] * R_10 + t_b[4, :] * R_11 + t_b[5, :] * R_12
95
+ t_out[5, :] = t_b[3, :] * R_20 + t_b[4, :] * R_21 + t_b[5, :] * R_22
96
+
97
+ def world2base_frame(t_w: np.ndarray,
98
+ q_b: np.ndarray,
99
+ t_out: np.ndarray):
100
+ """
101
+ Transforms a velocity vector expressed in the WORLD frame to
102
+ the base frame using the given quaternion that describes the orientation
103
+ of the base with respect to the world frame. The result is written in v_out.
104
+ """
105
+ # q_b = q_b / q_b.norm(dim=1, keepdim=True)
106
+ q_w, q_i, q_j, q_k = q_b[3, :], q_b[0, :], q_b[1, :], q_b[2, :]
107
+
108
+ R_00 = 1 - 2 * (q_j ** 2 + q_k ** 2)
109
+ R_01 = 2 * (q_i * q_j - q_k * q_w)
110
+ R_02 = 2 * (q_i * q_k + q_j * q_w)
111
+
112
+ R_10 = 2 * (q_i * q_j + q_k * q_w)
113
+ R_11 = 1 - 2 * (q_i ** 2 + q_k ** 2)
114
+ R_12 = 2 * (q_j * q_k - q_i * q_w)
115
+
116
+ R_20 = 2 * (q_i * q_k - q_j * q_w)
117
+ R_21 = 2 * (q_j * q_k + q_i * q_w)
118
+ R_22 = 1 - 2 * (q_i ** 2 + q_j ** 2)
119
+
120
+ # Transform the velocity to the base frame using the transpose of the rotation matrix
121
+ t_out[0, :] = t_w[0, :] * R_00 + t_w[1, :] * R_10 + t_w[2, :] * R_20
122
+ t_out[1, :] = t_w[0, :] * R_01 + t_w[1, :] * R_11 + t_w[2, :] * R_21
123
+ t_out[2, :] = t_w[0, :] * R_02 + t_w[1, :] * R_12 + t_w[2, :] * R_22
124
+ t_out[3, :] = t_w[3, :] * R_00 + t_w[4, :] * R_10 + t_w[5, :] * R_20
125
+ t_out[4, :] = t_w[3, :] * R_01 + t_w[4, :] * R_11 + t_w[5, :] * R_21
126
+ t_out[5, :] = t_w[3, :] * R_02 + t_w[4, :] * R_12 + t_w[5, :] * R_22
127
+
128
+ if __name__ == "__main__":
129
+
130
+ n_envs = 5000
131
+ t_b = np.random.rand(6, n_envs)
132
+
133
+ q_b = np.random.rand(4, n_envs)
134
+ q_b_norm = q_b / np.linalg.norm(q_b, axis=0, keepdims=True)
135
+
136
+ t_w = np.zeros_like(t_b) # To hold horizontal frame velocities
137
+ t_b_recovered = np.zeros_like(t_b) # To hold recovered world frame velocities
138
+ base2world_frame(t_b, q_b_norm, t_w)
139
+ world2base_frame(t_w, q_b_norm, t_b_recovered)
140
+ assert np.allclose(t_b, t_b_recovered, atol=1e-6), "Test failed: t_w_recovered does not match t_b"
141
+ print("Forward test passed: t_b_recovered matches t_b")
142
+
143
+ t_b2 = np.zeros_like(t_b) # To hold horizontal frame velocities
144
+ t_w_recovered = np.zeros_like(t_b)
145
+ world2base_frame(t_b, q_b_norm, t_b2)
146
+ base2world_frame(t_b2, q_b_norm, t_w_recovered)
147
+ assert np.allclose(t_b, t_w_recovered, atol=1e-6), "Test failed: t_w_recovered does not match t_b"
148
+ print("Backward test passed: t_w_recovered matches t_w")
149
+
150
+ # test transf. world-horizontal frame
151
+ v_h = np.zeros_like(t_b) # To hold horizontal frame velocities
152
+ v_recovered = np.zeros_like(t_b)
153
+ w2hor_frame(t_b, q_b_norm, v_h)
154
+ hor2w_frame(v_h, q_b_norm, v_recovered)
155
+ assert np.allclose(t_b, v_recovered, atol=1e-6), "Test failed: v_recovered does not match t_b"
156
+ print("horizontal forward frame test passed: matches ")
157
+
158
+ t_w = np.zeros_like(t_b) # To hold horizontal frame velocities
159
+ v_h_recovered = np.zeros_like(t_b)
160
+ hor2w_frame(t_b, q_b_norm, t_w)
161
+ w2hor_frame(t_w, q_b_norm, v_h_recovered)
162
+ assert np.allclose(t_b, v_h_recovered, atol=1e-6), "Test failed: v_h_recovered does not match t_b"
163
+ print("horizontal backward frame test passed: matches ")
164
+
165
+
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/other/hybrid_quad_client.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from aug_mpc.controllers.rhc.augmpc_cluster_client import AugMpcClusterClient
2
+
3
+ from aug_mpc.controllers.rhc.horizon_based.hybrid_quad_rhc import HybridQuadRhc
4
+ from aug_mpc.utils.hybrid_quad_xrdf_gen import get_xrdf_cmds_horizon
5
+ from aug_mpc.utils.sys_utils import PathsGetter
6
+
7
+ from typing import List, Dict
8
+
9
+ class HybridQuadrupedClusterClient(AugMpcClusterClient):
10
+
11
+ def _import_aux_libs(self):
12
+ super()._import_aux_libs()
13
+ # Import Horizon and related dependencies as global libs
14
+ from aug_mpc.controllers.rhc.horizon_based.horizon_imports_glob import import_horizon_global
15
+ import_horizon_global()
16
+
17
+ def __init__(self,
18
+ namespace: str,
19
+ urdf_xacro_path: str,
20
+ srdf_xacro_path: str,
21
+ cluster_size: int,
22
+ set_affinity: bool = False,
23
+ use_mp_fork: bool = False,
24
+ isolated_cores_only: bool = False,
25
+ core_ids_override_list: List[int] = None,
26
+ verbose: bool = False,
27
+ debug: bool = False,
28
+ open_loop: bool = True,
29
+ base_dump_dir: str = "/tmp",
30
+ timeout_ms: int = 60000,
31
+ codegen_override: str = None,
32
+ custom_opts: Dict = {}):
33
+
34
+ self._open_loop = open_loop
35
+
36
+ self._paths = PathsGetter()
37
+
38
+ self._codegen_dir_name = namespace
39
+
40
+ self._timeout_ms = timeout_ms
41
+
42
+ super().__init__(namespace = namespace,
43
+ urdf_xacro_path = urdf_xacro_path,
44
+ srdf_xacro_path = srdf_xacro_path,
45
+ cluster_size=cluster_size,
46
+ set_affinity = set_affinity,
47
+ use_mp_fork = use_mp_fork,
48
+ isolated_cores_only = isolated_cores_only,
49
+ core_ids_override_list = core_ids_override_list,
50
+ verbose = verbose,
51
+ debug = debug,
52
+ base_dump_dir=base_dump_dir,
53
+ codegen_override=codegen_override,
54
+ custom_opts=custom_opts)
55
+
56
+ self._n_nodes = 31 if not ("n_nodes" in self._custom_opts) else self._custom_opts["n_nodes"]
57
+ self._dt = 0.05 if not ("cluster_dt" in self._custom_opts) else self._custom_opts["cluster_dt"]
58
+
59
+ def _xrdf_cmds(self):
60
+ parts = self._urdf_path.split('/')
61
+ urdf_descr_root_path = '/'.join(parts[:-2])
62
+ cmds = get_xrdf_cmds_horizon(urdf_descr_root_path=urdf_descr_root_path)
63
+ return cmds
64
+
65
+ def _process_codegen_dir(self,idx:int):
66
+
67
+ codegen_dir = self.codegen_dir() + f"/{self._codegen_dir_name}Rhc{idx}"
68
+ codegen_dir_ovveride = self.codegen_dir_override()
69
+ if not (codegen_dir_ovveride=="" or \
70
+ codegen_dir_ovveride=="none" or \
71
+ codegen_dir_ovveride=="None" or \
72
+ (codegen_dir_ovveride is None)): # if overrde was provided
73
+ codegen_dir = f"{codegen_dir_ovveride}{idx}"# override
74
+
75
+ return codegen_dir
76
+
77
+ def _generate_controller(self,
78
+ idx: int):
79
+
80
+ codegen_dir=self._process_codegen_dir(idx=idx)
81
+
82
+ controller = HybridQuadRhc(
83
+ urdf_path=self._urdf_path,
84
+ srdf_path=self._srdf_path,
85
+ config_path = self._paths.CONFIGPATH,
86
+ robot_name=self._namespace,
87
+ codegen_dir=codegen_dir,
88
+ n_nodes=self._n_nodes,
89
+ dt=self._dt,
90
+ max_solver_iter = 1, # rti
91
+ open_loop = self._open_loop,
92
+ verbose = self._verbose,
93
+ debug = self._debug)
94
+
95
+ return controller
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/rhc.py ADDED
@@ -0,0 +1,1226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2023 Andrea Patrizi (AndrePatri, andreapatrizi1b6e6@gmail.com)
2
+ #
3
+ # This file is part of MPCHive and distributed under the General Public License version 2 license.
4
+ #
5
+ # MPCHive is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 2 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # MPCHive is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with MPCHive. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+ from abc import ABC, abstractmethod
19
+ # from perf_sleep.pyperfsleep import PerfSleep
20
+ # from mpc_hive.utilities.cpu_utils.core_utils import get_memory_usage
21
+
22
+ import time
23
+
24
+ import numpy as np
25
+
26
+ from mpc_hive.utilities.shared_data.rhc_data import RobotState
27
+ from mpc_hive.utilities.shared_data.rhc_data import RhcCmds, RhcPred, RhcPredDelta
28
+ from mpc_hive.utilities.shared_data.rhc_data import RhcStatus
29
+ from mpc_hive.utilities.shared_data.rhc_data import RhcInternal
30
+ from mpc_hive.utilities.shared_data.cluster_profiling import RhcProfiling
31
+ from mpc_hive.utilities.remote_triggering import RemoteTriggererClnt
32
+
33
+ from mpc_hive.utilities.homing import RobotHomer
34
+
35
+ from mpc_hive.utilities.math_utils import world2base_frame
36
+
37
+ from EigenIPC.PyEigenIPC import VLevel
38
+ from EigenIPC.PyEigenIPC import Journal, LogType
39
+ from EigenIPC.PyEigenIPCExt.wrappers.shared_data_view import SharedTWrapper
40
+ from EigenIPC.PyEigenIPC import dtype
41
+
42
+ from typing import List
43
+ # from typing import TypeVar, Union
44
+
45
+ import signal
46
+ import os
47
+ import inspect
48
+
49
+ class RHController(ABC):
50
+
51
+ def __init__(self,
52
+ srdf_path: str,
53
+ n_nodes: int,
54
+ dt: float,
55
+ namespace: str, # shared mem namespace
56
+ dtype = np.float32,
57
+ verbose = False,
58
+ debug = False,
59
+ timeout_ms: int = 60000,
60
+ allow_less_jnts: bool = True):
61
+
62
+ signal.signal(signal.SIGINT, self._handle_sigint)
63
+
64
+ self._allow_less_jnts = allow_less_jnts # whether to allow less joints in rhc controller than the ones on the robot
65
+ # (e.g. some joints might not be desirable for control purposes)
66
+
67
+ self.namespace = namespace
68
+ self._dtype = dtype
69
+ self._verbose = verbose
70
+ self._debug = debug
71
+
72
+ # if not self._debug:
73
+ np.seterr(over='ignore') # ignoring overflows
74
+
75
+ self._n_nodes = n_nodes
76
+ self._dt = dt
77
+ self._n_intervals = self._n_nodes - 1
78
+ self._t_horizon = self._n_intervals * dt
79
+ self._set_rhc_pred_idx() # prection is by default written on last node
80
+ self._set_rhc_cmds_idx() # default to idx 2 (i.e. cmds to get to third node)
81
+ self.controller_index = None # will be assigned upon registration to a cluster
82
+ self.controller_index_np = None
83
+ self._robot_mass=1.0
84
+
85
+ self.srdf_path = srdf_path # using for parsing robot homing
86
+
87
+ self._registered = False
88
+ self._closed = False
89
+
90
+ self._allow_triggering_when_failed = True
91
+
92
+ self._profiling_data_dict = {}
93
+ self._profiling_data_dict["full_solve_dt"] = np.nan
94
+ self._profiling_data_dict["rti_solve_dt"] = np.nan
95
+ self._profiling_data_dict["problem_update_dt"] = np.nan
96
+ self._profiling_data_dict["phases_shift_dt"] = np.nan
97
+ self._profiling_data_dict["task_ref_update"] = np.nan
98
+
99
+ self.n_dofs = None
100
+ self.n_contacts = None
101
+
102
+ # shared mem
103
+ self.robot_state = None
104
+ self.rhc_status = None
105
+ self.rhc_internal = None
106
+ self.cluster_stats = None
107
+ self.robot_cmds = None
108
+ self.robot_pred = None
109
+ self.rhc_pred_delta = None
110
+ self.rhc_refs = None
111
+ self._remote_triggerer = None
112
+ self._remote_triggerer_timeout = timeout_ms # [ms]
113
+
114
+ # remote termination
115
+ self._remote_term = None
116
+ self._term_req_received = False
117
+
118
+ # jnt names
119
+ self._env_side_jnt_names = []
120
+ self._controller_side_jnt_names = []
121
+ self._got_jnt_names_from_controllers = False
122
+
123
+ # data maps
124
+ self._to_controller = []
125
+ self._quat_remap = [0, 1, 2, 3] # defaults to no remap (to be overridden)
126
+
127
+ self._got_contact_names = False
128
+
129
+ self._received_trigger = False # used for proper termination
130
+
131
+ self._n_resets = 0
132
+ self._n_fails = 0
133
+ self._fail_idx_thresh = 5e3
134
+ self._contact_f_thresh = 1e5
135
+
136
+ self._failed = False
137
+
138
+ self._start_time = time.perf_counter() # used for profiling when in debug mode
139
+
140
+ self._homer = None # robot homing manager
141
+ self._homer_env = None # used for setting homing to jnts not contained in rhc prb
142
+
143
+ self._class_name_base = f"{self.__class__.__name__}"
144
+
145
+ self._contact_force_base_loc_aux=np.zeros((1,3),dtype=self._dtype)
146
+ self._norm_grav_vector_w=np.zeros((1,3),dtype=self._dtype)
147
+ self._norm_grav_vector_w[:, 2]=-1.0
148
+ self._norm_grav_vector_base_loc=np.zeros((1,3),dtype=self._dtype)
149
+
150
+ self._init() # initialize controller
151
+
152
+ if not hasattr(self, '_rhc_fpaths'):
153
+ self._rhc_fpaths = []
154
+ self._rhc_fpaths.append(os.path.abspath(__file__))
155
+
156
+ def __init_subclass__(cls, **kwargs):
157
+ super().__init_subclass__(**kwargs)
158
+ # Get the file path of the class being initialized and append it to the list
159
+ if not hasattr(cls, '_rhc_fpaths'):
160
+ cls._rhc_fpaths = []
161
+ child_class_file_path = os.path.abspath(inspect.getfile(cls))
162
+ cls._rhc_fpaths.append(child_class_file_path)
163
+
164
+ def this_paths(self):
165
+ return self._rhc_fpaths
166
+
167
+ def __del__(self):
168
+ self._close()
169
+
170
+ def _handle_sigint(self, signum, frame):
171
+ Journal.log(self._class_name_base,
172
+ "_handle_sigint",
173
+ "SIGINT received",
174
+ LogType.WARN)
175
+ self._term_req_received = True
176
+
177
+ def _set_rhc_pred_idx(self):
178
+ # default to last node
179
+ self._pred_node_idx=self._n_nodes-1
180
+
181
+ def _set_rhc_cmds_idx(self):
182
+ # use index 2 by default to compensate for
183
+ # the inevitable action delay
184
+ # (get_state, trigger sol -> +dt -> apply sol )
185
+ # if we apply action from second node (idenx 1)
186
+ # that action will already be one dt old. We assume we were already
187
+ # applying the right action to get to the state of node idx 1 and get the
188
+ # cmds for reaching the state at idx 1
189
+ self._rhc_cmds_node_idx=2
190
+
191
+ def _close(self):
192
+ if not self._closed:
193
+ self._unregister_from_cluster()
194
+ if self.robot_cmds is not None:
195
+ self.robot_cmds.close()
196
+ if self.robot_pred is not None:
197
+ self.robot_pred.close()
198
+ if self.rhc_pred_delta is not None:
199
+ self.rhc_pred_delta.close()
200
+ if self.robot_state is not None:
201
+ self.robot_state.close()
202
+ if self.rhc_status is not None:
203
+ self.rhc_status.close()
204
+ if self.rhc_internal is not None:
205
+ self.rhc_internal.close()
206
+ if self.cluster_stats is not None:
207
+ self.cluster_stats.close()
208
+ if self._remote_triggerer is not None:
209
+ self._remote_triggerer.close()
210
+ if self._remote_term is not None:
211
+ self._remote_term.close()
212
+ self._closed = True
213
+
214
+ def close(self):
215
+ self._close()
216
+
217
+ def get_file_paths(self):
218
+ # can be overriden by child
219
+ base_paths = []
220
+ base_paths.append(self._this_path)
221
+ return base_paths
222
+
223
+ def init_rhc_task_cmds(self):
224
+
225
+ self.rhc_refs = self._init_rhc_task_cmds()
226
+ self.rhc_refs.reset()
227
+
228
+ def _init_states(self):
229
+
230
+ quat_remap = self._get_quat_remap()
231
+ self.robot_state = RobotState(namespace=self.namespace,
232
+ is_server=False,
233
+ q_remapping=quat_remap, # remapping from environment to controller
234
+ with_gpu_mirror=False,
235
+ with_torch_view=False,
236
+ safe=False,
237
+ verbose=self._verbose,
238
+ vlevel=VLevel.V2,
239
+ optimize_mem=True,
240
+ n_robots=1, # we just need the row corresponding to this controller
241
+ n_jnts=None, # got from server
242
+ n_contacts=None # got from server
243
+ )
244
+ self.robot_state.run()
245
+ self.robot_cmds = RhcCmds(namespace=self.namespace,
246
+ is_server=False,
247
+ q_remapping=quat_remap, # remapping from environment to controller
248
+ with_gpu_mirror=False,
249
+ with_torch_view=False,
250
+ safe=False,
251
+ verbose=self._verbose,
252
+ vlevel=VLevel.V2,
253
+ optimize_mem=True,
254
+ n_robots=1, # we just need the row corresponding to this controller
255
+ n_jnts=None, # got from server
256
+ n_contacts=None # got from server
257
+ )
258
+ self.robot_cmds.run()
259
+ self.robot_pred = RhcPred(namespace=self.namespace,
260
+ is_server=False,
261
+ q_remapping=quat_remap, # remapping from environment to controller
262
+ with_gpu_mirror=False,
263
+ with_torch_view=False,
264
+ safe=False,
265
+ verbose=self._verbose,
266
+ vlevel=VLevel.V2,
267
+ optimize_mem=True,
268
+ n_robots=1, # we just need the row corresponding to this controller
269
+ n_jnts=None, # got from server
270
+ n_contacts=None # got from server
271
+ )
272
+ self.robot_pred.run()
273
+ self.rhc_pred_delta = RhcPredDelta(namespace=self.namespace,
274
+ is_server=False,
275
+ q_remapping=quat_remap, # remapping from environment to controller
276
+ with_gpu_mirror=False,
277
+ with_torch_view=False,
278
+ safe=False,
279
+ verbose=self._verbose,
280
+ vlevel=VLevel.V2,
281
+ optimize_mem=True,
282
+ n_robots=1, # we just need the row corresponding to this controller
283
+ n_jnts=None, # got from server
284
+ n_contacts=None # got from server
285
+ )
286
+ self.rhc_pred_delta.run()
287
+
288
+ def _rhc(self):
289
+ if self._debug:
290
+ self._rhc_db()
291
+ else:
292
+ self._rhc_min()
293
+
294
+ def _rhc_db(self):
295
+ # rhc with debug data
296
+ self._start_time = time.perf_counter()
297
+
298
+ self.robot_state.synch_from_shared_mem(robot_idx=self.controller_index, robot_idx_view=self.controller_index_np) # updates robot state with
299
+ # latest data on shared mem
300
+
301
+ self._compute_pred_delta()
302
+
303
+ if not self.failed():
304
+ # we can solve only if not in failure state
305
+ self._failed = not self._solve() # solve actual TO
306
+ if (self._failed):
307
+ # perform failure procedure
308
+ self._on_failure()
309
+ else:
310
+ if not self._allow_triggering_when_failed:
311
+ Journal.log(self._class_name_base,
312
+ "solve",
313
+ "Received solution req, but controller is in failure state. " + \
314
+ " You should have reset() the controller!",
315
+ LogType.EXCEP,
316
+ throw_when_excep = True)
317
+ else:
318
+ if self._verbose:
319
+ Journal.log(self._class_name_base,
320
+ "solve",
321
+ "Received solution req, but controller is in failure state. No solution will be performed. " + \
322
+ " Use the reset() method to continue solving!",
323
+ LogType.WARN)
324
+
325
+ self._write_cmds_from_sol() # we update update the views of the cmds
326
+ # from the latest solution
327
+
328
+ # in debug, rhc internal state is streamed over
329
+ # shared mem.
330
+ self._update_rhc_internal()
331
+ self._profiling_data_dict["full_solve_dt"] = time.perf_counter() - self._start_time
332
+ self._update_profiling_data() # updates all profiling data
333
+ if self._verbose:
334
+ Journal.log(self._class_name_base,
335
+ "solve",
336
+ f"RHC full solve loop execution time -> " + str(self._profiling_data_dict["full_solve_dt"]),
337
+ LogType.INFO,
338
+ throw_when_excep = True)
339
+
340
+ def _rhc_min(self):
341
+
342
+ self.robot_state.synch_from_shared_mem(robot_idx=self.controller_index, robot_idx_view=self.controller_index_np) # updates robot state with
343
+ # latest data on shared mem
344
+
345
+ self._compute_pred_delta()
346
+
347
+ if not self.failed():
348
+ # we can solve only if not in failure state
349
+ self._failed = not self._solve() # solve actual TO
350
+ if (self._failed):
351
+ # perform failure procedure
352
+ self._on_failure()
353
+ else:
354
+ if not self._allow_triggering_when_failed:
355
+ Journal.log(self._class_name_base,
356
+ "solve",
357
+ "Received solution req, but controller is in failure state. " + \
358
+ " You should have reset() the controller!",
359
+ LogType.EXCEP,
360
+ throw_when_excep = True)
361
+ else:
362
+ if self._verbose:
363
+ Journal.log(self._class_name_base,
364
+ "solve",
365
+ "Received solution req, but controller is in failure state. No solution will be performed. " + \
366
+ " Use the reset() method to continue solving!",
367
+ LogType.WARN)
368
+
369
+ self._write_cmds_from_sol() # we update the views of the cmds
370
+ # from the latest solution even if failed
371
+
372
+ def solve(self):
373
+
374
+ # run the solution loop and wait for trigger signals
375
+ # using cond. variables (efficient)
376
+ while not self._term_req_received:
377
+ # we are always listening for a trigger signal
378
+ if not self._remote_triggerer.wait(self._remote_triggerer_timeout):
379
+ Journal.log(self._class_name_base,
380
+ "solve",
381
+ "Didn't receive any remote trigger req within timeout!",
382
+ LogType.EXCEP,
383
+ throw_when_excep = False)
384
+ break
385
+ self._received_trigger = True
386
+ # signal received -> we process incoming requests
387
+ # perform reset, if required
388
+ if self.rhc_status.resets.read_retry(row_index=self.controller_index,
389
+ col_index=0,
390
+ row_index_view=0)[0]:
391
+ self.reset() # rhc is reset
392
+ # check if a trigger request was received
393
+ if self.rhc_status.trigger.read_retry(row_index=self.controller_index,
394
+ col_index=0,
395
+ row_index_view=0)[0]:
396
+ self._rhc() # run solution
397
+ self.rhc_status.trigger.write_retry(False,
398
+ row_index=self.controller_index,
399
+ col_index=0,
400
+ row_index_view=0) # allow next solution trigger
401
+
402
+ self._remote_triggerer.ack() # send ack signal to server
403
+ self._received_trigger = False
404
+
405
+ self._term_req_received = self._term_req_received or self._remote_term.read_retry(row_index=0,
406
+ col_index=0,
407
+ row_index_view=0)[0]
408
+ self.close() # is not stricly necessary
409
+
410
+ def reset(self):
411
+
412
+ if not self._closed:
413
+ self.reset_rhc_data()
414
+ self._failed = False # allow triggering
415
+ self._n_resets += 1
416
+ self.rhc_status.fails.write_retry(False,
417
+ row_index=self.controller_index,
418
+ col_index=0,
419
+ row_index_view=0)
420
+ self.rhc_status.resets.write_retry(False,
421
+ row_index=self.controller_index,
422
+ col_index=0,
423
+ row_index_view=0)
424
+
425
+ def _create_jnt_maps(self):
426
+
427
+ # retrieve env-side joint names from shared mem
428
+ self._env_side_jnt_names = self.robot_state.jnt_names()
429
+ self._check_jnt_names_compatibility() # will raise exception if not self._allow_less_jnts
430
+ if not self._got_jnt_names_from_controllers:
431
+ exception = f"Cannot run the solve(). assign_env_side_jnt_names() was not called!"
432
+ Journal.log(self._class_name_base,
433
+ "_create_jnt_maps",
434
+ exception,
435
+ LogType.EXCEP,
436
+ throw_when_excep = True)
437
+
438
+ # robot homer guarantees that _controller_side_jnt_names is at least contained in self._env_side_jnt_names (or equal)
439
+ self._to_controller = [self._env_side_jnt_names.index(element) for element in self._controller_side_jnt_names]
440
+ # set joint remappings for shared data from env data to controller
441
+ # all shared data is by convention specified according to the env (jnts are ordered that way)
442
+ # the remapping is used so that when data is returned, its a remapped view from env to controller,
443
+ # so that data can be assigned direclty from the rhc controller without any issues
444
+ self.robot_state.set_jnts_remapping(jnts_remapping=self._to_controller)
445
+ self.robot_cmds.set_jnts_remapping(jnts_remapping=self._to_controller)
446
+ self.robot_pred.set_jnts_remapping(jnts_remapping=self._to_controller)
447
+ self.rhc_pred_delta.set_jnts_remapping(jnts_remapping=self._to_controller)
448
+
449
+ return True
450
+
451
+ def reset_rhc_data(self):
452
+
453
+ self._reset() # custom reset (e.g. it should set the current solution to some
454
+ # default solution, like a bootstrap)
455
+
456
+ self.rhc_refs.reset() # reset rhc refs to default (has to be called after _reset)
457
+
458
+ self._write_cmds_from_sol() # use latest solution (e.g. from bootstrap if called before running
459
+ # the first solve) as default state
460
+
461
+ def failed(self):
462
+ return self._failed
463
+
464
+ def robot_mass(self):
465
+ return self._robot_mass
466
+
467
+ def _assign_cntrl_index(self, reg_state: np.ndarray):
468
+ state = reg_state.flatten() # ensure 1D tensor
469
+ free_spots = np.nonzero(~state.flatten())[0]
470
+ return free_spots[0].item() # just return the first free spot
471
+
472
+ def _register_to_cluster(self):
473
+
474
+ self.rhc_status = RhcStatus(is_server=False,
475
+ namespace=self.namespace,
476
+ verbose=self._verbose,
477
+ vlevel=VLevel.V2,
478
+ with_torch_view=False,
479
+ with_gpu_mirror=False,
480
+ optimize_mem=True,
481
+ cluster_size=1, # we just need the row corresponding to this controller
482
+ n_contacts=None, # we get this from server
483
+ n_nodes=None # we get this from server
484
+ )
485
+ self.rhc_status.run() # rhc status (reg. flags, failure, tot cost, tot cnstrl viol, etc...)
486
+
487
+ # acquire semaphores since we have to perform non-atomic operations
488
+ # on the whole memory views
489
+ self.rhc_status.registration.data_sem_acquire()
490
+ self.rhc_status.controllers_counter.data_sem_acquire()
491
+ self.rhc_status.controllers_counter.synch_all(retry = True,
492
+ read = True)
493
+
494
+ available_spots = self.rhc_status.cluster_size
495
+ # from here on all pre registration ops can be done
496
+
497
+ # incrementing cluster controllers counter
498
+ controllers_counter = self.rhc_status.controllers_counter.get_numpy_mirror()
499
+ if controllers_counter[0, 0] + 1 > available_spots: # no space left -> return
500
+ self.rhc_status.controllers_counter.data_sem_release()
501
+ self.rhc_status.registration.data_sem_release()
502
+ exception = "Cannot register to cluster. No space left " + \
503
+ f"({controllers_counter[0, 0]} controllers already registered)"
504
+ Journal.log(self._class_name_base,
505
+ "_register_to_cluster",
506
+ exception,
507
+ LogType.EXCEP,
508
+ throw_when_excep = True)
509
+
510
+ # now we can register
511
+
512
+ # increment controllers counter
513
+ controllers_counter += 1
514
+ self.controller_index = controllers_counter.item() -1
515
+
516
+ # actually register to cluster
517
+ self.rhc_status.controllers_counter.synch_all(retry = True,
518
+ read = False) # writes to shared mem (just one for all controllers (i.e n_rows = 1))
519
+
520
+ # read current registration state
521
+ self.rhc_status.registration.synch_all(retry = True,
522
+ read = True,
523
+ row_index=self.controller_index,
524
+ row_index_view=0)
525
+ registrations = self.rhc_status.registration.get_numpy_mirror()
526
+ # self.controller_index = self._assign_cntrl_index(registrations)
527
+
528
+
529
+ self._class_name_base = self._class_name_base+str(self.controller_index)
530
+ # self.controller_index_np = np.array(self.controller_index)
531
+ self.controller_index_np = np.array(0) # given that we use optimize_mem, the shared mem copy has shape 1 x n_cols (we can write and read at [0, :])
532
+
533
+ registrations[self.controller_index_np, 0] = True
534
+ self.rhc_status.registration.synch_all(retry = True,
535
+ read = False,
536
+ row_index=self.controller_index,
537
+ col_index=0,
538
+ row_index_view=0)
539
+
540
+ # now all heavy stuff that would otherwise make the registration slow
541
+ self._remote_term = SharedTWrapper(namespace=self.namespace,
542
+ basename="RemoteTermination",
543
+ is_server=False,
544
+ verbose = self._verbose,
545
+ vlevel = VLevel.V2,
546
+ with_gpu_mirror=False,
547
+ with_torch_view=False,
548
+ dtype=dtype.Bool)
549
+ self._remote_term.run()
550
+
551
+ # other initializations
552
+
553
+ self.cluster_stats = RhcProfiling(is_server=False,
554
+ name=self.namespace,
555
+ verbose=self._verbose,
556
+ vlevel=VLevel.V2,
557
+ safe=True,
558
+ optimize_mem=True,
559
+ cluster_size=1 # we just need the row corresponding to this controller
560
+ ) # profiling data
561
+ self.cluster_stats.run()
562
+ self.cluster_stats.synch_info()
563
+
564
+ self._create_jnt_maps()
565
+ self.init_rhc_task_cmds() # initializes rhc interface to external commands (defined by child class)
566
+ self._consinstency_checks() # sanity checks
567
+
568
+ if self._homer is None:
569
+ self._init_robot_homer() # call this in case it wasn't called by child
570
+
571
+ self._robot_mass = self._get_robot_mass() # uses child class implemented method
572
+ self._contact_f_scale = self._get_robot_mass() * 9.81
573
+
574
+ # writing some static info about this controller
575
+ # self.rhc_status.rhc_static_info.synch_all(retry = True,
576
+ # read = True,
577
+ # row_index=self.controller_index,
578
+ # col_index=0) # first read current static info from other controllers (not necessary if optimize_mem==True)
579
+ self.rhc_status.rhc_static_info.set(data=np.array(self._dt),
580
+ data_type="dts",
581
+ rhc_idxs=self.controller_index_np,
582
+ gpu=False)
583
+ self.rhc_status.rhc_static_info.set(data=np.array(self._t_horizon),
584
+ data_type="horizons",
585
+ rhc_idxs=self.controller_index_np,
586
+ gpu=False)
587
+ self.rhc_status.rhc_static_info.set(data=np.array(self._n_nodes),
588
+ data_type="nnodes",
589
+ rhc_idxs=self.controller_index_np,
590
+ gpu=False)
591
+ # writing some static rhc info which is only available after problem init
592
+ self.rhc_status.rhc_static_info.set(data=np.array(len(self._get_contacts())),
593
+ data_type="ncontacts",
594
+ rhc_idxs=self.controller_index_np,
595
+ gpu=False)
596
+ self.rhc_status.rhc_static_info.set(data=np.array(self.robot_mass()),
597
+ data_type="robot_mass",
598
+ rhc_idxs=self.controller_index_np,
599
+ gpu=False)
600
+ self.rhc_status.rhc_static_info.set(data=np.array(self._pred_node_idx),
601
+ data_type="pred_node_idx",
602
+ rhc_idxs=self.controller_index_np,
603
+ gpu=False)
604
+
605
+ self.rhc_status.rhc_static_info.synch_retry(row_index=self.controller_index,
606
+ col_index=0,
607
+ row_index_view=0,
608
+ n_rows=1, n_cols=self.rhc_status.rhc_static_info.n_cols,
609
+ read=False)
610
+
611
+ # we set homings also for joints which are not in the rhc homing map
612
+ # since this is usually required on env side
613
+
614
+ homing_full = self._homer_env.get_homing().reshape(1,
615
+ self.robot_cmds.n_jnts())
616
+
617
+ null_action = np.zeros((1, self.robot_cmds.n_jnts()),
618
+ dtype=self._dtype)
619
+
620
+ self.robot_cmds.jnts_state.set(data=homing_full, data_type="q",
621
+ robot_idxs=self.controller_index_np,
622
+ no_remap=True)
623
+ self.robot_cmds.jnts_state.set(data=null_action, data_type="v",
624
+ robot_idxs=self.controller_index_np,
625
+ no_remap=True)
626
+ self.robot_cmds.jnts_state.set(data=null_action, data_type="eff",
627
+ robot_idxs=self.controller_index_np,
628
+ no_remap=True)
629
+
630
+ # write all joints (including homing for env-only ones)
631
+ self.robot_cmds.jnts_state.synch_retry(row_index=self.controller_index, col_index=0,
632
+ row_index_view=0,
633
+ n_rows=1, n_cols=self.robot_cmds.jnts_state.n_cols,
634
+ read=False) # only write data corresponding to this controller
635
+
636
+ self.reset() # reset controller
637
+ self._n_resets=0
638
+
639
+ # for last we create the trigger client
640
+ self._remote_triggerer = RemoteTriggererClnt(namespace=self.namespace,
641
+ verbose=self._verbose,
642
+ vlevel=VLevel.V2) # remote triggering
643
+ self._remote_triggerer.run()
644
+
645
+ if self._debug:
646
+ # internal solution is published on shared mem
647
+ # we assume the user has made available the cost
648
+ # and constraint data at this point (e.g. through
649
+ # the solution of a bootstrap)
650
+ cost_data = self._get_cost_data()
651
+ constr_data = self._get_constr_data()
652
+ config = RhcInternal.Config(is_server=True,
653
+ enable_q= True,
654
+ enable_v=True,
655
+ enable_a=True,
656
+ enable_a_dot=False,
657
+ enable_f=True,
658
+ enable_f_dot=False,
659
+ enable_eff=False,
660
+ cost_names=cost_data[0],
661
+ cost_dims=cost_data[1],
662
+ constr_names=constr_data[0],
663
+ constr_dims=constr_data[1],
664
+ )
665
+ self.rhc_internal = RhcInternal(config=config,
666
+ namespace=self.namespace,
667
+ rhc_index = self.controller_index,
668
+ n_contacts=self.n_contacts,
669
+ n_jnts=self.n_dofs,
670
+ jnt_names=self._controller_side_jnt_names,
671
+ n_nodes=self._n_nodes,
672
+ verbose = self._verbose,
673
+ vlevel=VLevel.V2,
674
+ force_reconnection=True,
675
+ safe=True)
676
+ self.rhc_internal.run()
677
+
678
+ Journal.log(self._class_name_base,
679
+ "_register_to_cluster",
680
+ f"controller {self.controller_index} registered",
681
+ LogType.STAT,
682
+ throw_when_excep = True)
683
+
684
+ # we can now release everything so that other controllers can register
685
+ self.rhc_status.controllers_counter.data_sem_release()
686
+ self.rhc_status.registration.data_sem_release()
687
+
688
+ self._registered = True
689
+
690
+ def _unregister_from_cluster(self):
691
+
692
+ if self._received_trigger:
693
+ # received interrupt during solution -->
694
+ # send ack signal to server anyway
695
+ self._remote_triggerer.ack()
696
+ if self._registered:
697
+ # acquire semaphores since we have to perform operations
698
+ # on the whole memory views
699
+ self.rhc_status.registration.data_sem_acquire()
700
+ self.rhc_status.controllers_counter.data_sem_acquire()
701
+ self.rhc_status.registration.write_retry(False,
702
+ row_index=self.controller_index,
703
+ col_index=0,
704
+ row_index_view=0)
705
+ self._deactivate()
706
+ # decrementing controllers counter
707
+ self.rhc_status.controllers_counter.synch_all(retry = True,
708
+ read = True)
709
+ controllers_counter = self.rhc_status.controllers_counter.get_numpy_mirror()
710
+ controllers_counter -= 1
711
+ self.rhc_status.controllers_counter.synch_all(retry = True,
712
+ read = False)
713
+ Journal.log(self._class_name_base,
714
+ "_unregister_from_cluster",
715
+ "Done",
716
+ LogType.STAT,
717
+ throw_when_excep = True)
718
+ # we can now release everything
719
+ self.rhc_status.registration.data_sem_release()
720
+ self.rhc_status.controllers_counter.data_sem_release()
721
+ self._registered = False
722
+
723
+ def _get_quat_remap(self):
724
+ # to be overridden by child class if necessary
725
+ return [0, 1, 2, 3]
726
+
727
+ def _consinstency_checks(self):
728
+
729
+ # check controller dt
730
+ server_side_cluster_dt = self.cluster_stats.get_info(info_name="cluster_dt")
731
+ if not (abs(server_side_cluster_dt - self._dt) < 1e-4):
732
+ exception = f"Trying to initialize a controller with control dt {self._dt}, which" + \
733
+ f"does not match the cluster control dt {server_side_cluster_dt}"
734
+ Journal.log(self._class_name_base,
735
+ "_consinstency_checks",
736
+ exception,
737
+ LogType.EXCEP,
738
+ throw_when_excep = True)
739
+ # check contact names
740
+
741
+ server_side_contact_names = set(self.robot_state.contact_names())
742
+ control_side_contact_names = set(self._get_contacts())
743
+
744
+ if not server_side_contact_names == control_side_contact_names:
745
+ warn = f"Controller-side contact names do not match server-side names!" + \
746
+ f"\nServer: {self.robot_state.contact_names()}\n Controller: {self._get_contacts()}"
747
+ Journal.log(self._class_name_base,
748
+ "_consinstency_checks",
749
+ warn,
750
+ LogType.WARN,
751
+ throw_when_excep = True)
752
+ if not len(self.robot_state.contact_names()) == len(self._get_contacts()):
753
+ # at least, we need the n of contacts to match!
754
+ exception = f"Controller-side n contacts {self._get_contacts()} do not match " + \
755
+ f"server-side n contacts {len(self.robot_state.contact_names())}!"
756
+ Journal.log(self._class_name_base,
757
+ "_consinstency_checks",
758
+ exception,
759
+ LogType.EXCEP,
760
+ throw_when_excep = True)
761
+
762
+ def _init(self):
763
+
764
+ stat = f"Trying to initialize RHC controller " + \
765
+ f"with dt: {self._dt} s, t_horizon: {self._t_horizon} s, n_intervals: {self._n_intervals}"
766
+ Journal.log(self._class_name_base,
767
+ "_init",
768
+ stat,
769
+ LogType.STAT,
770
+ throw_when_excep = True)
771
+
772
+ self._init_states() # initializes shared mem. states
773
+
774
+ self._init_problem() # we call the child's initialization method for the actual problem
775
+ self._post_problem_init()
776
+
777
+ self._register_to_cluster() # registers the controller to the cluster
778
+
779
+ Journal.log(self._class_name_base,
780
+ "_init",
781
+ f"RHC controller initialized with cluster index {self.controller_index} on process {os.getpid()}",
782
+ LogType.STAT,
783
+ throw_when_excep = True)
784
+
785
+ def _deactivate(self):
786
+ # signal controller deactivation over shared mem
787
+ self.rhc_status.activation_state.write_retry(False,
788
+ row_index=self.controller_index,
789
+ col_index=0,
790
+ row_index_view=0)
791
+ # also set cmds to homing for safety
792
+ # self.reset_rhc_data()
793
+
794
+ def _on_failure(self):
795
+
796
+ self.rhc_status.fails.write_retry(True,
797
+ row_index=self.controller_index,
798
+ col_index=0,
799
+ row_index_view=0)
800
+ self._deactivate()
801
+ self._n_fails += 1
802
+ self.rhc_status.controllers_fail_counter.write_retry(self._n_fails,
803
+ row_index=self.controller_index,
804
+ col_index=0,
805
+ row_index_view=0)
806
+
807
+ def _init_robot_homer(self):
808
+ self._homer = RobotHomer(srdf_path=self.srdf_path,
809
+ jnt_names=self._controller_side_jnt_names)
810
+
811
+ self._homer_env = RobotHomer(srdf_path=self.srdf_path,
812
+ jnt_names=self.robot_state.jnt_names())
813
+
814
+ def _update_profiling_data(self):
815
+
816
+ # updated debug data on shared memory
817
+ # with the latest info available
818
+ self.cluster_stats.solve_loop_dt.write_retry(self._profiling_data_dict["full_solve_dt"],
819
+ row_index=self.controller_index,
820
+ col_index=0,
821
+ row_index_view=0)
822
+ self.cluster_stats.rti_sol_time.write_retry(self._profiling_data_dict["rti_solve_dt"],
823
+ row_index=self.controller_index,
824
+ col_index=0,
825
+ row_index_view=0)
826
+ self.cluster_stats.prb_update_dt.write_retry(self._profiling_data_dict["problem_update_dt"],
827
+ row_index=self.controller_index,
828
+ col_index=0,
829
+ row_index_view=0)
830
+ self.cluster_stats.phase_shift_dt.write_retry(self._profiling_data_dict["phases_shift_dt"],
831
+ row_index=self.controller_index,
832
+ col_index=0,
833
+ row_index_view=0)
834
+ self.cluster_stats.task_ref_update_dt.write_retry(self._profiling_data_dict["task_ref_update"],
835
+ row_index=self.controller_index,
836
+ col_index=0,
837
+ row_index_view=0)
838
+
839
+ def _write_cmds_from_sol(self):
840
+
841
+ # gets data from the solution and updates the view on the shared data
842
+
843
+ # cmds for robot
844
+ # jnts
845
+ self.robot_cmds.jnts_state.set(data=self._get_jnt_q_from_sol(node_idx=self._rhc_cmds_node_idx), data_type="q", robot_idxs=self.controller_index_np)
846
+ self.robot_cmds.jnts_state.set(data=self._get_jnt_v_from_sol(node_idx=self._rhc_cmds_node_idx), data_type="v", robot_idxs=self.controller_index_np)
847
+ self.robot_cmds.jnts_state.set(data=self._get_jnt_a_from_sol(node_idx=self._rhc_cmds_node_idx-1), data_type="a", robot_idxs=self.controller_index_np)
848
+ self.robot_cmds.jnts_state.set(data=self._get_jnt_eff_from_sol(node_idx=self._rhc_cmds_node_idx-1), data_type="eff", robot_idxs=self.controller_index_np)
849
+ # root
850
+ self.robot_cmds.root_state.set(data=self._get_root_full_q_from_sol(node_idx=self._rhc_cmds_node_idx), data_type="q_full", robot_idxs=self.controller_index_np)
851
+ self.robot_cmds.root_state.set(data=self._get_root_twist_from_sol(node_idx=self._rhc_cmds_node_idx), data_type="twist", robot_idxs=self.controller_index_np)
852
+ self.robot_cmds.root_state.set(data=self._get_root_a_from_sol(node_idx=self._rhc_cmds_node_idx-1), data_type="a_full", robot_idxs=self.controller_index_np)
853
+ self.robot_cmds.root_state.set(data=self._get_norm_grav_vector_from_sol(node_idx=self._rhc_cmds_node_idx-1), data_type="gn", robot_idxs=self.controller_index_np)
854
+ f_contact = self._get_f_from_sol()
855
+ if f_contact is not None:
856
+ contact_names = self.robot_state.contact_names()
857
+ node_idx_f_estimate=self._rhc_cmds_node_idx-1 # we always write the force to reach the desired state (prev node)
858
+ rhc_q_estimate=self._get_root_full_q_from_sol(node_idx=node_idx_f_estimate)[:, 3:7]
859
+ for i in range(len(contact_names)):
860
+ contact = contact_names[i]
861
+ contact_idx = i*3
862
+ contact_force_rhc_world=f_contact[contact_idx:(contact_idx+3), node_idx_f_estimate:node_idx_f_estimate+1].T
863
+ world2base_frame(v_w=contact_force_rhc_world,
864
+ q_b=rhc_q_estimate,
865
+ v_out=self._contact_force_base_loc_aux,
866
+ is_q_wijk=False # horizon q is ijkw
867
+ )
868
+ self.robot_cmds.contact_wrenches.set(data=self._contact_force_base_loc_aux,
869
+ data_type="f",
870
+ robot_idxs=self.controller_index_np,
871
+ contact_name=contact)
872
+
873
+ # prediction data from MPC horizon
874
+ self.robot_pred.jnts_state.set(data=self._get_jnt_q_from_sol(node_idx=self._pred_node_idx), data_type="q", robot_idxs=self.controller_index_np)
875
+ self.robot_pred.jnts_state.set(data=self._get_jnt_v_from_sol(node_idx=self._pred_node_idx), data_type="v", robot_idxs=self.controller_index_np)
876
+ self.robot_pred.jnts_state.set(data=self._get_jnt_a_from_sol(node_idx=self._pred_node_idx-1), data_type="a", robot_idxs=self.controller_index_np)
877
+ self.robot_pred.jnts_state.set(data=self._get_jnt_eff_from_sol(node_idx=self._pred_node_idx-1), data_type="eff", robot_idxs=self.controller_index_np)
878
+ self.robot_pred.root_state.set(data=self._get_root_full_q_from_sol(node_idx=self._pred_node_idx), data_type="q_full", robot_idxs=self.controller_index_np)
879
+ self.robot_pred.root_state.set(data=self._get_root_twist_from_sol(node_idx=self._pred_node_idx), data_type="twist", robot_idxs=self.controller_index_np)
880
+ self.robot_pred.root_state.set(data=self._get_root_a_from_sol(node_idx=self._pred_node_idx-1), data_type="a_full", robot_idxs=self.controller_index_np)
881
+ self.robot_pred.root_state.set(data=self._get_norm_grav_vector_from_sol(node_idx=self._pred_node_idx-1), data_type="gn", robot_idxs=self.controller_index_np)
882
+
883
+ # write robot commands
884
+ self.robot_cmds.jnts_state.synch_retry(row_index=self.controller_index, col_index=0,
885
+ row_index_view=0,
886
+ n_rows=1, n_cols=self.robot_cmds.jnts_state.n_cols,
887
+ read=False) # jnt state
888
+ self.robot_cmds.root_state.synch_retry(row_index=self.controller_index, col_index=0,
889
+ row_index_view=0,
890
+ n_rows=1, n_cols=self.robot_cmds.root_state.n_cols,
891
+ read=False) # root state, in case it was written
892
+ self.robot_cmds.contact_wrenches.synch_retry(row_index=self.controller_index, col_index=0,
893
+ row_index_view=0,
894
+ n_rows=1, n_cols=self.robot_cmds.contact_wrenches.n_cols,
895
+ read=False) # contact state
896
+
897
+ # write robot pred
898
+ self.robot_pred.jnts_state.synch_retry(row_index=self.controller_index, col_index=0,
899
+ row_index_view=0,
900
+ n_rows=1, n_cols=self.robot_cmds.jnts_state.n_cols,
901
+ read=False)
902
+ self.robot_pred.root_state.synch_retry(row_index=self.controller_index, col_index=0,
903
+ row_index_view=0,
904
+ n_rows=1, n_cols=self.robot_cmds.root_state.n_cols,
905
+ read=False)
906
+
907
+ # we also fill other data (cost, constr. violation, etc..)
908
+ self.rhc_status.rhc_cost.write_retry(self._get_rhc_cost(),
909
+ row_index=self.controller_index,
910
+ col_index=0,
911
+ row_index_view=0)
912
+ self.rhc_status.rhc_constr_viol.write_retry(self._get_rhc_constr_viol(),
913
+ row_index=self.controller_index,
914
+ col_index=0,
915
+ row_index_view=0)
916
+ self.rhc_status.rhc_n_iter.write_retry(self._get_rhc_niter_to_sol(),
917
+ row_index=self.controller_index,
918
+ col_index=0,
919
+ row_index_view=0)
920
+ self.rhc_status.rhc_nodes_cost.write_retry(data=self._get_rhc_nodes_cost(),
921
+ row_index=self.controller_index,
922
+ col_index=0,
923
+ row_index_view=0)
924
+ self.rhc_status.rhc_nodes_constr_viol.write_retry(data=self._get_rhc_nodes_constr_viol(),
925
+ row_index=self.controller_index,
926
+ col_index=0,
927
+ row_index_view=0)
928
+ self.rhc_status.rhc_fail_idx.write_retry(self._get_failure_index(),
929
+ row_index=self.controller_index,
930
+ col_index=0,
931
+ row_index_view=0) # write idx on shared mem
932
+
933
+ def _compute_pred_delta(self):
934
+
935
+ # measurements
936
+ q_full_root_meas = self.robot_state.root_state.get(data_type="q_full", robot_idxs=self.controller_index_np)
937
+ twist_root_meas = self.robot_state.root_state.get(data_type="twist", robot_idxs=self.controller_index_np)
938
+ a_root_meas = self.robot_state.root_state.get(data_type="a_full", robot_idxs=self.controller_index_np)
939
+ g_vec_root_meas = self.robot_state.root_state.get(data_type="gn", robot_idxs=self.controller_index_np)
940
+
941
+ q_jnts_meas = self.robot_state.jnts_state.get(data_type="q", robot_idxs=self.controller_index_np)
942
+ v_jnts_meas = self.robot_state.jnts_state.get(data_type="v", robot_idxs=self.controller_index_np)
943
+ a_jnts_meas = self.robot_state.jnts_state.get(data_type="a", robot_idxs=self.controller_index_np)
944
+ eff_jnts_meas = self.robot_state.jnts_state.get(data_type="eff", robot_idxs=self.controller_index_np)
945
+
946
+ # prediction from rhc
947
+ delta_root_q_full=self._get_root_full_q_from_sol(node_idx=1)-q_full_root_meas
948
+ delta_root_twist=self._get_root_twist_from_sol(node_idx=1)-twist_root_meas
949
+ delta_root_a=self._get_root_a_from_sol(node_idx=0)-a_root_meas
950
+ delta_g_vec=self._get_norm_grav_vector_from_sol(node_idx=0)-g_vec_root_meas
951
+
952
+ delta_jnts_q=self._get_jnt_q_from_sol(node_idx=1)-q_jnts_meas
953
+ delta_jnts_v=self._get_jnt_v_from_sol(node_idx=1)-v_jnts_meas
954
+ delta_jnts_a=self._get_jnt_a_from_sol(node_idx=0)-a_jnts_meas
955
+ delta_jnts_eff=self._get_jnt_eff_from_sol(node_idx=0)-eff_jnts_meas
956
+
957
+ # writing pred. errors
958
+ self.rhc_pred_delta.root_state.set(data=delta_root_q_full, data_type="q_full", robot_idxs=self.controller_index_np)
959
+ self.rhc_pred_delta.root_state.set(data=delta_root_twist,data_type="twist", robot_idxs=self.controller_index_np)
960
+ self.rhc_pred_delta.root_state.set(data=delta_root_a,data_type="a_full", robot_idxs=self.controller_index_np)
961
+ self.rhc_pred_delta.root_state.set(data=delta_g_vec, data_type="gn", robot_idxs=self.controller_index_np)
962
+
963
+ self.rhc_pred_delta.jnts_state.set(data=delta_jnts_q,data_type="q", robot_idxs=self.controller_index_np)
964
+ self.rhc_pred_delta.jnts_state.set(data=delta_jnts_v,data_type="v", robot_idxs=self.controller_index_np)
965
+ self.rhc_pred_delta.jnts_state.set(data=delta_jnts_a,data_type="a", robot_idxs=self.controller_index_np)
966
+ self.rhc_pred_delta.jnts_state.set(data=delta_jnts_eff, data_type="eff", robot_idxs=self.controller_index_np)
967
+
968
+ # write on shared memory
969
+ self.rhc_pred_delta.jnts_state.synch_retry(row_index=self.controller_index,
970
+ col_index=0,
971
+ n_rows=1,
972
+ row_index_view=0,
973
+ n_cols=self.robot_cmds.jnts_state.n_cols,
974
+ read=False) # jnt state
975
+ self.rhc_pred_delta.root_state.synch_retry(row_index=self.controller_index,
976
+ col_index=0,
977
+ n_rows=1,
978
+ row_index_view=0,
979
+ n_cols=self.robot_cmds.root_state.n_cols,
980
+ read=False) # root state
981
+
982
+ def _assign_controller_side_jnt_names(self,
983
+ jnt_names: List[str]):
984
+
985
+ self._controller_side_jnt_names = jnt_names
986
+ self._got_jnt_names_from_controllers = True
987
+
988
+ def _check_jnt_names_compatibility(self):
989
+
990
+ set_rhc = set(self._controller_side_jnt_names)
991
+ set_env = set(self._env_side_jnt_names)
992
+
993
+ if not set_rhc == set_env:
994
+ rhc_is_missing=set_env-set_rhc
995
+ env_is_missing=set_rhc-set_env
996
+
997
+ msg_type=LogType.WARN
998
+ message=""
999
+ if not len(rhc_is_missing)==0: # allowed
1000
+ message = "\nSome env-side joint names are missing on rhc-side!\n" + \
1001
+ "ENV-SIDE-> \n" + \
1002
+ " ".join(self._env_side_jnt_names) + "\n" +\
1003
+ "RHC-SIDE -> \n" + \
1004
+ " ".join(self._controller_side_jnt_names) + "\n" \
1005
+ "\MISSING -> \n" + \
1006
+ " ".join(list(rhc_is_missing)) + "\n"
1007
+ if not self._allow_less_jnts: # raise exception
1008
+ msg_type=LogType.EXCEP
1009
+
1010
+ if not len(env_is_missing)==0: # not allowed
1011
+ message = "\nSome rhc-side joint names are missing on env-side!\n" + \
1012
+ "ENV-SIDE-> \n" + \
1013
+ " ".join(self._env_side_jnt_names) + \
1014
+ "RHC-SIDE -> \n" + \
1015
+ " ".join(self._controller_side_jnt_names) + "\n" \
1016
+ "\nmissing -> \n" + \
1017
+ " ".join(list(env_is_missing))
1018
+ msg_type=LogType.EXCEP
1019
+
1020
+ Journal.log(self._class_name_base,
1021
+ "_check_jnt_names_compatibility",
1022
+ message,
1023
+ msg_type,
1024
+ throw_when_excep = True)
1025
+
1026
+ def _get_cost_data(self):
1027
+ # to be overridden by child class
1028
+ return None, None
1029
+
1030
+ def _get_constr_data(self):
1031
+ # to be overridden by child class
1032
+ return None, None
1033
+
1034
+ def _get_fail_idx(self):
1035
+ # to be overriden by parent
1036
+ return 0.0
1037
+
1038
+ def _get_failure_index(self):
1039
+ fail_idx=self._get_fail_idx()/self._fail_idx_thresh
1040
+ if fail_idx>1.0:
1041
+ fail_idx=1.0
1042
+ return fail_idx
1043
+
1044
+ def _check_rhc_failure(self):
1045
+ # we use fail idx viol to detect failures
1046
+ idx = self._get_failure_index()
1047
+ return idx>=1.0
1048
+
1049
+ def _update_rhc_internal(self):
1050
+ # data which is not enabled in the config is not actually
1051
+ # written so overhead is minimal for non-enabled data
1052
+ self.rhc_internal.write_q(data= self._get_q_from_sol(),
1053
+ retry=True)
1054
+ self.rhc_internal.write_v(data= self._get_v_from_sol(),
1055
+ retry=True)
1056
+ self.rhc_internal.write_a(data= self._get_a_from_sol(),
1057
+ retry=True)
1058
+ self.rhc_internal.write_a_dot(data= self._get_a_dot_from_sol(),
1059
+ retry=True)
1060
+ self.rhc_internal.write_f(data= self._get_f_from_sol(),
1061
+ retry=True)
1062
+ self.rhc_internal.write_f_dot(data= self._get_f_dot_from_sol(),
1063
+ retry=True)
1064
+ self.rhc_internal.write_eff(data= self._get_eff_from_sol(),
1065
+ retry=True)
1066
+ for cost_idx in range(self.rhc_internal.config.n_costs):
1067
+ # iterate over all costs and update all values
1068
+ cost_name = self.rhc_internal.config.cost_names[cost_idx]
1069
+ self.rhc_internal.write_cost(data= self._get_cost_from_sol(cost_name = cost_name),
1070
+ cost_name = cost_name,
1071
+ retry=True)
1072
+ for constr_idx in range(self.rhc_internal.config.n_constr):
1073
+ # iterate over all constraints and update all values
1074
+ constr_name = self.rhc_internal.config.constr_names[constr_idx]
1075
+ self.rhc_internal.write_constr(data= self._get_constr_from_sol(constr_name=constr_name),
1076
+ constr_name = constr_name,
1077
+ retry=True)
1078
+
1079
+ def _get_contacts(self):
1080
+ contact_names = self._get_contact_names()
1081
+ self._got_contact_names = True
1082
+ return contact_names
1083
+
1084
+ def _get_q_from_sol(self):
1085
+ # to be overridden by child class
1086
+ return None
1087
+
1088
+ def _get_v_from_sol(self):
1089
+ # to be overridden by child class
1090
+ return None
1091
+
1092
+ def _get_a_from_sol(self):
1093
+ # to be overridden by child class
1094
+ return None
1095
+
1096
+ def _get_a_dot_from_sol(self):
1097
+ # to be overridden by child class
1098
+ return None
1099
+
1100
+ def _get_f_from_sol(self):
1101
+ # to be overridden by child class
1102
+ return None
1103
+
1104
+ def _get_f_dot_from_sol(self):
1105
+ # to be overridden by child class
1106
+ return None
1107
+
1108
+ def _get_eff_from_sol(self):
1109
+ # to be overridden by child class
1110
+ return None
1111
+
1112
+ def _get_cost_from_sol(self,
1113
+ cost_name: str):
1114
+ # to be overridden by child class
1115
+ return None
1116
+
1117
+ def _get_constr_from_sol(self,
1118
+ constr_name: str):
1119
+ # to be overridden by child class
1120
+ return None
1121
+
1122
+ @abstractmethod
1123
+ def _reset(self):
1124
+ pass
1125
+
1126
+ @abstractmethod
1127
+ def _init_rhc_task_cmds(self):
1128
+ pass
1129
+
1130
+ @abstractmethod
1131
+ def _get_robot_jnt_names(self):
1132
+ pass
1133
+
1134
+ @abstractmethod
1135
+ def _get_contact_names(self):
1136
+ pass
1137
+
1138
+ @abstractmethod
1139
+ def _get_jnt_q_from_sol(self, node_idx=1) -> np.ndarray:
1140
+ pass
1141
+
1142
+ @abstractmethod
1143
+ def _get_jnt_v_from_sol(self, node_idx=1) -> np.ndarray:
1144
+ pass
1145
+
1146
+ @abstractmethod
1147
+ def _get_jnt_a_from_sol(self, node_idx=0) -> np.ndarray:
1148
+ pass
1149
+
1150
+ @abstractmethod
1151
+ def _get_jnt_eff_from_sol(self, node_idx=0) -> np.ndarray:
1152
+ pass
1153
+
1154
+ @abstractmethod
1155
+ def _get_root_full_q_from_sol(self, node_idx=1) -> np.ndarray:
1156
+ pass
1157
+
1158
+ @abstractmethod
1159
+ def _get_full_q_from_sol(self, node_idx=1) -> np.ndarray:
1160
+ pass
1161
+
1162
+ @abstractmethod
1163
+ def _get_root_twist_from_sol(self, node_idx=1) -> np.ndarray:
1164
+ pass
1165
+
1166
+ @abstractmethod
1167
+ def _get_root_a_from_sol(self, node_idx=0) -> np.ndarray:
1168
+ pass
1169
+
1170
+ def _get_norm_grav_vector_from_sol(self, node_idx=1) -> np.ndarray:
1171
+ rhc_q=self._get_root_full_q_from_sol(node_idx=node_idx)[:, 3:7]
1172
+ world2base_frame(v_w=self._norm_grav_vector_w,q_b=rhc_q,v_out=self._norm_grav_vector_base_loc,
1173
+ is_q_wijk=False)
1174
+ return self._norm_grav_vector_base_loc
1175
+
1176
+ def _get_rhc_cost(self):
1177
+ # to be overridden
1178
+ return np.nan
1179
+
1180
+ def _get_rhc_constr_viol(self):
1181
+ # to be overridden
1182
+ return np.nan
1183
+
1184
+ def _get_rhc_nodes_cost(self):
1185
+ # to be overridden
1186
+ return np.zeros((1,self.rhc_status.n_nodes), dtype=self._dtype)
1187
+
1188
+ def _get_rhc_nodes_constr_viol(self):
1189
+ # to be overridden
1190
+ return np.zeros((1,self.rhc_status.n_nodes), dtype=self._dtype)
1191
+
1192
+ def _get_rhc_niter_to_sol(self) -> np.ndarray:
1193
+ # to be overridden
1194
+ return np.nan
1195
+
1196
+ @abstractmethod
1197
+ def _update_open_loop(self):
1198
+ # updates rhc controller
1199
+ # using the internal state
1200
+ pass
1201
+
1202
+ @abstractmethod
1203
+ def _update_closed_loop(self):
1204
+ # uses meas. from robot
1205
+ pass
1206
+
1207
+ @abstractmethod
1208
+ def _solve(self) -> bool:
1209
+ pass
1210
+
1211
+ @abstractmethod
1212
+ def _get_ndofs(self):
1213
+ pass
1214
+
1215
+ abstractmethod
1216
+ def _get_robot_mass(self):
1217
+ pass
1218
+
1219
+ @abstractmethod
1220
+ def _init_problem(self):
1221
+ # initialized horizon's TO problem
1222
+ pass
1223
+
1224
+ @abstractmethod
1225
+ def _post_problem_init(self):
1226
+ pass
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/sac.py ADDED
@@ -0,0 +1,680 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from torch.distributions.normal import Normal
4
+ import math
5
+
6
+ from aug_mpc.utils.nn.normalization_utils import RunningNormalizer
7
+ from aug_mpc.utils.nn.layer_utils import llayer_init
8
+
9
+ from typing import List
10
+
11
+ from EigenIPC.PyEigenIPC import LogType
12
+ from EigenIPC.PyEigenIPC import Journal
13
+ from EigenIPC.PyEigenIPC import VLevel
14
+
15
+ class SACAgent(nn.Module):
16
+ def __init__(self,
17
+ obs_dim: int,
18
+ actions_dim: int,
19
+ obs_ub: List[float] = None,
20
+ obs_lb: List[float] = None,
21
+ actions_ub: List[float] = None,
22
+ actions_lb: List[float] = None,
23
+ rescale_obs: bool = False,
24
+ norm_obs: bool = True,
25
+ use_action_rescale_for_critic: bool = True,
26
+ device:str="cuda",
27
+ dtype=torch.float32,
28
+ is_eval:bool=False,
29
+ load_qf:bool=False,
30
+ epsilon:float=1e-8,
31
+ debug:bool=False,
32
+ compression_ratio:float=-1.0, # > 0; if [0, 1] compression, >1 "expansion"
33
+ layer_width_actor:int=256,
34
+ n_hidden_layers_actor:int=2,
35
+ layer_width_critic:int=512,
36
+ n_hidden_layers_critic:int=4,
37
+ torch_compile: bool = False,
38
+ add_weight_norm: bool = False,
39
+ add_layer_norm: bool = False,
40
+ add_batch_norm: bool = False):
41
+
42
+ super().__init__()
43
+
44
+ self._use_torch_compile=torch_compile
45
+
46
+ self._layer_width_actor=layer_width_actor
47
+ self._layer_width_critic=layer_width_critic
48
+ self._n_hidden_layers_actor=n_hidden_layers_actor
49
+ self._n_hidden_layers_critic=n_hidden_layers_critic
50
+
51
+ self._obs_dim=obs_dim
52
+ self._actions_dim=actions_dim
53
+ self._actions_ub=actions_ub
54
+ self._actions_lb=actions_lb
55
+
56
+ self._add_weight_norm=add_weight_norm
57
+ self._add_layer_norm=add_layer_norm
58
+ self._add_batch_norm=add_batch_norm
59
+
60
+ self._is_eval=is_eval
61
+ self._load_qf=load_qf
62
+
63
+ self._epsilon=epsilon
64
+
65
+ if compression_ratio > 0.0:
66
+ self._layer_width_actor=int(compression_ratio*obs_dim)
67
+ self._layer_width_critic=int(compression_ratio*(obs_dim+actions_dim))
68
+
69
+ self._normalize_obs = norm_obs
70
+ self._rescale_obs=rescale_obs
71
+ if self._rescale_obs and self._normalize_obs:
72
+ Journal.log(self.__class__.__name__,
73
+ "__init__",
74
+ f"Both running normalization and obs rescaling is enabled. Was this intentional?",
75
+ LogType.WARN,
76
+ throw_when_excep = True)
77
+
78
+ self._use_action_rescale_for_critic=use_action_rescale_for_critic
79
+
80
+ self._rescaling_epsi=1e-9
81
+
82
+ self._debug = debug
83
+
84
+ self._torch_device = device
85
+ self._torch_dtype = dtype
86
+
87
+ # obs scale and bias
88
+ if obs_ub is None:
89
+ obs_ub = [1] * obs_dim
90
+ if obs_lb is None:
91
+ obs_lb = [-1] * obs_dim
92
+ if (len(obs_ub) != obs_dim):
93
+ Journal.log(self.__class__.__name__,
94
+ "__init__",
95
+ f"Observations ub list length should be equal to {obs_dim}, but got {len(obs_ub)}",
96
+ LogType.EXCEP,
97
+ throw_when_excep = True)
98
+ if (len(obs_lb) != obs_dim):
99
+ Journal.log(self.__class__.__name__,
100
+ "__init__",
101
+ f"Observations lb list length should be equal to {obs_dim}, but got {len(obs_lb)}",
102
+ LogType.EXCEP,
103
+ throw_when_excep = True)
104
+
105
+ self._obs_ub = torch.tensor(obs_ub, dtype=self._torch_dtype,
106
+ device=self._torch_device)
107
+ self._obs_lb = torch.tensor(obs_lb, dtype=self._torch_dtype,
108
+ device=self._torch_device)
109
+ obs_scale = torch.full((obs_dim, ),
110
+ fill_value=0.0,
111
+ dtype=self._torch_dtype,
112
+ device=self._torch_device)
113
+ obs_scale[:] = (self._obs_ub-self._obs_lb)/2.0
114
+ self.register_buffer(
115
+ "obs_scale", obs_scale
116
+ )
117
+ obs_bias = torch.full((obs_dim, ),
118
+ fill_value=0.0,
119
+ dtype=self._torch_dtype,
120
+ device=self._torch_device)
121
+ obs_bias[:] = (self._obs_ub+self._obs_lb)/2.0
122
+ self.register_buffer(
123
+ "obs_bias", obs_bias)
124
+
125
+ self._build_nets()
126
+
127
+ self._init_obs_norm()
128
+
129
+ msg=f"Created SAC agent with actor [{self._layer_width_actor}, {self._n_hidden_layers_actor}]\
130
+ and critic [{self._layer_width_critic}, {self._n_hidden_layers_critic}] sizes.\
131
+ \n Runningobs normalizer: {type(self.obs_running_norm)} \
132
+ \n Batch normalization: {self._add_batch_norm} \
133
+ \n Layer normalization: {self._add_layer_norm} \
134
+ \n Weight normalization: {self._add_weight_norm} \
135
+ \n Critic input actions are descaled: {self._use_action_rescale_for_critic}"
136
+ Journal.log(self.__class__.__name__,
137
+ "__init__",
138
+ msg,
139
+ LogType.INFO)
140
+
141
+ def _init_obs_norm(self):
142
+
143
+ self.obs_running_norm=None
144
+ if self._normalize_obs:
145
+ self.obs_running_norm = RunningNormalizer((self._obs_dim,),
146
+ epsilon=self._epsilon,
147
+ device=self._torch_device, dtype=self._torch_dtype,
148
+ freeze_stats=True, # always start with freezed stats
149
+ debug=self._debug)
150
+ self.obs_running_norm.type(self._torch_dtype) # ensuring correct dtype for whole module
151
+
152
+ def _build_nets(self):
153
+
154
+ if self._add_weight_norm:
155
+ Journal.log(self.__class__.__name__,
156
+ "__init__",
157
+ f"Will use weight normalization reparametrization\n",
158
+ LogType.INFO)
159
+
160
+ self.actor=None
161
+ self.qf1=None
162
+ self.qf2=None
163
+ self.qf1_target=None
164
+ self.qf2_target=None
165
+
166
+ self.actor = Actor(obs_dim=self._obs_dim,
167
+ actions_dim=self._actions_dim,
168
+ actions_ub=self._actions_ub,
169
+ actions_lb=self._actions_lb,
170
+ device=self._torch_device,
171
+ dtype=self._torch_dtype,
172
+ layer_width=self._layer_width_actor,
173
+ n_hidden_layers=self._n_hidden_layers_actor,
174
+ add_weight_norm=self._add_weight_norm,
175
+ add_layer_norm=self._add_layer_norm,
176
+ add_batch_norm=self._add_batch_norm,
177
+ )
178
+
179
+ if (not self._is_eval) or self._load_qf: # just needed for training or during eval
180
+ # for debug, if enabled
181
+ self.qf1 = CriticQ(obs_dim=self._obs_dim,
182
+ actions_dim=self._actions_dim,
183
+ device=self._torch_device,
184
+ dtype=self._torch_dtype,
185
+ layer_width=self._layer_width_critic,
186
+ n_hidden_layers=self._n_hidden_layers_critic,
187
+ add_weight_norm=self._add_weight_norm,
188
+ add_layer_norm=self._add_layer_norm,
189
+ add_batch_norm=self._add_batch_norm)
190
+ self.qf1_target = CriticQ(obs_dim=self._obs_dim,
191
+ actions_dim=self._actions_dim,
192
+ device=self._torch_device,
193
+ dtype=self._torch_dtype,
194
+ layer_width=self._layer_width_critic,
195
+ n_hidden_layers=self._n_hidden_layers_critic,
196
+ add_weight_norm=self._add_weight_norm,
197
+ add_layer_norm=self._add_layer_norm,
198
+ add_batch_norm=self._add_batch_norm)
199
+
200
+ self.qf2 = CriticQ(obs_dim=self._obs_dim,
201
+ actions_dim=self._actions_dim,
202
+ device=self._torch_device,
203
+ dtype=self._torch_dtype,
204
+ layer_width=self._layer_width_critic,
205
+ n_hidden_layers=self._n_hidden_layers_critic,
206
+ add_weight_norm=self._add_weight_norm,
207
+ add_layer_norm=self._add_layer_norm,
208
+ add_batch_norm=self._add_batch_norm)
209
+ self.qf2_target = CriticQ(obs_dim=self._obs_dim,
210
+ actions_dim=self._actions_dim,
211
+ device=self._torch_device,
212
+ dtype=self._torch_dtype,
213
+ layer_width=self._layer_width_critic,
214
+ n_hidden_layers=self._n_hidden_layers_critic,
215
+ add_weight_norm=self._add_weight_norm,
216
+ add_layer_norm=self._add_layer_norm,
217
+ add_batch_norm=self._add_batch_norm)
218
+
219
+ self.qf1_target.load_state_dict(self.qf1.state_dict())
220
+ self.qf2_target.load_state_dict(self.qf2.state_dict())
221
+
222
+ if self._use_torch_compile:
223
+ self.obs_running_norm=torch.compile(self.obs_running_norm)
224
+ self.actor = torch.compile(self.actor)
225
+ if (not self._is_eval) or self._load_qf:
226
+ self.qf1 = torch.compile(self.qf1)
227
+ self.qf2 = torch.compile(self.qf2)
228
+ self.qf1_target = torch.compile(self.qf1_target)
229
+ self.qf2_target = torch.compile(self.qf2_target)
230
+
231
+ def reset(self, reset_stats: bool = False):
232
+ # we should just reinitialize the parameters, but for easiness
233
+ # we recreate the networks
234
+
235
+ # force deallocation of objects
236
+ import gc
237
+ del self.actor
238
+ del self.qf1
239
+ del self.qf2
240
+ del self.qf1_target
241
+ del self.qf2_target
242
+ gc.collect()
243
+
244
+ self._build_nets()
245
+
246
+ if reset_stats: # we also reinitialize obs norm
247
+ self._init_obs_norm()
248
+
249
+ # self.obs_running_norm.reset()
250
+
251
+ def layer_width_actor(self):
252
+ return self._layer_width_actor
253
+
254
+ def n_hidden_layers_actor(self):
255
+ return self._n_hidden_layers_actor
256
+
257
+ def layer_width_critic(self):
258
+ return self._layer_width_critic
259
+
260
+ def n_hidden_layers_critic(self):
261
+ return self._n_hidden_layers_critic
262
+
263
+ def get_impl_path(self):
264
+ import os
265
+ return os.path.abspath(__file__)
266
+
267
+ def update_obs_bnorm(self, x):
268
+ self.obs_running_norm.unfreeze()
269
+ self.obs_running_norm.manual_stat_update(x)
270
+ self.obs_running_norm.freeze()
271
+
272
+ def _obs_scaling_layer(self, x):
273
+ x=(x-self.obs_bias)
274
+ x=x/(self.obs_scale+self._rescaling_epsi)
275
+ return x
276
+
277
+ def _preprocess_obs(self, x):
278
+ if self._rescale_obs:
279
+ x = self._obs_scaling_layer(x)
280
+ if self.obs_running_norm is not None:
281
+ x = self.obs_running_norm(x)
282
+ return x
283
+
284
+ def _preprocess_actions(self, a):
285
+ if self._use_action_rescale_for_critic:
286
+ a=self.actor.remove_scaling(a=a) # rescale to be in range [-1, 1]
287
+ return a
288
+
289
+ def get_action(self, x):
290
+ x = self._preprocess_obs(x)
291
+ return self.actor.get_action(x)
292
+
293
+ def get_qf1_val(self, x, a):
294
+ x = self._preprocess_obs(x)
295
+ a = self._preprocess_actions(a)
296
+ return self.qf1(x, a)
297
+
298
+ def get_qf2_val(self, x, a):
299
+ x = self._preprocess_obs(x)
300
+ a = self._preprocess_actions(a)
301
+ return self.qf2(x, a)
302
+
303
+ def get_qf1t_val(self, x, a):
304
+ x = self._preprocess_obs(x)
305
+ a = self._preprocess_actions(a)
306
+ return self.qf1_target(x, a)
307
+
308
+ def get_qf2t_val(self, x, a):
309
+ x = self._preprocess_obs(x)
310
+ a = self._preprocess_actions(a)
311
+ return self.qf2_target(x, a)
312
+
313
+ def load_state_dict(self, param_dict):
314
+
315
+ missing, unexpected = super().load_state_dict(param_dict,
316
+ strict=False)
317
+ if not len(missing)==0:
318
+ Journal.log(self.__class__.__name__,
319
+ "load_state_dict",
320
+ f"These parameters are missing from the provided state dictionary: {str(missing)}\n",
321
+ LogType.EXCEP,
322
+ throw_when_excep = True)
323
+ if not len(unexpected)==0:
324
+ Journal.log(self.__class__.__name__,
325
+ "load_state_dict",
326
+ f"These parameters present in the provided state dictionary are not needed: {str(unexpected)}\n",
327
+ LogType.WARN)
328
+
329
+ # sanity check on running normalizer
330
+ import re
331
+ running_norm_pattern = r"running_norm"
332
+ error=f"Found some keys in model state dictionary associated with a running normalizer. Are you running the agent with norm_obs=True?\n"
333
+ if any(re.match(running_norm_pattern, key) for key in unexpected):
334
+ Journal.log(self.__class__.__name__,
335
+ "load_state_dict",
336
+ error,
337
+ LogType.EXCEP,
338
+ throw_when_excep=True)
339
+
340
+ class CriticQ(nn.Module):
341
+ def __init__(self,
342
+ obs_dim: int,
343
+ actions_dim: int,
344
+ device: str = "cuda",
345
+ dtype = torch.float32,
346
+ layer_width: int = 512,
347
+ n_hidden_layers: int = 4,
348
+ add_weight_norm: bool = False,
349
+ add_layer_norm: bool = False,
350
+ add_batch_norm: bool = False):
351
+
352
+ super().__init__()
353
+
354
+ self._lrelu_slope=0.01
355
+
356
+ self._torch_device = device
357
+ self._torch_dtype = dtype
358
+
359
+ self._obs_dim = obs_dim
360
+ self._actions_dim = actions_dim
361
+ self._q_net_dim = self._obs_dim + self._actions_dim
362
+
363
+ self._first_hidden_layer_width=self._q_net_dim # fist layer fully connected and of same dim
364
+ # as input
365
+
366
+ init_type="kaiming_uniform" # maintains the variance of activations throughout the network
367
+ nonlinearity="leaky_relu" # suited for kaiming
368
+
369
+ # Input layer
370
+ layers=llayer_init(
371
+ layer=nn.Linear(self._q_net_dim, self._first_hidden_layer_width),
372
+ init_type=init_type,
373
+ nonlinearity=nonlinearity,
374
+ a_leaky_relu=self._lrelu_slope,
375
+ device=self._torch_device,
376
+ dtype=self._torch_dtype,
377
+ add_weight_norm=add_weight_norm,
378
+ add_layer_norm=add_layer_norm,
379
+ add_batch_norm=add_batch_norm,
380
+ uniform_biases=False, # constant bias init
381
+ bias_const=0.0
382
+ )
383
+ layers.extend([nn.LeakyReLU(negative_slope=self._lrelu_slope)])
384
+
385
+ # Hidden layers
386
+ layers.extend(
387
+ llayer_init(
388
+ layer=nn.Linear(self._first_hidden_layer_width, layer_width),
389
+ init_type=init_type,
390
+ nonlinearity=nonlinearity,
391
+ a_leaky_relu=self._lrelu_slope,
392
+ device=self._torch_device,
393
+ dtype=self._torch_dtype,
394
+ add_weight_norm=add_weight_norm,
395
+ add_layer_norm=add_layer_norm,
396
+ add_batch_norm=add_batch_norm,
397
+ uniform_biases=False, # constant bias init
398
+ bias_const=0.0
399
+ )
400
+ )
401
+ layers.extend([nn.LeakyReLU(negative_slope=self._lrelu_slope)])
402
+
403
+ for _ in range(n_hidden_layers - 1):
404
+ layers.extend(
405
+ llayer_init(
406
+ layer=nn.Linear(layer_width, layer_width),
407
+ init_type=init_type,
408
+ nonlinearity=nonlinearity,
409
+ a_leaky_relu=self._lrelu_slope,
410
+ device=self._torch_device,
411
+ dtype=self._torch_dtype,
412
+ add_weight_norm=add_weight_norm,
413
+ add_layer_norm=add_layer_norm,
414
+ add_batch_norm=add_batch_norm,
415
+ uniform_biases=False, # constant bias init
416
+ bias_const=0.0
417
+ )
418
+ )
419
+ layers.extend([nn.LeakyReLU(negative_slope=self._lrelu_slope)])
420
+
421
+ # Output layer
422
+ layers.extend(
423
+ llayer_init(
424
+ layer=nn.Linear(layer_width, 1),
425
+ init_type="uniform",
426
+ uniform_biases=False, # contact biases
427
+ bias_const=-0.1, # negative to prevent overestimation
428
+ scale_weight=1e-2,
429
+ device=self._torch_device,
430
+ dtype=self._torch_dtype,
431
+ add_weight_norm=False,
432
+ add_layer_norm=False,
433
+ add_batch_norm=False
434
+ )
435
+ )
436
+
437
+ # Creating the full sequential network
438
+ self._q_net = nn.Sequential(*layers)
439
+ self._q_net.to(self._torch_device).type(self._torch_dtype)
440
+
441
+ print("Critic architecture")
442
+ print(self._q_net)
443
+
444
+ def get_n_params(self):
445
+ return sum(p.numel() for p in self.parameters())
446
+
447
+ def forward(self, x, a):
448
+ x = torch.cat([x, a], dim=1)
449
+ return self._q_net(x)
450
+
451
+ class Actor(nn.Module):
452
+ def __init__(self,
453
+ obs_dim: int,
454
+ actions_dim: int,
455
+ actions_ub: List[float] = None,
456
+ actions_lb: List[float] = None,
457
+ device: str = "cuda",
458
+ dtype = torch.float32,
459
+ layer_width: int = 256,
460
+ n_hidden_layers: int = 2,
461
+ add_weight_norm: bool = False,
462
+ add_layer_norm: bool = False,
463
+ add_batch_norm: bool = False):
464
+
465
+ super().__init__()
466
+
467
+ self._lrelu_slope=0.01
468
+
469
+ self._torch_device = device
470
+ self._torch_dtype = dtype
471
+
472
+ self._obs_dim = obs_dim
473
+ self._actions_dim = actions_dim
474
+
475
+ self._first_hidden_layer_width=self._obs_dim # fist layer fully connected and of same dim
476
+
477
+ # Action scale and bias
478
+ if actions_ub is None:
479
+ actions_ub = [1] * actions_dim
480
+ if actions_lb is None:
481
+ actions_lb = [-1] * actions_dim
482
+ if (len(actions_ub) != actions_dim):
483
+ Journal.log(self.__class__.__name__,
484
+ "__init__",
485
+ f"Actions ub list length should be equal to {actions_dim}, but got {len(actions_ub)}",
486
+ LogType.EXCEP,
487
+ throw_when_excep = True)
488
+ if (len(actions_lb) != actions_dim):
489
+ Journal.log(self.__class__.__name__,
490
+ "__init__",
491
+ f"Actions lb list length should be equal to {actions_dim}, but got {len(actions_lb)}",
492
+ LogType.EXCEP,
493
+ throw_when_excep = True)
494
+
495
+ self._actions_ub = torch.tensor(actions_ub, dtype=self._torch_dtype,
496
+ device=self._torch_device)
497
+ self._actions_lb = torch.tensor(actions_lb, dtype=self._torch_dtype,
498
+ device=self._torch_device)
499
+ action_scale = torch.full((actions_dim, ),
500
+ fill_value=0.0,
501
+ dtype=self._torch_dtype,
502
+ device=self._torch_device)
503
+ action_scale[:] = (self._actions_ub-self._actions_lb)/2.0
504
+ self.register_buffer(
505
+ "action_scale", action_scale
506
+ )
507
+ actions_bias = torch.full((actions_dim, ),
508
+ fill_value=0.0,
509
+ dtype=self._torch_dtype,
510
+ device=self._torch_device)
511
+ actions_bias[:] = (self._actions_ub+self._actions_lb)/2.0
512
+ self.register_buffer(
513
+ "action_bias", actions_bias)
514
+
515
+ # Network configuration
516
+ self.LOG_STD_MAX = 2
517
+ self.LOG_STD_MIN = -5
518
+
519
+ # Input layer followed by hidden layers
520
+ layers=llayer_init(nn.Linear(self._obs_dim, self._first_hidden_layer_width),
521
+ init_type="kaiming_uniform",
522
+ nonlinearity="leaky_relu",
523
+ a_leaky_relu=self._lrelu_slope,
524
+ device=self._torch_device,
525
+ dtype=self._torch_dtype,
526
+ add_weight_norm=add_weight_norm,
527
+ add_layer_norm=add_layer_norm,
528
+ add_batch_norm=add_batch_norm,
529
+ uniform_biases=False, # constant bias init
530
+ bias_const=0.0
531
+ )
532
+ layers.extend([nn.LeakyReLU(negative_slope=self._lrelu_slope)])
533
+
534
+ # Hidden layers
535
+ layers.extend(
536
+ llayer_init(nn.Linear(self._first_hidden_layer_width, layer_width),
537
+ init_type="kaiming_uniform",
538
+ nonlinearity="leaky_relu",
539
+ a_leaky_relu=self._lrelu_slope,
540
+ device=self._torch_device,
541
+ dtype=self._torch_dtype,
542
+ add_weight_norm=add_weight_norm,
543
+ add_layer_norm=add_layer_norm,
544
+ add_batch_norm=add_batch_norm,
545
+ uniform_biases=False, # constant bias init
546
+ bias_const=0.0)
547
+ )
548
+ layers.extend([nn.LeakyReLU(negative_slope=self._lrelu_slope)])
549
+
550
+ for _ in range(n_hidden_layers - 1):
551
+ layers.extend(
552
+ llayer_init(nn.Linear(layer_width, layer_width),
553
+ init_type="kaiming_uniform",
554
+ nonlinearity="leaky_relu",
555
+ a_leaky_relu=self._lrelu_slope,
556
+ device=self._torch_device,
557
+ dtype=self._torch_dtype,
558
+ add_weight_norm=add_weight_norm,
559
+ add_layer_norm=add_layer_norm,
560
+ add_batch_norm=add_batch_norm,
561
+ uniform_biases=False, # constant bias init
562
+ bias_const=0.0)
563
+ )
564
+ layers.extend([nn.LeakyReLU(negative_slope=self._lrelu_slope)])
565
+
566
+ # Sequential layers for the feature extractor
567
+ self._fc12 = nn.Sequential(*layers)
568
+
569
+ # Mean and log_std layers
570
+ out_fc_mean=llayer_init(nn.Linear(layer_width, self._actions_dim),
571
+ init_type="uniform",
572
+ uniform_biases=False, # constant bias init
573
+ bias_const=0.0,
574
+ scale_weight=1e-3, # scaling (output layer)
575
+ scale_bias=1.0,
576
+ device=self._torch_device,
577
+ dtype=self._torch_dtype,
578
+ add_weight_norm=False,
579
+ add_layer_norm=False,
580
+ add_batch_norm=False
581
+ )
582
+ self.fc_mean = nn.Sequential(*out_fc_mean)
583
+ out_fc_logstd= llayer_init(nn.Linear(layer_width, self._actions_dim),
584
+ init_type="uniform",
585
+ uniform_biases=False,
586
+ bias_const=math.log(0.5),
587
+ scale_weight=1e-3, # scaling (output layer)
588
+ scale_bias=1.0,
589
+ device=self._torch_device,
590
+ dtype=self._torch_dtype,
591
+ add_weight_norm=False,
592
+ add_layer_norm=False,
593
+ add_batch_norm=False,
594
+ )
595
+ self.fc_logstd = nn.Sequential(*out_fc_logstd)
596
+
597
+ # Move all components to the specified device and dtype
598
+ self._fc12.to(device=self._torch_device, dtype=self._torch_dtype)
599
+ self.fc_mean.to(device=self._torch_device, dtype=self._torch_dtype)
600
+ self.fc_logstd.to(device=self._torch_device, dtype=self._torch_dtype)
601
+
602
+ print("Actor architecture")
603
+ print(self._fc12)
604
+ print(self.fc_mean)
605
+ print(self.fc_logstd)
606
+
607
+ def get_n_params(self):
608
+ return sum(p.numel() for p in self.parameters())
609
+
610
+ def forward(self, x):
611
+ x = self._fc12(x)
612
+ mean = self.fc_mean(x)
613
+ log_std = self.fc_logstd(x)
614
+ log_std = torch.tanh(log_std)
615
+ log_std = self.LOG_STD_MIN + 0.5 * (self.LOG_STD_MAX - self.LOG_STD_MIN) * (log_std + 1) # From SpinUp / Denis Yarats
616
+ return mean, log_std
617
+
618
+ def get_action(self, x):
619
+ mean, log_std = self(x)
620
+ std = log_std.exp()
621
+ normal = torch.distributions.Normal(mean, std)
622
+ x_t = normal.rsample() # Reparameterization trick (for SAC we neex action
623
+ # to be differentible since we use Q nets. Using sample() would break the
624
+ # comp. graph and not allow gradients to flow)
625
+ y_t = torch.tanh(x_t)
626
+ action = y_t * self.action_scale + self.action_bias
627
+ log_prob_vec = normal.log_prob(x_t) # per-dimension log prob before tanh
628
+ log_prob_vec = log_prob_vec - torch.log(self.action_scale * (1 - y_t.pow(2)) + 1e-6) # tanh Jacobian + scaling
629
+ log_prob_sum = log_prob_vec.sum(1, keepdim=True)
630
+ mean = torch.tanh(mean) * self.action_scale + self.action_bias
631
+ return action, (log_prob_sum, log_prob_vec), mean
632
+
633
+ def remove_scaling(self, a):
634
+ return (a - self.action_bias)/self.action_scale
635
+
636
+ if __name__ == "__main__":
637
+ device = "cpu" # or "cpu"
638
+ import time
639
+ obs_dim = 273
640
+ agent = SACAgent(
641
+ obs_dim=obs_dim,
642
+ actions_dim=10,
643
+ actions_lb=None,
644
+ actions_ub=None,
645
+ obs_lb=None,
646
+ obs_ub=None,
647
+ rescale_obs=False,
648
+ norm_obs=True,
649
+ use_action_rescale_for_critic=True,
650
+ is_eval=True,
651
+ compression_ratio=0.6,
652
+ layer_width_actor=128,
653
+ layer_width_critic=256,
654
+ n_hidden_layers_actor=3,
655
+ n_hidden_layers_critic=3,
656
+ device=device,
657
+ dtype=torch.float32,
658
+ add_weight_norm=True,
659
+ add_layer_norm=False,
660
+ add_batch_norm=False
661
+ )
662
+
663
+ n_samples = 10000
664
+ random_obs = torch.rand((1, obs_dim), dtype=torch.float32, device=device)
665
+
666
+ if device == "cuda":
667
+ torch.cuda.synchronize()
668
+ start = time.time()
669
+
670
+ for i in range(n_samples):
671
+ actions, _, mean = agent.get_action(x=random_obs)
672
+ actions = actions.detach()
673
+ actions[:, :] = mean.detach()
674
+
675
+ if device == "cuda":
676
+ torch.cuda.synchronize()
677
+ end = time.time()
678
+
679
+ avrg_eval_time = (end - start) / n_samples
680
+ print(f"Average policy evaluation time on {device}: {avrg_eval_time:.6f} s")
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/sactor_critic_algo.py ADDED
The diff for this file is too large to render. See raw diff
 
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/training_env_base.py ADDED
@@ -0,0 +1,2000 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import math
3
+ from aug_mpc.utils.math_utils import quaternion_to_angular_velocity, quaternion_difference
4
+
5
+ from mpc_hive.utilities.shared_data.rhc_data import RobotState
6
+ from mpc_hive.utilities.shared_data.rhc_data import RhcCmds, RhcPred
7
+ from mpc_hive.utilities.shared_data.rhc_data import RhcRefs
8
+ from mpc_hive.utilities.shared_data.rhc_data import RhcStatus
9
+ from mpc_hive.utilities.shared_data.sim_data import SharedEnvInfo
10
+
11
+ from aug_mpc.utils.shared_data.remote_stepping import RemoteStepperSrvr
12
+ from aug_mpc.utils.shared_data.remote_stepping import RemoteResetSrvr
13
+ from aug_mpc.utils.shared_data.remote_stepping import RemoteResetRequest
14
+
15
+ from aug_mpc.utils.shared_data.agent_refs import AgentRefs
16
+ from aug_mpc.utils.shared_data.training_env import SharedTrainingEnvInfo
17
+
18
+ from aug_mpc.utils.shared_data.training_env import Observations, NextObservations
19
+ from aug_mpc.utils.shared_data.training_env import TotRewards
20
+ from aug_mpc.utils.shared_data.training_env import SubRewards
21
+ from aug_mpc.utils.shared_data.training_env import Actions
22
+ from aug_mpc.utils.shared_data.training_env import Terminations, SubTerminations
23
+ from aug_mpc.utils.shared_data.training_env import Truncations, SubTruncations
24
+ from aug_mpc.utils.shared_data.training_env import EpisodesCounter,TaskRandCounter,SafetyRandResetsCounter,RandomTruncCounter,SubStepAbsCounter
25
+
26
+ from aug_mpc.utils.episodic_rewards import EpisodicRewards
27
+ from aug_mpc.utils.episodic_data import EpisodicData
28
+ from aug_mpc.utils.episodic_data import MemBuffer
29
+ from aug_mpc.utils.signal_smoother import ExponentialSignalSmoother
30
+ from aug_mpc.utils.math_utils import check_capsize
31
+
32
+ from mpc_hive.utilities.math_utils_torch import world2base_frame
33
+
34
+ from EigenIPC.PyEigenIPC import VLevel
35
+ from EigenIPC.PyEigenIPC import LogType
36
+ from EigenIPC.PyEigenIPC import Journal
37
+ from EigenIPC.PyEigenIPC import StringTensorClient
38
+
39
+ from perf_sleep.pyperfsleep import PerfSleep
40
+
41
+ from abc import abstractmethod, ABC
42
+
43
+ import os
44
+ from typing import List, Dict
45
+
46
+ class AugMPCTrainingEnvBase(ABC):
47
+
48
+ """Base class for a remote training environment tailored to Learning-based Receding Horizon Control"""
49
+
50
+ def __init__(self,
51
+ namespace: str,
52
+ obs_dim: int,
53
+ actions_dim: int,
54
+ env_name: str = "",
55
+ verbose: bool = False,
56
+ vlevel: VLevel = VLevel.V1,
57
+ debug: bool = True,
58
+ use_gpu: bool = True,
59
+ dtype: torch.dtype = torch.float32,
60
+ override_agent_refs: bool = False,
61
+ timeout_ms: int = 60000,
62
+ env_opts: Dict = {}):
63
+
64
+ self._this_path = os.path.abspath(__file__)
65
+
66
+ self.custom_db_data = None
67
+
68
+ self._random_reset_active=False
69
+
70
+ self._action_smoother_continuous=None
71
+ self._action_smoother_discrete=None
72
+
73
+ self._closed = False
74
+ self._ready=False
75
+
76
+ self._namespace = namespace
77
+ self._with_gpu_mirror = True
78
+ self._safe_shared_mem = False
79
+
80
+ self._obs_dim = obs_dim
81
+ self._actions_dim = actions_dim
82
+
83
+ self._use_gpu = use_gpu
84
+ if self._use_gpu:
85
+ self._device="cuda"
86
+ else:
87
+ self._device="cpu"
88
+
89
+ self._dtype = dtype
90
+
91
+ self._verbose = verbose
92
+ self._vlevel = vlevel
93
+
94
+ self._is_debug = debug
95
+
96
+ self._env_name = env_name
97
+
98
+ self._override_agent_refs = override_agent_refs
99
+
100
+ self._substep_dt=1.0 # dt [s] between each substep
101
+
102
+ self._env_opts={}
103
+ self._env_opts.update(env_opts)
104
+ self._process_env_opts()
105
+
106
+ self._robot_state = None
107
+ self._rhc_cmds = None
108
+ self._rhc_pred = None
109
+ self._rhc_refs = None
110
+ self._rhc_status = None
111
+
112
+ self._remote_stepper = None
113
+ self._remote_resetter = None
114
+ self._remote_reset_req = None
115
+
116
+ self._agent_refs = None
117
+
118
+ self._n_envs = 0
119
+
120
+ self._ep_timeout_counter = None
121
+ self._task_rand_counter = None
122
+ self._rand_safety_reset_counter = None
123
+ self._rand_trunc_counter = None
124
+
125
+ self._actions_map={} # to be used to hold info like action idxs
126
+ self._obs_map={}
127
+
128
+ self._obs = None
129
+ self._obs_ub = None
130
+ self._obs_lb = None
131
+ self._next_obs = None
132
+ self._actions = None
133
+ self._actual_actions = None
134
+ self._actions_ub = None
135
+ self._actions_lb = None
136
+ self._tot_rewards = None
137
+ self._sub_rewards = None
138
+ self._sub_terminations = None
139
+ self._sub_truncations = None
140
+ self._terminations = None
141
+ self._truncations = None
142
+ self._act_mem_buffer = None
143
+
144
+ self._episodic_rewards_metrics = None
145
+
146
+ self._timeout = timeout_ms
147
+
148
+ self._height_grid_size = None
149
+ self._height_flat_dim = 0
150
+
151
+ self._attach_to_shared_mem()
152
+
153
+ self._init_obs(obs_dim)
154
+ self._init_actions(actions_dim)
155
+ self._init_rewards()
156
+ self._init_terminations()
157
+ self._init_truncations()
158
+ self._init_custom_db_data()
159
+
160
+ self._demo_setup() # setup for demo envs
161
+
162
+ # to ensure maps are properly initialized
163
+ _ = self._get_action_names()
164
+ _ = self._get_obs_names()
165
+ _ = self._get_sub_trunc_names()
166
+ _ = self._get_sub_term_names()
167
+
168
+ self._set_substep_rew()
169
+ self._set_substep_obs()
170
+
171
+ self._custom_post_init()
172
+
173
+ # update actions scale and offset in case it was modified in _custom_post_init
174
+ self._actions_scale = (self._actions_ub - self._actions_lb)/2.0
175
+ self._actions_offset = (self._actions_ub + self._actions_lb)/2.0
176
+
177
+ if self._env_opts["use_action_smoothing"]:
178
+ self._init_action_smoothing()
179
+
180
+ self._ready=self._init_step()
181
+
182
+ def _add_env_opt(self,
183
+ opts: Dict,
184
+ name: str,
185
+ default):
186
+
187
+ if not name in opts:
188
+ opts[name]=default
189
+
190
+ def _process_env_opts(self, ):
191
+
192
+ self._check_for_env_opts("episode_timeout_lb", int)
193
+ self._check_for_env_opts("episode_timeout_ub", int)
194
+ self._check_for_env_opts("n_steps_task_rand_lb", int)
195
+ self._check_for_env_opts("n_steps_task_rand_ub", int)
196
+ self._check_for_env_opts("use_random_trunc", bool)
197
+ self._check_for_env_opts("random_trunc_freq", int)
198
+ self._check_for_env_opts("random_trunc_freq_delta", int)
199
+ self._check_for_env_opts("use_random_safety_reset", bool)
200
+ self._check_for_env_opts("random_reset_freq", int)
201
+
202
+ self._check_for_env_opts("action_repeat", int)
203
+
204
+ self._check_for_env_opts("n_preinit_steps", int)
205
+
206
+ self._check_for_env_opts("demo_envs_perc", float)
207
+
208
+ self._check_for_env_opts("vec_ep_freq_metrics_db", int)
209
+
210
+ self._check_for_env_opts("srew_drescaling", bool)
211
+
212
+ self._check_for_env_opts("use_action_history", bool)
213
+ self._check_for_env_opts("actions_history_size", int)
214
+
215
+ self._check_for_env_opts("use_action_smoothing", bool)
216
+ self._check_for_env_opts("smoothing_horizon_c", float)
217
+ self._check_for_env_opts("smoothing_horizon_d", float)
218
+
219
+ self._check_for_env_opts("add_heightmap_obs", bool)
220
+
221
+ # parse action repeat opt + get some sim information
222
+ if self._env_opts["action_repeat"] <=0:
223
+ self._env_opts["action_repeat"] = 1
224
+ self._action_repeat=self._env_opts["action_repeat"]
225
+ # parse remote sim info
226
+ sim_info = {}
227
+ sim_info_shared = SharedEnvInfo(namespace=self._namespace,
228
+ is_server=False,
229
+ safe=False,
230
+ verbose=self._verbose,
231
+ vlevel=self._vlevel)
232
+ sim_info_shared.run()
233
+ sim_info_keys = sim_info_shared.param_keys
234
+ sim_info_data = sim_info_shared.get().flatten()
235
+ for i in range(len(sim_info_keys)):
236
+ sim_info[sim_info_keys[i]] = sim_info_data[i]
237
+ if "substepping_dt" in sim_info_keys:
238
+ self._substep_dt=sim_info["substepping_dt"]
239
+ self._env_opts.update(sim_info)
240
+
241
+ self._env_opts["substep_dt"]=self._substep_dt
242
+
243
+ self._env_opts["override_agent_refs"]=self._override_agent_refs
244
+
245
+ self._env_opts["episode_timeout_lb"] = round(self._env_opts["episode_timeout_lb"]/self._action_repeat)
246
+ self._env_opts["episode_timeout_ub"] = round(self._env_opts["episode_timeout_ub"]/self._action_repeat)
247
+
248
+ self._env_opts["n_steps_task_rand_lb"] = round(self._env_opts["n_steps_task_rand_lb"]/self._action_repeat)
249
+ self._env_opts["n_steps_task_rand_ub"] = round(self._env_opts["n_steps_task_rand_ub"]/self._action_repeat)
250
+
251
+ if self._env_opts["random_reset_freq"] <=0:
252
+ self._env_opts["use_random_safety_reset"]=False
253
+ self._env_opts["random_reset_freq"]=-1
254
+ self._random_reset_active=self._env_opts["use_random_safety_reset"]
255
+
256
+ self._env_opts["random_trunc_freq"] = round(self._env_opts["random_trunc_freq"]/self._action_repeat)
257
+ self._env_opts["random_trunc_freq_delta"] = round(self._env_opts["random_trunc_freq_delta"]/self._action_repeat)
258
+
259
+ if self._env_opts["random_trunc_freq"] <=0:
260
+ self._env_opts["use_random_trunc"]=False
261
+ self._env_opts["random_trunc_freq"]=-1
262
+
263
+ self._full_db=False
264
+ if "full_env_db" in self._env_opts:
265
+ self._full_db=self._env_opts["full_env_db"]
266
+
267
+ def _check_for_env_opts(self,
268
+ name: str,
269
+ expected_type):
270
+ if not (name in self._env_opts):
271
+ Journal.log(self.__class__.__name__,
272
+ "_check_for_env_opts",
273
+ f"Required option {name} missing for env opts!",
274
+ LogType.EXCEP,
275
+ throw_when_excep=True)
276
+ if not isinstance(self._env_opts[name], expected_type):
277
+ Journal.log(self.__class__.__name__,
278
+ "_check_for_env_opts",
279
+ f"Option {name} in env opts is not of type {expected_type} (got {type(self._env_opts[name])})!",
280
+ LogType.EXCEP,
281
+ throw_when_excep=True)
282
+
283
+ def __del__(self):
284
+
285
+ self.close()
286
+
287
+ def _demo_setup(self):
288
+
289
+ self._demo_envs_idxs=None
290
+ self._demo_envs_idxs_bool=None
291
+ self._n_demo_envs=round(self._env_opts["demo_envs_perc"]*self._n_envs)
292
+ self._add_demos=False
293
+ if not self._n_demo_envs >0:
294
+ Journal.log(self.__class__.__name__,
295
+ "__init__",
296
+ "will not use demo environments",
297
+ LogType.INFO,
298
+ throw_when_excep=False)
299
+ else:
300
+ Journal.log(self.__class__.__name__,
301
+ "__init__",
302
+ f"Will run with {self._n_demo_envs} demonstration envs.",
303
+ LogType.INFO)
304
+ self._demo_envs_idxs = torch.randperm(self._n_envs, device=self._device)[:self._n_demo_envs]
305
+ self._demo_envs_idxs_bool=torch.full((self._n_envs, ), dtype=torch.bool, device=self._device,
306
+ fill_value=False)
307
+ self._demo_envs_idxs_bool[self._demo_envs_idxs]=True
308
+
309
+ self._init_demo_envs() # custom logic
310
+
311
+ demo_idxs_str=", ".join(map(str, self._demo_envs_idxs.tolist()))
312
+ Journal.log(self.__class__.__name__,
313
+ "__init__",
314
+ f"Demo env. indexes are [{demo_idxs_str}]",
315
+ LogType.INFO)
316
+
317
+ def env_opts(self):
318
+ return self._env_opts
319
+
320
+ def demo_env_idxs(self, get_bool: bool=False):
321
+ if get_bool:
322
+ return self._demo_envs_idxs_bool
323
+ else:
324
+ return self._demo_envs_idxs
325
+
326
+ def _init_demo_envs(self):
327
+ pass
328
+
329
+ def n_demo_envs(self):
330
+ return self._n_demo_envs
331
+
332
+ def demo_active(self):
333
+ return self._add_demos
334
+
335
+ def switch_demo(self, active: bool = False):
336
+ if self._demo_envs_idxs is not None:
337
+ self._add_demos=active
338
+ else:
339
+ Journal.log(self.__class__.__name__,
340
+ "switch_demo",
341
+ f"Cannot switch demostrations on. No demo envs available!",
342
+ LogType.EXCEP,
343
+ throw_when_excep=True)
344
+
345
+ def _get_this_file_path(self):
346
+ return self._this_path
347
+
348
+ def episode_timeout_bounds(self):
349
+ return self._env_opts["episode_timeout_lb"], self._env_opts["episode_timeout_ub"]
350
+
351
+ def task_rand_timeout_bounds(self):
352
+ return self._env_opts["n_steps_task_rand_lb"], self._env_opts["n_steps_task_rand_ub"]
353
+
354
+ def n_action_reps(self):
355
+ return self._action_repeat
356
+
357
+ def get_file_paths(self):
358
+ from aug_mpc.utils.sys_utils import PathsGetter
359
+ path_getter = PathsGetter()
360
+ base_paths = []
361
+ base_paths.append(self._get_this_file_path())
362
+ base_paths.append(path_getter.REMOTENVPATH)
363
+ for script_path in path_getter.SCRIPTSPATHS:
364
+ base_paths.append(script_path)
365
+
366
+ # rhc files
367
+ from EigenIPC.PyEigenIPC import StringTensorClient
368
+ from perf_sleep.pyperfsleep import PerfSleep
369
+ shared_rhc_shared_files = StringTensorClient(
370
+ basename="SharedRhcFilesDropDir",
371
+ name_space=self._namespace,
372
+ verbose=self._verbose,
373
+ vlevel=VLevel.V2)
374
+ shared_rhc_shared_files.run()
375
+ shared_rhc_files_vals=[""]*shared_rhc_shared_files.length()
376
+ while not shared_rhc_shared_files.read_vec(shared_rhc_files_vals, 0):
377
+ nsecs = 1000000000 # 1 sec
378
+ PerfSleep.thread_sleep(nsecs) # we just keep it alive
379
+ rhc_list=[]
380
+ for rhc_files in shared_rhc_files_vals:
381
+ file_list = rhc_files.split(", ")
382
+ rhc_list.extend(file_list)
383
+ rhc_list = list(set(rhc_list)) # removing duplicates
384
+ base_paths.extend(rhc_list)
385
+
386
+ # world interface files
387
+ get_world_interface_paths = self.get_world_interface_paths()
388
+ base_paths.extend(get_world_interface_paths)
389
+ return base_paths
390
+
391
+ def get_world_interface_paths(self):
392
+ paths = []
393
+ shared_world_iface_files = StringTensorClient(
394
+ basename="SharedWorldInterfaceFilesDropDir",
395
+ name_space=self._namespace,
396
+ verbose=self._verbose,
397
+ vlevel=VLevel.V2)
398
+ shared_world_iface_files.run()
399
+ world_iface_vals=[""]*shared_world_iface_files.length()
400
+ while not shared_world_iface_files.read_vec(world_iface_vals, 0):
401
+ nsecs = 1000000000 # 1 sec
402
+ PerfSleep.thread_sleep(nsecs) # keep alive while waiting
403
+ shared_world_iface_files.close()
404
+ for files in world_iface_vals:
405
+ if files == "":
406
+ continue
407
+ file_list = files.split(", ")
408
+ for f in file_list:
409
+ if f not in paths:
410
+ paths.append(f)
411
+ return paths
412
+
413
+ def get_aux_dir(self):
414
+ empty_list = []
415
+ return empty_list
416
+
417
+ def _init_step(self):
418
+
419
+ self._check_controllers_registered(retry=True)
420
+ self._activate_rhc_controllers()
421
+
422
+ # just an auxiliary tensor
423
+ initial_reset_aux = self._terminations.get_torch_mirror(gpu=self._use_gpu).clone()
424
+ initial_reset_aux[:, :] = True # we reset all sim envs first
425
+ init_step_ok=True
426
+ init_step_ok=self._remote_sim_step() and init_step_ok
427
+ if not init_step_ok:
428
+ return False
429
+ init_step_ok=self._remote_reset(reset_mask=initial_reset_aux) and init_step_ok
430
+ if not init_step_ok:
431
+ return False
432
+
433
+ for i in range(self._env_opts["n_preinit_steps"]): # perform some
434
+ # dummy remote env stepping to make sure to have meaningful
435
+ # initializations (doesn't increment step counter)
436
+ init_step_ok=self._remote_sim_step() and init_step_ok # 1 remote sim. step
437
+ if not init_step_ok:
438
+ return False
439
+ init_step_ok=self._send_remote_reset_req() and init_step_ok # fake reset request
440
+ if not init_step_ok:
441
+ return False
442
+
443
+ self.reset()
444
+
445
+ return init_step_ok
446
+
447
+ def _debug(self):
448
+
449
+ if self._use_gpu:
450
+ # using non_blocking which is not safe when GPU->CPU
451
+ self._obs.synch_mirror(from_gpu=True,non_blocking=True) # copy data from gpu to cpu view
452
+ self._next_obs.synch_mirror(from_gpu=True,non_blocking=True)
453
+ self._actions.synch_mirror(from_gpu=True,non_blocking=True)
454
+ self._truncations.synch_mirror(from_gpu=True,non_blocking=True)
455
+ self._sub_truncations.synch_mirror(from_gpu=True,non_blocking=True)
456
+ self._terminations.synch_mirror(from_gpu=True,non_blocking=True)
457
+ self._sub_terminations.synch_mirror(from_gpu=True,non_blocking=True)
458
+ self._tot_rewards.synch_mirror(from_gpu=True,non_blocking=True)
459
+ self._sub_rewards.synch_mirror(from_gpu=True,non_blocking=True)
460
+ # if we want reliable db data then we should synchronize data streams
461
+ torch.cuda.synchronize()
462
+
463
+ # copy CPU view on shared memory
464
+ self._obs.synch_all(read=False, retry=True)
465
+ self._next_obs.synch_all(read=False, retry=True)
466
+ self._actions.synch_all(read=False, retry=True)
467
+ self._tot_rewards.synch_all(read=False, retry=True)
468
+ self._sub_rewards.synch_all(read=False, retry=True)
469
+ self._truncations.synch_all(read=False, retry = True)
470
+ self._sub_truncations.synch_all(read=False, retry = True)
471
+ self._terminations.synch_all(read=False, retry = True)
472
+ self._sub_terminations.synch_all(read=False, retry = True)
473
+
474
+ self._debug_agent_refs()
475
+
476
+ def _debug_agent_refs(self):
477
+ if self._use_gpu:
478
+ if not self._override_agent_refs:
479
+ self._agent_refs.rob_refs.root_state.synch_mirror(from_gpu=True,non_blocking=False)
480
+ if not self._override_agent_refs:
481
+ self._agent_refs.rob_refs.root_state.synch_all(read=False, retry = True)
482
+
483
+ def _remote_sim_step(self):
484
+
485
+ self._remote_stepper.trigger() # triggers simulation + RHC
486
+ if not self._remote_stepper.wait_ack_from(1, self._timeout):
487
+ Journal.log(self.__class__.__name__,
488
+ "_remote_sim_step",
489
+ "Remote sim. env step ack not received within timeout",
490
+ LogType.EXCEP,
491
+ throw_when_excep = False)
492
+ return False
493
+ return True
494
+
495
+ def _remote_reset(self,
496
+ reset_mask: torch.Tensor = None):
497
+
498
+ reset_reqs = self._remote_reset_req.get_torch_mirror()
499
+ if reset_mask is None: # just send the signal to allow stepping, but do not reset any of
500
+ # the remote envs
501
+ reset_reqs[:, :] = False
502
+ else:
503
+ reset_reqs[:, :] = reset_mask # remotely reset envs corresponding to
504
+ # the mask (True--> to be reset)
505
+ self._remote_reset_req.synch_all(read=False, retry=True) # write on shared buffer
506
+ remote_reset_ok = self._send_remote_reset_req() # process remote request
507
+
508
+ if reset_mask is not None:
509
+ self._synch_state(gpu=self._use_gpu) # if some env was reset, we use _obs
510
+ # to hold the states, including resets, while _next_obs will always hold the
511
+ # state right after stepping the sim env
512
+ # (could be a bit more efficient, since in theory we only need to read the envs
513
+ # corresponding to the reset_mask)
514
+
515
+
516
+ return remote_reset_ok
517
+
518
+ def _send_remote_reset_req(self):
519
+
520
+ self._remote_resetter.trigger()
521
+ if not self._remote_resetter.wait_ack_from(1, self._timeout): # remote reset completed
522
+ Journal.log(self.__class__.__name__,
523
+ "_post_step",
524
+ "Remote reset did not complete within the prescribed timeout!",
525
+ LogType.EXCEP,
526
+ throw_when_excep = False)
527
+ return False
528
+ return True
529
+
530
+ def step(self,
531
+ action):
532
+
533
+ actions_norm = action.detach() # IMPORTANT: assumes actions are already normalized in [-1, 1]
534
+
535
+ actions = self._actions.get_torch_mirror(gpu=self._use_gpu) # will hold agent actions (real range)
536
+
537
+ # scale normalized actions to physical space before interfacing with controllers
538
+ actions[:, :] = actions_norm*self._actions_scale + self._actions_offset
539
+
540
+ self._override_actions_with_demo() # if necessary override some actions with expert demonstrations
541
+ # (getting actions with get_actions will return the modified actions tensor)
542
+
543
+ actions.clamp_(self._actions_lb, self._actions_ub) # just to be safe
544
+
545
+ if self._act_mem_buffer is not None: # store norm actions in memory buffer
546
+ self._act_mem_buffer.update(new_data=actions_norm)
547
+
548
+ if self._env_opts["use_action_smoothing"]:
549
+ self._apply_actions_smoothing() # smooth actions if enabled (the tensor returned by
550
+ # get_actions does not contain smoothing and can be safely employed for experience collection)
551
+
552
+ self._apply_actions_to_rhc() # apply last agent actions to rhc controller
553
+
554
+ stepping_ok = True
555
+ tot_rewards = self._tot_rewards.get_torch_mirror(gpu=self._use_gpu)
556
+ sub_rewards = self._sub_rewards.get_torch_mirror(gpu=self._use_gpu)
557
+ next_obs = self._next_obs.get_torch_mirror(gpu=self._use_gpu)
558
+ tot_rewards.zero_()
559
+ sub_rewards.zero_()
560
+ self._substep_rewards.zero_()
561
+ next_obs.zero_() # necessary for substep obs
562
+
563
+ for i in range(0, self._action_repeat):
564
+
565
+ self._pre_substep() # custom logic @ substep freq
566
+
567
+ stepping_ok = stepping_ok and self._check_controllers_registered(retry=False) # does not make sense to run training
568
+ # if we lost some controllers
569
+ stepping_ok = stepping_ok and self._remote_sim_step() # blocking,
570
+
571
+ # no sim substepping is allowed to fail
572
+ self._synch_state(gpu=self._use_gpu) # read state from shared mem (done in substeps also,
573
+ # since substeps rewards will need updated substep obs)
574
+
575
+ self._custom_post_substp_pre_rew() # custom substepping logic
576
+ self._compute_substep_rewards()
577
+ self._assemble_substep_rewards() # includes rewards clipping
578
+ self._custom_post_substp_post_rew() # custom substepping logic
579
+
580
+ # fill substep obs
581
+ self._fill_substep_obs(self._substep_obs)
582
+ self._assemble_substep_obs()
583
+ if not i==(self._action_repeat-1):
584
+ # sends reset signal to complete remote step sequence,
585
+ # but does not reset any remote env
586
+ stepping_ok = stepping_ok and self._remote_reset(reset_mask=None)
587
+ else: # last substep
588
+
589
+ self._fill_step_obs(next_obs) # update next obs
590
+ self._clamp_obs(next_obs) # good practice
591
+ obs = self._obs.get_torch_mirror(gpu=self._use_gpu)
592
+ obs[:, :] = next_obs # start from next observation, unless reset (handled in post_step())
593
+
594
+ self._compute_step_rewards() # implemented by child
595
+
596
+ tot_rewards = self._tot_rewards.get_torch_mirror(gpu=self._use_gpu)
597
+ sub_rewards = self._sub_rewards.get_torch_mirror(gpu=self._use_gpu)
598
+ self._clamp_rewards(sub_rewards) # clamp all sub rewards
599
+
600
+ tot_rewards[:, :] = torch.sum(sub_rewards, dim=1, keepdim=True)
601
+
602
+ scale=1 # scale tot rew by the number of action repeats
603
+ if self._env_opts["srew_drescaling"]: # scale rewards depending on the n of subrewards
604
+ scale*=sub_rewards.shape[1] # n. dims rescaling
605
+ tot_rewards.mul_(1/scale)
606
+
607
+ self._substep_abs_counter.increment() # @ substep freq
608
+
609
+ if not stepping_ok:
610
+ return False
611
+
612
+ stepping_ok = stepping_ok and self._post_step() # post sub-stepping operations
613
+ # (if action_repeat > 1, then just the db data at the last substep is logged)
614
+ # also, if a reset of an env occurs, obs will hold the reset state
615
+
616
+ return stepping_ok
617
+
618
+ def _post_step(self):
619
+
620
+ # first increment counters
621
+ self._ep_timeout_counter.increment() # episode timeout
622
+ self._task_rand_counter.increment() # task randomization
623
+ if self._rand_trunc_counter is not None: # random truncations (for removing temp. correlations)
624
+ self._rand_trunc_counter.increment()
625
+
626
+ # check truncation and termination conditions
627
+ self._check_truncations() # defined in child env
628
+ self._check_terminations()
629
+ terminated = self._terminations.get_torch_mirror(gpu=self._use_gpu)
630
+ truncated = self._truncations.get_torch_mirror(gpu=self._use_gpu)
631
+ ignore_ep_end=None
632
+ if self._rand_trunc_counter is not None:
633
+ ignore_ep_end=self._rand_trunc_counter.time_limits_reached()
634
+ if self._use_gpu:
635
+ ignore_ep_end=ignore_ep_end.cuda()
636
+
637
+ truncated = torch.logical_or(truncated,
638
+ ignore_ep_end) # add truncation (sub truncations defined in child env
639
+ # remain untouched)
640
+
641
+ episode_finished = torch.logical_or(terminated,
642
+ truncated)
643
+ episode_finished_cpu = episode_finished.cpu()
644
+
645
+ if self._rand_safety_reset_counter is not None and self._random_reset_active:
646
+ self._rand_safety_reset_counter.increment(to_be_incremented=episode_finished_cpu.flatten())
647
+ # truncated[:,:] = torch.logical_or(truncated,
648
+ # self._rand_safety_reset_counter.time_limits_reached().cuda())
649
+
650
+ if self._act_mem_buffer is not None:
651
+ self._act_mem_buffer.reset(to_be_reset=episode_finished.flatten(),
652
+ init_data=self._normalize_actions(self.default_action))
653
+
654
+ if self._action_smoother_continuous is not None:
655
+ self._action_smoother_continuous.reset(to_be_reset=episode_finished.flatten(),
656
+ reset_val=self.default_action[:, self._is_continuous_actions])
657
+ if self._action_smoother_discrete is not None:
658
+ self._action_smoother_discrete.reset(to_be_reset=episode_finished.flatten(),
659
+ reset_val=self.default_action[:, ~self._is_continuous_actions])
660
+
661
+ # debug step if required (IMPORTANT: must be before remote reset so that we always db
662
+ # actual data from the step and not after reset)
663
+ if self._is_debug:
664
+ self._debug() # copies db data on shared memory
665
+ ignore_ep_end_cpu=ignore_ep_end if not self._use_gpu else ignore_ep_end.cpu()
666
+ self._update_custom_db_data(episode_finished=episode_finished_cpu,
667
+ ignore_ep_end=ignore_ep_end_cpu # ignore data if random trunc
668
+ )
669
+ self._episodic_rewards_metrics.update(rewards = self._sub_rewards.get_torch_mirror(gpu=False),
670
+ ep_finished=episode_finished_cpu,
671
+ ignore_ep_end=ignore_ep_end_cpu # ignore data if random trunc
672
+ )
673
+
674
+ # remotely reset envs
675
+ to_be_reset=self._to_be_reset()
676
+ to_be_reset_custom=self._custom_reset()
677
+ if to_be_reset_custom is not None:
678
+ to_be_reset[:, :] = torch.logical_or(to_be_reset,to_be_reset_custom)
679
+ rm_reset_ok = self._remote_reset(reset_mask=to_be_reset)
680
+
681
+ self._custom_post_step(episode_finished=episode_finished) # any additional logic from child env
682
+ # here, before actual reset taskes place (at this point the state is the reset one)
683
+
684
+ # updating also prev pos and orientation in case some env was reset
685
+ self._prev_root_p_substep[:, :]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)
686
+ self._prev_root_q_substep[:, :]=self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
687
+
688
+ obs = self._obs.get_torch_mirror(gpu=self._use_gpu)
689
+ self._fill_step_obs(obs)
690
+ self._clamp_obs(obs)
691
+
692
+ # updating prev step quantities
693
+ self._prev_root_p_step[:, :]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)
694
+ self._prev_root_q_step[:, :]=self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
695
+
696
+ # synchronize and reset counters for finished episodes
697
+ self._ep_timeout_counter.reset(to_be_reset=episode_finished)
698
+ self._task_rand_counter.reset(to_be_reset=episode_finished)
699
+ self._substep_abs_counter.reset(to_be_reset=torch.logical_or(terminated,to_be_reset),
700
+ randomize_offsets=True # otherwise timers across envs would be strongly correlated
701
+ ) # reset only if resetting environment or if terminal
702
+
703
+ if self._rand_trunc_counter is not None:
704
+ # only reset when safety truncation was is triggered
705
+ self._rand_trunc_counter.reset(to_be_reset=self._rand_trunc_counter.time_limits_reached(),
706
+ randomize_limits=True, # we need to randomize otherwise the other counters will synchronize
707
+ # with the episode counters
708
+ randomize_offsets=False # always restart at 0
709
+ )
710
+ # safety reset counter is only when it reches its reset interval (just to keep
711
+ # the counter bounded)
712
+ if self._rand_safety_reset_counter is not None and self._random_reset_active:
713
+ self._rand_safety_reset_counter.reset(to_be_reset=self._rand_safety_reset_counter.time_limits_reached())
714
+
715
+ return rm_reset_ok
716
+
717
+ def _to_be_reset(self):
718
+ # always reset if a termination occurred or if there's a random safety reset
719
+ # request
720
+ terminated = self._terminations.get_torch_mirror(gpu=self._use_gpu)
721
+ to_be_reset=terminated.clone()
722
+ if (self._rand_safety_reset_counter is not None) and self._random_reset_active:
723
+ to_be_reset=torch.logical_or(to_be_reset,
724
+ self._rand_safety_reset_counter.time_limits_reached())
725
+
726
+ return to_be_reset
727
+
728
+ def _custom_reset(self):
729
+ # can be overridden by child
730
+ return None
731
+
732
+ def _apply_actions_smoothing(self):
733
+
734
+ actions = self._actions.get_torch_mirror(gpu=self._use_gpu)
735
+ actual_actions=self.get_actual_actions() # will write smoothed actions here
736
+ if self._action_smoother_continuous is not None:
737
+ self._action_smoother_continuous.update(new_signal=
738
+ actions[:, self._is_continuous_actions])
739
+ actual_actions[:, self._is_continuous_actions]=self._action_smoother_continuous.get()
740
+ if self._action_smoother_discrete is not None:
741
+ self._action_smoother_discrete.update(new_signal=
742
+ actions[:, ~self._is_continuous_actions])
743
+ actual_actions[:, ~self._is_continuous_actions]=self._action_smoother_discrete.get()
744
+
745
+ def _update_custom_db_data(self,
746
+ episode_finished,
747
+ ignore_ep_end):
748
+
749
+ # update defaults
750
+ self.custom_db_data["RhcRefsFlag"].update(new_data=self._rhc_refs.contact_flags.get_torch_mirror(gpu=False),
751
+ ep_finished=episode_finished,
752
+ ignore_ep_end=ignore_ep_end) # before potentially resetting the flags, get data
753
+ self.custom_db_data["Actions"].update(new_data=self._actions.get_torch_mirror(gpu=False),
754
+ ep_finished=episode_finished,
755
+ ignore_ep_end=ignore_ep_end)
756
+ self.custom_db_data["Obs"].update(new_data=self._obs.get_torch_mirror(gpu=False),
757
+ ep_finished=episode_finished,
758
+ ignore_ep_end=ignore_ep_end)
759
+
760
+ self.custom_db_data["SubTerminations"].update(new_data=self._sub_terminations.get_torch_mirror(gpu=False),
761
+ ep_finished=episode_finished,
762
+ ignore_ep_end=ignore_ep_end)
763
+ self.custom_db_data["SubTruncations"].update(new_data=self._sub_truncations.get_torch_mirror(gpu=False),
764
+ ep_finished=episode_finished,
765
+ ignore_ep_end=ignore_ep_end)
766
+
767
+ self.custom_db_data["Terminations"].update(new_data=self._terminations.get_torch_mirror(gpu=False),
768
+ ep_finished=episode_finished,
769
+ ignore_ep_end=ignore_ep_end)
770
+ self.custom_db_data["Truncations"].update(new_data=self._truncations.get_torch_mirror(gpu=False),
771
+ ep_finished=episode_finished,
772
+ ignore_ep_end=ignore_ep_end)
773
+
774
+
775
+ self._get_custom_db_data(episode_finished=episode_finished, ignore_ep_end=ignore_ep_end)
776
+
777
+ def reset_custom_db_data(self, keep_track: bool = False):
778
+ # to be called periodically to reset custom db data stat. collection
779
+ for custom_db_data in self.custom_db_data.values():
780
+ custom_db_data.reset(keep_track=keep_track)
781
+
782
+ def _assemble_substep_rewards(self):
783
+ # by default assemble substep rewards by averaging
784
+ sub_rewards = self._sub_rewards.get_torch_mirror(gpu=self._use_gpu)
785
+
786
+ # average over substeps depending on scale
787
+ # sub_rewards[:, self._is_substep_rew] = sub_rewards[:, self._is_substep_rew] + \
788
+ # self._substep_rewards[:, self._is_substep_rew]/self._action_repeat
789
+ sub_rewards[:, self._is_substep_rew] += self._substep_rewards[:, self._is_substep_rew]/self._action_repeat
790
+
791
+ def _assemble_substep_obs(self):
792
+ next_obs = self._next_obs.get_torch_mirror(gpu=self._use_gpu)
793
+ next_obs[:, self._is_substep_obs] += self._substep_obs[:, self._is_substep_obs]/self._action_repeat
794
+
795
+ def randomize_task_refs(self,
796
+ env_indxs: torch.Tensor = None):
797
+
798
+ if self._override_agent_refs:
799
+ self._override_refs(env_indxs=env_indxs)
800
+ else:
801
+ self._randomize_task_refs(env_indxs=env_indxs)
802
+
803
+ def reset(self):
804
+
805
+ self.randomize_task_refs(env_indxs=None) # randomize all refs across envs
806
+
807
+ self._obs.reset()
808
+ self._actions.reset()
809
+ self._next_obs.reset()
810
+ self._sub_rewards.reset()
811
+ self._tot_rewards.reset()
812
+ self._terminations.reset()
813
+ self._sub_terminations.reset()
814
+ self._truncations.reset()
815
+ self._sub_truncations.reset()
816
+
817
+ self._ep_timeout_counter.reset(randomize_offsets=True)
818
+ self._task_rand_counter.reset()
819
+ self._task_rand_counter.sync_counters(other_counter=self._ep_timeout_counter)
820
+ if self._rand_safety_reset_counter is not None:
821
+ self._rand_safety_reset_counter.reset()
822
+ self._substep_abs_counter.reset()
823
+
824
+ if self._act_mem_buffer is not None:
825
+ self._act_mem_buffer.reset_all(init_data=self._normalize_actions(self.default_action))
826
+
827
+ if self._action_smoother_continuous is not None:
828
+ self._action_smoother_continuous.reset(reset_val=self.default_action[:, self._is_continuous_actions])
829
+ if self._action_smoother_discrete is not None:
830
+ self._action_smoother_discrete.reset(reset_val=self.default_action[:, ~self._is_continuous_actions])
831
+
832
+ self._synch_state(gpu=self._use_gpu) # read obs from shared mem
833
+
834
+ # just calling custom post step to ensure tak refs are updated
835
+ terminated = self._terminations.get_torch_mirror(gpu=self._use_gpu)
836
+ truncated = self._truncations.get_torch_mirror(gpu=self._use_gpu)
837
+ episode_finished = torch.logical_or(terminated,
838
+ truncated)
839
+ self._custom_post_step(episode_finished=episode_finished)
840
+
841
+ obs = self._obs.get_torch_mirror(gpu=self._use_gpu)
842
+ next_obs = self._next_obs.get_torch_mirror(gpu=self._use_gpu)
843
+ self._fill_step_obs(obs) # initialize observations
844
+ self._clamp_obs(obs) # to avoid bad things
845
+ self._fill_step_obs(next_obs) # and next obs
846
+ self._clamp_obs(next_obs)
847
+
848
+ self.reset_custom_db_data(keep_track=False)
849
+ self._episodic_rewards_metrics.reset(keep_track=False)
850
+
851
+ self._prev_root_p_step[:, :]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)
852
+ self._prev_root_q_step[:, :]=self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
853
+ self._prev_root_p_substep[:, :]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)
854
+ self._prev_root_q_substep[:, :]=self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
855
+
856
+ def is_ready(self):
857
+ return self._ready
858
+
859
+ def close(self):
860
+
861
+ if not self._closed:
862
+
863
+ # close all shared mem. clients
864
+ self._robot_state.close()
865
+ self._rhc_cmds.close()
866
+ self._rhc_pred.close()
867
+ self._rhc_refs.close()
868
+ self._rhc_status.close()
869
+
870
+ self._remote_stepper.close()
871
+
872
+ self._ep_timeout_counter.close()
873
+ self._task_rand_counter.close()
874
+ if self._rand_safety_reset_counter is not None:
875
+ self._rand_safety_reset_counter.close()
876
+
877
+ # closing env.-specific shared data
878
+ self._obs.close()
879
+ self._next_obs.close()
880
+ self._actions.close()
881
+ if self._actual_actions is not None:
882
+ self._actual_actions.close()
883
+ self._sub_rewards.close()
884
+ self._tot_rewards.close()
885
+
886
+ self._terminations.close()
887
+ self._sub_terminations.close()
888
+ self._truncations.close()
889
+ self._sub_truncations.close()
890
+
891
+ self._closed = True
892
+
893
+ def get_obs(self, clone:bool=False):
894
+ if clone:
895
+ return self._obs.get_torch_mirror(gpu=self._use_gpu).detach().clone()
896
+ else:
897
+ return self._obs.get_torch_mirror(gpu=self._use_gpu).detach()
898
+
899
+ def get_next_obs(self, clone:bool=False):
900
+ if clone:
901
+ return self._next_obs.get_torch_mirror(gpu=self._use_gpu).detach().clone()
902
+ else:
903
+ return self._next_obs.get_torch_mirror(gpu=self._use_gpu).detach()
904
+
905
+ def get_actions(self, clone:bool=False, normalized: bool = False):
906
+ actions = self._actions.get_torch_mirror(gpu=self._use_gpu).detach()
907
+ if normalized:
908
+ normalized_actions = self._normalize_actions(actions)
909
+ return normalized_actions.clone() if clone else normalized_actions
910
+ return actions.clone() if clone else actions
911
+
912
+ def get_actual_actions(self, clone:bool=False, normalized: bool = False):
913
+ if self._env_opts["use_action_smoothing"]:
914
+ actions = self._actual_actions.get_torch_mirror(gpu=self._use_gpu).detach()
915
+ else: # actual action coincides with the one from the agent + possible modif.
916
+ actions = self.get_actions(clone=False, normalized=False)
917
+ if normalized:
918
+ normalized_actions = self._normalize_actions(actions)
919
+ return normalized_actions.clone() if clone else normalized_actions
920
+ return actions.clone() if clone else actions
921
+
922
+ def _normalize_actions(self, actions: torch.Tensor):
923
+ scale = torch.where(self._actions_scale == 0.0,
924
+ torch.ones_like(self._actions_scale),
925
+ self._actions_scale)
926
+ normalized = (actions - self._actions_offset)/scale
927
+ zero_scale_mask = torch.eq(self._actions_scale, 0.0).squeeze(0)
928
+ if torch.any(zero_scale_mask):
929
+ normalized[:, zero_scale_mask] = 0.0
930
+ return normalized
931
+
932
+ def get_rewards(self, clone:bool=False):
933
+ if clone:
934
+ return self._tot_rewards.get_torch_mirror(gpu=self._use_gpu).detach().clone()
935
+ else:
936
+ return self._tot_rewards.get_torch_mirror(gpu=self._use_gpu).detach()
937
+
938
+ def get_terminations(self, clone:bool=False):
939
+ if clone:
940
+ return self._terminations.get_torch_mirror(gpu=self._use_gpu).detach().clone()
941
+ else:
942
+ return self._terminations.get_torch_mirror(gpu=self._use_gpu).detach()
943
+
944
+ def get_truncations(self, clone:bool=False):
945
+ if clone:
946
+ return self._truncations.get_torch_mirror(gpu=self._use_gpu).detach().clone()
947
+ else:
948
+ return self._truncations.get_torch_mirror(gpu=self._use_gpu).detach()
949
+
950
+ def obs_dim(self):
951
+
952
+ return self._obs_dim
953
+
954
+ def actions_dim(self):
955
+
956
+ return self._actions_dim
957
+
958
+ def ep_rewards_metrics(self):
959
+
960
+ return self._episodic_rewards_metrics
961
+
962
+ def using_gpu(self):
963
+
964
+ return self._use_gpu
965
+
966
+ def name(self):
967
+
968
+ return self._env_name
969
+
970
+ def n_envs(self):
971
+
972
+ return self._n_envs
973
+
974
+ def dtype(self):
975
+
976
+ return self._dtype
977
+
978
+ def obs_names(self):
979
+ return self._get_obs_names()
980
+
981
+ def action_names(self):
982
+ return self._get_action_names()
983
+
984
+ def sub_rew_names(self):
985
+ return self._get_rewards_names()
986
+
987
+ def sub_term_names(self):
988
+ return self._get_sub_term_names()
989
+
990
+ def sub_trunc_names(self):
991
+ return self._get_sub_trunc_names()
992
+
993
+ def _get_obs_names(self):
994
+ # to be overridden by child class
995
+ return None
996
+
997
+ def get_robot_jnt_names(self):
998
+ return self._robot_state.jnt_names()
999
+
1000
+ def _get_action_names(self):
1001
+ # to be overridden by child class
1002
+ return None
1003
+
1004
+ def _get_rewards_names(self):
1005
+ # to be overridden by child class
1006
+ return None
1007
+
1008
+ def _get_sub_term_names(self):
1009
+ # to be overridden by child class
1010
+ sub_term_names = []
1011
+ sub_term_names.append("rhc_failure")
1012
+ sub_term_names.append("robot_capsize")
1013
+ sub_term_names.append("rhc_capsize")
1014
+
1015
+ return sub_term_names
1016
+
1017
+ def _get_sub_trunc_names(self):
1018
+ # to be overridden by child class
1019
+ sub_trunc_names = []
1020
+ sub_trunc_names.append("ep_timeout")
1021
+
1022
+ return sub_trunc_names
1023
+
1024
+ def _get_custom_db_data(self, episode_finished):
1025
+ # to be overridden by child class
1026
+ pass
1027
+
1028
+ def set_observed_joints(self):
1029
+ # ny default observe all joints available
1030
+ return self._robot_state.jnt_names()
1031
+
1032
+ def _set_jnts_blacklist_pattern(self):
1033
+ self._jnt_q_blacklist_patterns=[]
1034
+
1035
+ def get_observed_joints(self):
1036
+ return self._observed_jnt_names
1037
+
1038
+ def _init_obs(self, obs_dim: int):
1039
+
1040
+ device = "cuda" if self._use_gpu else "cpu"
1041
+
1042
+ obs_threshold_default = 1e3
1043
+ self._obs_threshold_lb = -obs_threshold_default # used for clipping observations
1044
+ self._obs_threshold_ub = obs_threshold_default
1045
+
1046
+ self._obs_ub = torch.full((1, obs_dim), dtype=self._dtype, device=device,
1047
+ fill_value=1.0)
1048
+ self._obs_lb = torch.full((1, obs_dim), dtype=self._dtype, device=device,
1049
+ fill_value=-1.0)
1050
+ self._obs_scale = (self._obs_ub - self._obs_lb)/2.0
1051
+ self._obs_offset = (self._obs_ub + self._obs_lb)/2.0
1052
+
1053
+ if not self._obs_dim==len(self._get_obs_names()):
1054
+ error=f"obs dim {self._obs_dim} does not match obs names length {len(self._get_obs_names())}!!"
1055
+ Journal.log(self.__class__.__name__,
1056
+ "_init_obs",
1057
+ error,
1058
+ LogType.EXCEP,
1059
+ throw_when_excep = True)
1060
+
1061
+ self._obs = Observations(namespace=self._namespace,
1062
+ n_envs=self._n_envs,
1063
+ obs_dim=self._obs_dim,
1064
+ obs_names=self._get_obs_names(),
1065
+ env_names=None,
1066
+ is_server=True,
1067
+ verbose=self._verbose,
1068
+ vlevel=self._vlevel,
1069
+ safe=True,
1070
+ force_reconnection=True,
1071
+ with_gpu_mirror=self._use_gpu,
1072
+ fill_value=0.0)
1073
+
1074
+ self._next_obs = NextObservations(namespace=self._namespace,
1075
+ n_envs=self._n_envs,
1076
+ obs_dim=self._obs_dim,
1077
+ obs_names=self._get_obs_names(),
1078
+ env_names=None,
1079
+ is_server=True,
1080
+ verbose=self._verbose,
1081
+ vlevel=self._vlevel,
1082
+ safe=True,
1083
+ force_reconnection=True,
1084
+ with_gpu_mirror=self._use_gpu,
1085
+ fill_value=0.0)
1086
+
1087
+ self._obs.run()
1088
+ self._next_obs.run()
1089
+
1090
+ self._is_substep_obs = torch.zeros((self.obs_dim(),), dtype=torch.bool, device=device)
1091
+ self._is_substep_obs.fill_(False) # default to all step obs
1092
+
1093
+ # not super memory efficient
1094
+ self._substep_obs=torch.full_like(self._obs.get_torch_mirror(gpu=self._use_gpu), fill_value=0.0)
1095
+
1096
+ def _init_actions(self, actions_dim: int):
1097
+
1098
+ device = "cuda" if self._use_gpu else "cpu"
1099
+ # action scalings to be applied to agent's output
1100
+ self._actions_ub = torch.full((1, actions_dim), dtype=self._dtype, device=device,
1101
+ fill_value=1.0)
1102
+ self._actions_lb = torch.full((1, actions_dim), dtype=self._dtype, device=device,
1103
+ fill_value=-1.0)
1104
+ self._actions_scale = (self._actions_ub - self._actions_lb)/2.0
1105
+ self._actions_offset = (self._actions_ub + self._actions_lb)/2.0
1106
+
1107
+ if not self._actions_dim==len(self._get_action_names()):
1108
+ error=f"action dim {self._actions_dim} does not match action names length {len(self._get_action_names())}!!"
1109
+ Journal.log(self.__class__.__name__,
1110
+ "_init_actions",
1111
+ error,
1112
+ LogType.EXCEP,
1113
+ throw_when_excep = True)
1114
+ self._actions = Actions(namespace=self._namespace,
1115
+ n_envs=self._n_envs,
1116
+ action_dim=self._actions_dim,
1117
+ action_names=self._get_action_names(),
1118
+ env_names=None,
1119
+ is_server=True,
1120
+ verbose=self._verbose,
1121
+ vlevel=self._vlevel,
1122
+ safe=True,
1123
+ force_reconnection=True,
1124
+ with_gpu_mirror=self._use_gpu,
1125
+ fill_value=0.0)
1126
+
1127
+ self._actions.run()
1128
+
1129
+ self.default_action = torch.full_like(input=self.get_actions(),fill_value=0.0)
1130
+ self.safe_action = torch.full_like(input=self.get_actions(),fill_value=0.0)
1131
+
1132
+ if self._env_opts["use_action_history"]:
1133
+ self._act_mem_buffer=MemBuffer(name="ActionMemBuf",
1134
+ data_tensor=self._actions.get_torch_mirror(),
1135
+ data_names=self._get_action_names(),
1136
+ debug=self._debug,
1137
+ horizon=self._env_opts["actions_history_size"],
1138
+ dtype=self._dtype,
1139
+ use_gpu=self._use_gpu)
1140
+
1141
+ # default to all continuous actions (changes the way noise is added)
1142
+ self._is_continuous_actions=torch.full((actions_dim, ),
1143
+ dtype=torch.bool, device=device,
1144
+ fill_value=True)
1145
+
1146
+ def _init_action_smoothing(self):
1147
+
1148
+ continuous_actions=self.get_actions()[:, self._is_continuous_actions]
1149
+ discrete_actions=self.get_actions()[:, ~self._is_continuous_actions]
1150
+ self._action_smoother_continuous=ExponentialSignalSmoother(signal=continuous_actions,
1151
+ update_dt=self._substep_dt*self._action_repeat, # rate at which actions are decided by agent
1152
+ smoothing_horizon=self._env_opts["smoothing_horizon_c"],
1153
+ target_smoothing=0.5,
1154
+ debug=self._debug,
1155
+ dtype=self._dtype,
1156
+ use_gpu=self._use_gpu,
1157
+ name="ActionSmootherContinuous")
1158
+ self._action_smoother_discrete=ExponentialSignalSmoother(signal=discrete_actions,
1159
+ update_dt=self._substep_dt*self._action_repeat, # rate at which actions are decided by agent
1160
+ smoothing_horizon=self._env_opts["smoothing_horizon_d"],
1161
+ target_smoothing=0.5,
1162
+ debug=self._debug,
1163
+ dtype=self._dtype,
1164
+ use_gpu=self._use_gpu,
1165
+ name="ActionSmootherDiscrete")
1166
+
1167
+ # we also need somewhere to keep the actual actions after smoothing
1168
+ self._actual_actions = Actions(namespace=self._namespace+"_actual",
1169
+ n_envs=self._n_envs,
1170
+ action_dim=self._actions_dim,
1171
+ action_names=self._get_action_names(),
1172
+ env_names=None,
1173
+ is_server=True,
1174
+ verbose=self._verbose,
1175
+ vlevel=self._vlevel,
1176
+ safe=True,
1177
+ force_reconnection=True,
1178
+ with_gpu_mirror=self._use_gpu,
1179
+ fill_value=0.0)
1180
+ self._actual_actions.run()
1181
+
1182
+ def _init_rewards(self):
1183
+
1184
+ reward_thresh_default = 1.0
1185
+ n_sub_rewards = len(self._get_rewards_names())
1186
+ device = "cuda" if self._use_gpu else "cpu"
1187
+ self._reward_thresh_lb = torch.full((1, n_sub_rewards), dtype=self._dtype, fill_value=-reward_thresh_default, device=device) # used for clipping rewards
1188
+ self._reward_thresh_ub = torch.full((1, n_sub_rewards), dtype=self._dtype, fill_value=reward_thresh_default, device=device)
1189
+
1190
+ self._sub_rewards = SubRewards(namespace=self._namespace,
1191
+ n_envs=self._n_envs,
1192
+ n_rewards=n_sub_rewards,
1193
+ reward_names=self._get_rewards_names(),
1194
+ env_names=None,
1195
+ is_server=True,
1196
+ verbose=self._verbose,
1197
+ vlevel=self._vlevel,
1198
+ safe=True,
1199
+ force_reconnection=True,
1200
+ with_gpu_mirror=self._use_gpu,
1201
+ fill_value=0.0)
1202
+
1203
+ self._tot_rewards = TotRewards(namespace=self._namespace,
1204
+ n_envs=self._n_envs,
1205
+ reward_names=["total_reward"],
1206
+ env_names=None,
1207
+ is_server=True,
1208
+ verbose=self._verbose,
1209
+ vlevel=self._vlevel,
1210
+ safe=True,
1211
+ force_reconnection=True,
1212
+ with_gpu_mirror=self._use_gpu,
1213
+ fill_value=0.0)
1214
+
1215
+ self._sub_rewards.run()
1216
+ self._tot_rewards.run()
1217
+
1218
+ self._substep_rewards = self._sub_rewards.get_torch_mirror(gpu=self._use_gpu).detach().clone()
1219
+ # used to hold substep rewards (not super mem. efficient)
1220
+ self._is_substep_rew = torch.zeros((self._substep_rewards.shape[1],),dtype=torch.bool,device=device)
1221
+ self._is_substep_rew.fill_(True) # default to all substep rewards
1222
+
1223
+ self._episodic_rewards_metrics = EpisodicRewards(reward_tensor=self._sub_rewards.get_torch_mirror(),
1224
+ reward_names=self._get_rewards_names(),
1225
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1226
+ store_transitions=self._full_db,
1227
+ max_ep_duration=self._max_ep_length())
1228
+ self._episodic_rewards_metrics.set_constant_data_scaling(scaling=self._get_reward_scaling())
1229
+
1230
+ def _get_reward_scaling(self):
1231
+ # to be overridden by child (default to no scaling)
1232
+ return 1
1233
+
1234
+ def _max_ep_length(self):
1235
+ #.should be overriden by child
1236
+ return self._env_opts["episode_timeout_ub"]
1237
+
1238
+ def _init_custom_db_data(self):
1239
+
1240
+ self.custom_db_data = {}
1241
+ # by default always log this contact data
1242
+ rhc_latest_contact_ref = self._rhc_refs.contact_flags.get_torch_mirror()
1243
+ contact_names = self._rhc_refs.rob_refs.contact_names()
1244
+ stepping_data = EpisodicData("RhcRefsFlag", rhc_latest_contact_ref, contact_names,
1245
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1246
+ store_transitions=self._full_db,
1247
+ max_ep_duration=self._max_ep_length())
1248
+ self._add_custom_db_data(db_data=stepping_data)
1249
+
1250
+ # log also action data
1251
+ actions = self._actions.get_torch_mirror()
1252
+ action_names = self._get_action_names()
1253
+ action_data = EpisodicData("Actions", actions, action_names,
1254
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1255
+ store_transitions=self._full_db,
1256
+ max_ep_duration=self._max_ep_length())
1257
+ self._add_custom_db_data(db_data=action_data)
1258
+
1259
+ # and observations
1260
+ observations = self._obs.get_torch_mirror()
1261
+ observations_names = self._get_obs_names()
1262
+ obs_data = EpisodicData("Obs", observations, observations_names,
1263
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1264
+ store_transitions=self._full_db,
1265
+ max_ep_duration=self._max_ep_length())
1266
+ self._add_custom_db_data(db_data=obs_data)
1267
+
1268
+ # log sub-term and sub-truncations data
1269
+ t_scaling=1 # 1 so that we log an interpretable data in terms of why the episode finished
1270
+ data_scaling = torch.full((self._n_envs, 1),
1271
+ fill_value=t_scaling,
1272
+ dtype=torch.int32,device="cpu")
1273
+ sub_term = self._sub_terminations.get_torch_mirror()
1274
+ term = self._terminations.get_torch_mirror()
1275
+ sub_termination_names = self.sub_term_names()
1276
+
1277
+ sub_term_data = EpisodicData("SubTerminations", sub_term, sub_termination_names,
1278
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1279
+ store_transitions=self._full_db,
1280
+ max_ep_duration=self._max_ep_length())
1281
+ sub_term_data.set_constant_data_scaling(enable=True,scaling=data_scaling)
1282
+ self._add_custom_db_data(db_data=sub_term_data)
1283
+ term_data = EpisodicData("Terminations", term, ["terminations"],
1284
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1285
+ store_transitions=self._full_db,
1286
+ max_ep_duration=self._max_ep_length())
1287
+ term_data.set_constant_data_scaling(enable=True,scaling=data_scaling)
1288
+ self._add_custom_db_data(db_data=term_data)
1289
+
1290
+ sub_trunc = self._sub_truncations.get_torch_mirror()
1291
+ trunc = self._truncations.get_torch_mirror()
1292
+ sub_truncations_names = self.sub_trunc_names()
1293
+ sub_trunc_data = EpisodicData("SubTruncations", sub_trunc, sub_truncations_names,
1294
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1295
+ store_transitions=self._full_db,
1296
+ max_ep_duration=self._max_ep_length())
1297
+ sub_trunc_data.set_constant_data_scaling(enable=True,scaling=data_scaling)
1298
+ self._add_custom_db_data(db_data=sub_trunc_data)
1299
+ trunc_data = EpisodicData("Truncations", trunc, ["truncations"],
1300
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1301
+ store_transitions=self._full_db,
1302
+ max_ep_duration=self._max_ep_length())
1303
+ trunc_data.set_constant_data_scaling(enable=True,scaling=data_scaling)
1304
+ self._add_custom_db_data(db_data=trunc_data)
1305
+
1306
+ def _add_custom_db_data(self, db_data: EpisodicData):
1307
+ self.custom_db_data[db_data.name()] = db_data
1308
+
1309
+ def _init_terminations(self):
1310
+
1311
+ # Boolean array indicating whether each environment episode has terminated after
1312
+ # the current step. An episode termination could occur based on predefined conditions
1313
+ # in the environment, such as reaching a goal or exceeding a time limit.
1314
+
1315
+ self._terminations = Terminations(namespace=self._namespace,
1316
+ n_envs=self._n_envs,
1317
+ is_server=True,
1318
+ verbose=self._verbose,
1319
+ vlevel=self._vlevel,
1320
+ safe=True,
1321
+ force_reconnection=True,
1322
+ with_gpu_mirror=self._use_gpu,
1323
+ fill_value=False)
1324
+ self._terminations.run()
1325
+
1326
+ sub_t_names = self.sub_term_names()
1327
+ self._sub_terminations = SubTerminations(namespace=self._namespace,
1328
+ n_envs=self._n_envs,
1329
+ n_term=len(sub_t_names),
1330
+ term_names=sub_t_names,
1331
+ is_server=True,
1332
+ verbose=self._verbose,
1333
+ vlevel=self._vlevel,
1334
+ safe=True,
1335
+ force_reconnection=True,
1336
+ with_gpu_mirror=self._use_gpu,
1337
+ fill_value=False)
1338
+ self._sub_terminations.run()
1339
+
1340
+ device = "cuda" if self._use_gpu else "cpu"
1341
+ self._is_capsized=torch.zeros((self._n_envs,1),
1342
+ dtype=torch.bool, device=device)
1343
+ self._is_rhc_capsized=torch.zeros((self._n_envs,1),
1344
+ dtype=torch.bool, device=device)
1345
+ self._max_pitch_angle=60.0*math.pi/180.0
1346
+
1347
+ def _init_truncations(self):
1348
+
1349
+ self._truncations = Truncations(namespace=self._namespace,
1350
+ n_envs=self._n_envs,
1351
+ is_server=True,
1352
+ verbose=self._verbose,
1353
+ vlevel=self._vlevel,
1354
+ safe=True,
1355
+ force_reconnection=True,
1356
+ with_gpu_mirror=self._use_gpu,
1357
+ fill_value=False)
1358
+
1359
+ self._truncations.run()
1360
+
1361
+ sub_trc_names = self.sub_trunc_names()
1362
+ self._sub_truncations = SubTruncations(namespace=self._namespace,
1363
+ n_envs=self._n_envs,
1364
+ n_trunc=len(sub_trc_names),
1365
+ truc_names=sub_trc_names,
1366
+ is_server=True,
1367
+ verbose=self._verbose,
1368
+ vlevel=self._vlevel,
1369
+ safe=True,
1370
+ force_reconnection=True,
1371
+ with_gpu_mirror=self._use_gpu,
1372
+ fill_value=False)
1373
+ self._sub_truncations.run()
1374
+
1375
+ def _update_jnt_blacklist(self):
1376
+ device = "cuda" if self._use_gpu else "cpu"
1377
+ all_available_jnts=self.get_observed_joints()
1378
+ blacklist=[]
1379
+ for i in range(len(all_available_jnts)):
1380
+ for pattern in self._jnt_q_blacklist_patterns:
1381
+ if pattern in all_available_jnts[i]:
1382
+ # stop at first pattern match
1383
+ blacklist.append(i)
1384
+ break
1385
+ if not len(blacklist)==0:
1386
+ self._jnt_q_blacklist_idxs=torch.tensor(blacklist, dtype=torch.int, device=device)
1387
+
1388
+ def _attach_to_shared_mem(self):
1389
+
1390
+ # runs shared mem clients for getting observation and setting RHC commands
1391
+
1392
+ # remote stepping data
1393
+ self._remote_stepper = RemoteStepperSrvr(namespace=self._namespace,
1394
+ verbose=self._verbose,
1395
+ vlevel=self._vlevel,
1396
+ force_reconnection=True)
1397
+ self._remote_stepper.run()
1398
+ self._remote_resetter = RemoteResetSrvr(namespace=self._namespace,
1399
+ verbose=self._verbose,
1400
+ vlevel=self._vlevel,
1401
+ force_reconnection=True)
1402
+ self._remote_resetter.run()
1403
+ self._remote_reset_req = RemoteResetRequest(namespace=self._namespace,
1404
+ is_server=False,
1405
+ verbose=self._verbose,
1406
+ vlevel=self._vlevel,
1407
+ safe=True)
1408
+ self._remote_reset_req.run()
1409
+
1410
+ self._jnts_remapping=None
1411
+ self._jnt_q_blacklist_idxs=None
1412
+
1413
+ self._robot_state = RobotState(namespace=self._namespace,
1414
+ is_server=False,
1415
+ safe=self._safe_shared_mem,
1416
+ verbose=self._verbose,
1417
+ vlevel=self._vlevel,
1418
+ with_gpu_mirror=self._use_gpu,
1419
+ with_torch_view=True,
1420
+ enable_height_sensor=self._env_opts["add_heightmap_obs"])
1421
+
1422
+ self._rhc_cmds = RhcCmds(namespace=self._namespace,
1423
+ is_server=False,
1424
+ safe=self._safe_shared_mem,
1425
+ verbose=self._verbose,
1426
+ vlevel=self._vlevel,
1427
+ with_gpu_mirror=self._use_gpu,
1428
+ with_torch_view=True)
1429
+
1430
+ self._rhc_pred = RhcPred(namespace=self._namespace,
1431
+ is_server=False,
1432
+ safe=self._safe_shared_mem,
1433
+ verbose=self._verbose,
1434
+ vlevel=self._vlevel,
1435
+ with_gpu_mirror=self._use_gpu,
1436
+ with_torch_view=True)
1437
+
1438
+ self._rhc_refs = RhcRefs(namespace=self._namespace,
1439
+ is_server=False,
1440
+ safe=self._safe_shared_mem,
1441
+ verbose=self._verbose,
1442
+ vlevel=self._vlevel,
1443
+ with_gpu_mirror=self._use_gpu,
1444
+ with_torch_view=True)
1445
+
1446
+ self._rhc_status = RhcStatus(namespace=self._namespace,
1447
+ is_server=False,
1448
+ verbose=self._verbose,
1449
+ vlevel=self._vlevel,
1450
+ with_gpu_mirror=self._use_gpu,
1451
+ with_torch_view=True)
1452
+
1453
+ self._robot_state.run()
1454
+ self._n_envs = self._robot_state.n_robots()
1455
+ self._n_jnts = self._robot_state.n_jnts()
1456
+ self._n_contacts = self._robot_state.n_contacts() # we assume same n contacts for all rhcs for now
1457
+
1458
+ self._rhc_cmds.run()
1459
+ self._rhc_pred.run()
1460
+ self._rhc_refs.run()
1461
+ self._rhc_status.run()
1462
+ # we read rhc info now and just this time, since it's assumed to be static
1463
+ self._check_controllers_registered(retry=True) # blocking
1464
+ # (we need controllers to be connected to read meaningful data)
1465
+
1466
+ self._rhc_status.rhc_static_info.synch_all(read=True,retry=True)
1467
+ if self._use_gpu:
1468
+ self._rhc_status.rhc_static_info.synch_mirror(from_gpu=False,non_blocking=False)
1469
+ rhc_horizons=self._rhc_status.rhc_static_info.get("horizons",gpu=self._use_gpu)
1470
+ rhc_nnodes=self._rhc_status.rhc_static_info.get("nnodes",gpu=self._use_gpu)
1471
+ rhc_dts=self._rhc_status.rhc_static_info.get("dts",gpu=self._use_gpu)
1472
+
1473
+ # height sensor metadata (client side)
1474
+ if self._env_opts["add_heightmap_obs"]:
1475
+ self._height_grid_size = self._robot_state.height_sensor.grid_size
1476
+ self._height_flat_dim = self._robot_state.height_sensor.n_cols
1477
+ rhc_ncontacts=self._rhc_status.rhc_static_info.get("ncontacts",gpu=self._use_gpu)
1478
+ robot_mass=self._rhc_status.rhc_static_info.get("robot_mass",gpu=self._use_gpu)
1479
+ pred_node_idxs_rhc=self._rhc_status.rhc_static_info.get("pred_node_idx",gpu=self._use_gpu)
1480
+
1481
+ self._n_nodes_rhc=torch.round(rhc_nnodes) # we assume nodes are static during an env lifetime
1482
+ self._rhc_horizons=rhc_horizons
1483
+ self._rhc_dts=rhc_dts
1484
+ self._n_contacts_rhc=rhc_ncontacts
1485
+ self._rhc_robot_masses=robot_mass
1486
+ if (self._rhc_robot_masses == 0).any():
1487
+ zero_indices = torch.nonzero(self._rhc_robot_masses == 0, as_tuple=True)
1488
+ print(zero_indices) # This will print the indices of zero elements
1489
+ Journal.log(self.__class__.__name__,
1490
+ "_attach_to_shared_mem",
1491
+ "Found at least one robot with 0 mass from RHC static info!!",
1492
+ LogType.EXCEP,
1493
+ throw_when_excep=True)
1494
+
1495
+ self._rhc_robot_weight=robot_mass*9.81
1496
+ self._pred_node_idxs_rhc=pred_node_idxs_rhc
1497
+ self._pred_horizon_rhc=self._pred_node_idxs_rhc*self._rhc_dts
1498
+
1499
+ # run server for agent commands
1500
+ self._agent_refs = AgentRefs(namespace=self._namespace,
1501
+ is_server=True,
1502
+ n_robots=self._n_envs,
1503
+ n_jnts=self._robot_state.n_jnts(),
1504
+ n_contacts=self._robot_state.n_contacts(),
1505
+ contact_names=self._robot_state.contact_names(),
1506
+ q_remapping=None,
1507
+ with_gpu_mirror=self._use_gpu,
1508
+ force_reconnection=True,
1509
+ safe=False,
1510
+ verbose=self._verbose,
1511
+ vlevel=self._vlevel,
1512
+ fill_value=0)
1513
+ self._agent_refs.run()
1514
+ q_init_agent_refs=torch.full_like(self._robot_state.root_state.get(data_type="q", gpu=self._use_gpu),fill_value=0.0)
1515
+ q_init_agent_refs[:, 0]=1.0
1516
+ self._agent_refs.rob_refs.root_state.set(data_type="q", data=q_init_agent_refs,
1517
+ gpu=self._use_gpu)
1518
+ if self._use_gpu:
1519
+ self._agent_refs.rob_refs.root_state.synch_mirror(from_gpu=True,non_blocking=True)
1520
+ self._agent_refs.rob_refs.root_state.synch_all(read=False, retry=True)
1521
+ # episode steps counters (for detecting episode truncations for
1522
+ # time limits)
1523
+ self._ep_timeout_counter = EpisodesCounter(namespace=self._namespace,
1524
+ n_envs=self._n_envs,
1525
+ n_steps_lb=self._env_opts["episode_timeout_lb"],
1526
+ n_steps_ub=self._env_opts["episode_timeout_ub"],
1527
+ randomize_offsets_at_startup=True, # this has to be randomized
1528
+ is_server=True,
1529
+ verbose=self._verbose,
1530
+ vlevel=self._vlevel,
1531
+ safe=True,
1532
+ force_reconnection=True,
1533
+ with_gpu_mirror=self._use_gpu,
1534
+ debug=self._debug) # handles step counter through episodes and through envs
1535
+ self._ep_timeout_counter.run()
1536
+ self._task_rand_counter = TaskRandCounter(namespace=self._namespace,
1537
+ n_envs=self._n_envs,
1538
+ n_steps_lb=self._env_opts["n_steps_task_rand_lb"],
1539
+ n_steps_ub=self._env_opts["n_steps_task_rand_ub"],
1540
+ randomize_offsets_at_startup=False, # not necessary since it will be synched with the timeout counter
1541
+ is_server=True,
1542
+ verbose=self._verbose,
1543
+ vlevel=self._vlevel,
1544
+ safe=True,
1545
+ force_reconnection=True,
1546
+ with_gpu_mirror=self._use_gpu,
1547
+ debug=self._debug) # handles step counter through episodes and through envs
1548
+ self._task_rand_counter.run()
1549
+ self._task_rand_counter.sync_counters(other_counter=self._ep_timeout_counter)
1550
+ if self._env_opts["use_random_trunc"]:
1551
+ self._rand_trunc_counter=RandomTruncCounter(namespace=self._namespace,
1552
+ n_envs=self._n_envs,
1553
+ n_steps_lb=self._env_opts["random_trunc_freq"]-self._env_opts["random_trunc_freq_delta"],
1554
+ n_steps_ub=self._env_opts["random_trunc_freq"],
1555
+ randomize_offsets_at_startup=True,
1556
+ is_server=True,
1557
+ verbose=self._verbose,
1558
+ vlevel=self._vlevel,
1559
+ safe=True,
1560
+ force_reconnection=True,
1561
+ with_gpu_mirror=self._use_gpu,
1562
+ debug=False)
1563
+ self._rand_trunc_counter.run()
1564
+ # self._rand_trunc_counter.sync_counters(other_counter=self._ep_timeout_counter)
1565
+ if self._env_opts["use_random_safety_reset"]:
1566
+ self._rand_safety_reset_counter=SafetyRandResetsCounter(namespace=self._namespace,
1567
+ n_envs=self._n_envs,
1568
+ n_steps_lb=self._env_opts["random_reset_freq"],
1569
+ n_steps_ub=self._env_opts["random_reset_freq"],
1570
+ randomize_offsets_at_startup=True,
1571
+ is_server=True,
1572
+ verbose=self._verbose,
1573
+ vlevel=self._vlevel,
1574
+ safe=True,
1575
+ force_reconnection=True,
1576
+ with_gpu_mirror=self._use_gpu,
1577
+ debug=False)
1578
+ self._rand_safety_reset_counter.run()
1579
+ # self._rand_safety_reset_counter.sync_counters(other_counter=self._ep_timeout_counter)
1580
+
1581
+ # timer to track abs time in each env (reset logic to be implemented in child)
1582
+ self._substep_abs_counter = SubStepAbsCounter(namespace=self._namespace,
1583
+ n_envs=self._n_envs,
1584
+ n_steps_lb=1e9,
1585
+ n_steps_ub=1e9,
1586
+ randomize_offsets_at_startup=True, # randomizing startup offsets
1587
+ is_server=True,
1588
+ verbose=self._verbose,
1589
+ vlevel=self._vlevel,
1590
+ safe=True,
1591
+ force_reconnection=True,
1592
+ with_gpu_mirror=self._use_gpu,
1593
+ debug=self._debug)
1594
+ self._substep_abs_counter.run()
1595
+
1596
+ # debug data servers
1597
+ traing_env_param_dict = {}
1598
+ traing_env_param_dict["use_gpu"] = self._use_gpu
1599
+ traing_env_param_dict["debug"] = self._is_debug
1600
+ traing_env_param_dict["n_preinit_steps"] = self._env_opts["n_preinit_steps"]
1601
+ traing_env_param_dict["n_preinit_steps"] = self._n_envs
1602
+
1603
+ self._training_sim_info = SharedTrainingEnvInfo(namespace=self._namespace,
1604
+ is_server=True,
1605
+ training_env_params_dict=traing_env_param_dict,
1606
+ safe=False,
1607
+ force_reconnection=True,
1608
+ verbose=self._verbose,
1609
+ vlevel=self._vlevel)
1610
+ self._training_sim_info.run()
1611
+
1612
+ self._observed_jnt_names=self.set_observed_joints()
1613
+ self._set_jnts_blacklist_pattern()
1614
+ self._update_jnt_blacklist()
1615
+
1616
+ self._prev_root_p_substep=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu).clone()
1617
+ self._prev_root_q_substep=self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu).clone()
1618
+ self._prev_root_p_step=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu).clone()
1619
+ self._prev_root_q_step=self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu).clone()
1620
+
1621
+ def _activate_rhc_controllers(self):
1622
+ self._rhc_status.activation_state.get_torch_mirror()[:, :] = True
1623
+ self._rhc_status.activation_state.synch_all(read=False, retry=True) # activates all controllers
1624
+
1625
+ def _synch_state(self,
1626
+ gpu: bool = True):
1627
+
1628
+ # read from shared memory on CPU
1629
+ # robot state
1630
+ self._robot_state.root_state.synch_all(read = True, retry = True)
1631
+ self._robot_state.jnts_state.synch_all(read = True, retry = True)
1632
+ # rhc cmds
1633
+ self._rhc_cmds.root_state.synch_all(read = True, retry = True)
1634
+ self._rhc_cmds.jnts_state.synch_all(read = True, retry = True)
1635
+ self._rhc_cmds.contact_wrenches.synch_all(read = True, retry = True)
1636
+ # rhc pred
1637
+ self._rhc_pred.root_state.synch_all(read = True, retry = True)
1638
+ # self._rhc_pred.jnts_state.synch_all(read = True, retry = True)
1639
+ # self._rhc_pred.contact_wrenches.synch_all(read = True, retry = True)
1640
+ # refs for root link and contacts
1641
+ self._rhc_refs.rob_refs.root_state.synch_all(read = True, retry = True)
1642
+ self._rhc_refs.contact_flags.synch_all(read = True, retry = True)
1643
+ self._rhc_refs.flight_info.synch_all(read = True, retry = True)
1644
+ self._rhc_refs.flight_settings_req.synch_all(read = True, retry = True)
1645
+ self._rhc_refs.rob_refs.contact_pos.synch_all(read = True, retry = True)
1646
+ # rhc cost
1647
+ self._rhc_status.rhc_cost.synch_all(read = True, retry = True)
1648
+ # rhc constr. violations
1649
+ self._rhc_status.rhc_constr_viol.synch_all(read = True, retry = True)
1650
+ # failure states
1651
+ self._rhc_status.fails.synch_all(read = True, retry = True)
1652
+ # tot cost and cnstr viol on nodes + step variable
1653
+ self._rhc_status.rhc_nodes_cost.synch_all(read = True, retry = True)
1654
+ self._rhc_status.rhc_nodes_constr_viol.synch_all(read = True, retry = True)
1655
+ self._rhc_status.rhc_fcn.synch_all(read = True, retry = True)
1656
+ self._rhc_status.rhc_fail_idx.synch_all(read = True, retry = True)
1657
+ if self._env_opts["add_heightmap_obs"]:
1658
+ self._robot_state.height_sensor.synch_all(read=True, retry=True)
1659
+ if gpu:
1660
+ # copies data to "mirror" on GPU --> we can do it non-blocking since
1661
+ # in this direction it should be safe
1662
+ self._robot_state.root_state.synch_mirror(from_gpu=False,non_blocking=True) # copies shared data on GPU
1663
+ self._robot_state.jnts_state.synch_mirror(from_gpu=False,non_blocking=True)
1664
+ self._rhc_cmds.root_state.synch_mirror(from_gpu=False,non_blocking=True)
1665
+ self._rhc_cmds.jnts_state.synch_mirror(from_gpu=False,non_blocking=True)
1666
+ self._rhc_cmds.contact_wrenches.synch_mirror(from_gpu=False,non_blocking=True)
1667
+ self._rhc_pred.root_state.synch_mirror(from_gpu=False,non_blocking=True)
1668
+ # self._rhc_pred.jnts_state.synch_mirror(from_gpu=False,non_blocking=True)
1669
+ # self._rhc_pred.contact_wrenches.synch_mirror(from_gpu=False,non_blocking=True)
1670
+ self._rhc_refs.rob_refs.root_state.synch_mirror(from_gpu=False,non_blocking=True)
1671
+ self._rhc_refs.contact_flags.synch_mirror(from_gpu=False,non_blocking=True)
1672
+ self._rhc_refs.rob_refs.contact_pos.synch_mirror(from_gpu=False,non_blocking=True)
1673
+ self._rhc_refs.flight_info.synch_mirror(from_gpu=False,non_blocking=True)
1674
+ self._rhc_refs.flight_settings_req.synch_mirror(from_gpu=False,non_blocking=True)
1675
+ self._rhc_status.rhc_cost.synch_mirror(from_gpu=False,non_blocking=True)
1676
+ self._rhc_status.rhc_constr_viol.synch_mirror(from_gpu=False,non_blocking=True)
1677
+ self._rhc_status.fails.synch_mirror(from_gpu=False,non_blocking=True)
1678
+ self._rhc_status.rhc_nodes_cost.synch_mirror(from_gpu=False,non_blocking=True)
1679
+ self._rhc_status.rhc_nodes_constr_viol.synch_mirror(from_gpu=False,non_blocking=True)
1680
+ self._rhc_status.rhc_fcn.synch_mirror(from_gpu=False,non_blocking=True)
1681
+ self._rhc_status.rhc_fail_idx.synch_mirror(from_gpu=False,non_blocking=True)
1682
+ if self._env_opts["add_heightmap_obs"]:
1683
+ self._robot_state.height_sensor.synch_mirror(from_gpu=False, non_blocking=True)
1684
+ torch.cuda.synchronize() # ensuring that all the streams on the GPU are completed \
1685
+ # before the CPU continues execution
1686
+
1687
+ def _override_refs(self,
1688
+ env_indxs: torch.Tensor = None):
1689
+
1690
+ # just used for setting agent refs externally (i.e. from shared mem on CPU)
1691
+ self._agent_refs.rob_refs.root_state.synch_all(read=True,retry=True) # first read from mem
1692
+ if self._use_gpu:
1693
+ # copies latest refs to GPU
1694
+ self._agent_refs.rob_refs.root_state.synch_mirror(from_gpu=False,non_blocking=False)
1695
+
1696
+ def _clamp_obs(self,
1697
+ obs: torch.Tensor):
1698
+ if self._is_debug:
1699
+ self._check_finite(obs, "observations", False)
1700
+ torch.nan_to_num(input=obs, out=obs, nan=self._obs_threshold_ub,
1701
+ posinf=self._obs_threshold_ub,
1702
+ neginf=self._obs_threshold_lb) # prevent nans
1703
+
1704
+ obs.clamp_(self._obs_threshold_lb, self._obs_threshold_ub)
1705
+
1706
+ def _clamp_rewards(self,
1707
+ rewards: torch.Tensor):
1708
+ if self._is_debug:
1709
+ self._check_finite(rewards, "rewards", False)
1710
+ torch.nan_to_num(input=rewards, out=rewards, nan=0.0,
1711
+ posinf=None,
1712
+ neginf=None) # prevent nans
1713
+ rewards.clamp_(self._reward_thresh_lb, self._reward_thresh_ub)
1714
+
1715
+ def get_actions_lb(self):
1716
+ return self._actions_lb
1717
+
1718
+ def get_actions_ub(self):
1719
+ return self._actions_ub
1720
+
1721
+ def get_actions_scale(self):
1722
+ return self._actions_scale
1723
+
1724
+ def get_actions_offset(self):
1725
+ return self._actions_offset
1726
+
1727
+ def get_obs_lb(self):
1728
+ return self._obs_lb
1729
+
1730
+ def get_obs_ub(self):
1731
+ return self._obs_ub
1732
+
1733
+ def get_obs_scale(self):
1734
+ self._obs_scale = (self._obs_ub - self._obs_lb)/2.0
1735
+ return self._obs_scale
1736
+
1737
+ def get_obs_offset(self):
1738
+ self._obs_offset = (self._obs_ub + self._obs_lb)/2.0
1739
+ return self._obs_offset
1740
+
1741
+ def switch_random_reset(self, on: bool = True):
1742
+ self._random_reset_active=on
1743
+
1744
+ def set_jnts_remapping(self,
1745
+ remapping: List = None):
1746
+
1747
+ self._jnts_remapping=remapping
1748
+ if self._jnts_remapping is not None:
1749
+ self._robot_state.set_jnts_remapping(jnts_remapping=self._jnts_remapping)
1750
+ self._rhc_cmds.set_jnts_remapping(jnts_remapping=self._jnts_remapping)
1751
+ self._rhc_pred.set_jnts_remapping(jnts_remapping=self._jnts_remapping)
1752
+ # we need to also update the list of observed joints to match
1753
+ available_joints=self._robot_state.jnt_names()
1754
+ # the remapping ordering
1755
+ self._observed_jnt_names=[]
1756
+ for i in range(len(available_joints)):
1757
+ self._observed_jnt_names.append(available_joints[self._jnts_remapping[i]])
1758
+
1759
+ self._update_jnt_blacklist()
1760
+
1761
+ updated_obs_names=self._get_obs_names() # get updated obs names (should use get_observed_joints
1762
+ # internally, so that jnt names are updated)
1763
+
1764
+ # also update jnt obs names on shared memory
1765
+ names_old=self._obs.get_obs_names()
1766
+ names_old_next=self._next_obs.get_obs_names()
1767
+ names_old[:]=updated_obs_names
1768
+ names_old_next[:]=updated_obs_names
1769
+ self._obs.update_names()
1770
+ self._next_obs.update_names()
1771
+
1772
+ # also update
1773
+ if "Obs" in self.custom_db_data:
1774
+ db_obs_names=self.custom_db_data["Obs"].data_names()
1775
+ db_obs_names[:]=updated_obs_names
1776
+
1777
+ def _check_finite(self,
1778
+ tensor: torch.Tensor,
1779
+ name: str,
1780
+ throw: bool = False):
1781
+ if not torch.isfinite(tensor).all().item():
1782
+ exception = f"Found nonfinite elements in {name} tensor!!"
1783
+ non_finite_idxs=torch.nonzero(~torch.isfinite(tensor))
1784
+ n_nonf_elems=non_finite_idxs.shape[0]
1785
+
1786
+ if name=="observations":
1787
+ for i in range(n_nonf_elems):
1788
+ db_msg=f"{self.obs_names()[non_finite_idxs[i,1]]} (env. {non_finite_idxs[i,0]}):" + \
1789
+ f" {tensor[non_finite_idxs[i,0],non_finite_idxs[i,1]].item()}"
1790
+ print(db_msg)
1791
+ if name=="rewards":
1792
+ for i in range(n_nonf_elems):
1793
+ db_msg=f"{self.sub_rew_names()[non_finite_idxs[i,1]]} (env. {non_finite_idxs[i,0]}):" + \
1794
+ f" {tensor[non_finite_idxs[i,0],non_finite_idxs[i,1]].item()}"
1795
+ print(db_msg)
1796
+ print(tensor)
1797
+ Journal.log(self.__class__.__name__,
1798
+ "_check_finite",
1799
+ exception,
1800
+ LogType.EXCEP,
1801
+ throw_when_excep = throw)
1802
+
1803
+ def _check_controllers_registered(self,
1804
+ retry: bool = False):
1805
+
1806
+ if retry:
1807
+ self._rhc_status.controllers_counter.synch_all(read=True, retry=True)
1808
+ n_connected_controllers = self._rhc_status.controllers_counter.get_torch_mirror()[0, 0].item()
1809
+ while not (n_connected_controllers == self._n_envs):
1810
+ warn = f"Expected {self._n_envs} controllers to be connected during training, " + \
1811
+ f"but got {n_connected_controllers}. Will wait for all to be connected..."
1812
+ Journal.log(self.__class__.__name__,
1813
+ "_check_controllers_registered",
1814
+ warn,
1815
+ LogType.WARN,
1816
+ throw_when_excep = False)
1817
+ nsecs = int(2 * 1000000000)
1818
+ PerfSleep.thread_sleep(nsecs)
1819
+ self._rhc_status.controllers_counter.synch_all(read=True, retry=True)
1820
+ n_connected_controllers = self._rhc_status.controllers_counter.get_torch_mirror()[0, 0].item()
1821
+ info = f"All {n_connected_controllers} controllers connected!"
1822
+ Journal.log(self.__class__.__name__,
1823
+ "_check_controllers_registered",
1824
+ info,
1825
+ LogType.INFO,
1826
+ throw_when_excep = False)
1827
+ return True
1828
+ else:
1829
+ self._rhc_status.controllers_counter.synch_all(read=True, retry=True)
1830
+ n_connected_controllers = self._rhc_status.controllers_counter.get_torch_mirror()[0, 0].item()
1831
+ if not (n_connected_controllers == self._n_envs):
1832
+ exception = f"Expected {self._n_envs} controllers to be connected during training, " + \
1833
+ f"but got {n_connected_controllers}. Aborting..."
1834
+ Journal.log(self.__class__.__name__,
1835
+ "_check_controllers_registered",
1836
+ exception,
1837
+ LogType.EXCEP,
1838
+ throw_when_excep = False)
1839
+ return False
1840
+ return True
1841
+
1842
+ def _check_truncations(self):
1843
+
1844
+ self._check_sub_truncations()
1845
+ sub_truncations = self._sub_truncations.get_torch_mirror(gpu=self._use_gpu)
1846
+ truncations = self._truncations.get_torch_mirror(gpu=self._use_gpu)
1847
+ truncations[:, :] = torch.any(sub_truncations,dim=1,keepdim=True)
1848
+
1849
+ def _check_terminations(self):
1850
+
1851
+ self._check_sub_terminations()
1852
+ sub_terminations = self._sub_terminations.get_torch_mirror(gpu=self._use_gpu)
1853
+ terminations = self._terminations.get_torch_mirror(gpu=self._use_gpu)
1854
+ terminations[:, :] = torch.any(sub_terminations,dim=1,keepdim=True)
1855
+
1856
+ def _check_sub_truncations(self):
1857
+ # default behaviour-> to be overriden by child
1858
+ sub_truncations = self._sub_truncations.get_torch_mirror(gpu=self._use_gpu)
1859
+ sub_truncations[:, 0:1]=self._ep_timeout_counter.time_limits_reached()
1860
+
1861
+ def _check_sub_terminations(self):
1862
+ # default behaviour-> to be overriden by child
1863
+ sub_terminations = self._sub_terminations.get_torch_mirror(gpu=self._use_gpu)
1864
+ robot_q_meas = self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
1865
+ robot_q_pred = self._rhc_cmds.root_state.get(data_type="q",gpu=self._use_gpu)
1866
+
1867
+ # terminate when either the real robot or the prediction from the MPC are capsized
1868
+ check_capsize(quat=robot_q_meas,max_angle=self._max_pitch_angle,
1869
+ output_t=self._is_capsized)
1870
+ check_capsize(quat=robot_q_pred,max_angle=self._max_pitch_angle,
1871
+ output_t=self._is_rhc_capsized)
1872
+
1873
+ sub_terminations[:, 0:1] = self._rhc_status.fails.get_torch_mirror(gpu=self._use_gpu)
1874
+ sub_terminations[:, 1:2] = self._is_capsized
1875
+ sub_terminations[:, 2:3] = self._is_rhc_capsized
1876
+
1877
+ def is_action_continuous(self):
1878
+ return self._is_continuous_actions
1879
+
1880
+ def is_action_discrete(self):
1881
+ return ~self._is_continuous_actions
1882
+
1883
+ @abstractmethod
1884
+ def _pre_substep(self):
1885
+ pass
1886
+
1887
+ @abstractmethod
1888
+ def _custom_post_step(self,episode_finished):
1889
+ pass
1890
+
1891
+ @abstractmethod
1892
+ def _custom_post_substp_post_rew(self):
1893
+ pass
1894
+
1895
+ @abstractmethod
1896
+ def _custom_post_substp_pre_rew(self):
1897
+ pass
1898
+
1899
+ @abstractmethod
1900
+ def _apply_actions_to_rhc(self):
1901
+ pass
1902
+
1903
+ def _override_actions_with_demo(self):
1904
+ pass
1905
+
1906
+ @abstractmethod
1907
+ def _compute_substep_rewards(self):
1908
+ pass
1909
+
1910
+ @abstractmethod
1911
+ def _set_substep_rew(self):
1912
+ pass
1913
+
1914
+ @abstractmethod
1915
+ def _set_substep_obs(self):
1916
+ pass
1917
+
1918
+ @abstractmethod
1919
+ def _compute_step_rewards(self):
1920
+ pass
1921
+
1922
+ @abstractmethod
1923
+ def _fill_substep_obs(self,
1924
+ obs: torch.Tensor):
1925
+ pass
1926
+
1927
+ @abstractmethod
1928
+ def _fill_step_obs(self,
1929
+ obs: torch.Tensor):
1930
+ pass
1931
+
1932
+ @abstractmethod
1933
+ def _randomize_task_refs(self,
1934
+ env_indxs: torch.Tensor = None):
1935
+ pass
1936
+
1937
+ def _custom_post_init(self):
1938
+ pass
1939
+
1940
+ def _get_avrg_substep_root_twist(self,
1941
+ out: torch.Tensor,
1942
+ base_loc: bool = True):
1943
+ # to be called at each substep
1944
+ robot_p_meas = self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)
1945
+ robot_q_meas = self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
1946
+
1947
+ root_v_avrg_w=(robot_p_meas-self._prev_root_p_substep)/self._substep_dt
1948
+ root_omega_avrg_w=quaternion_to_angular_velocity(q_diff=quaternion_difference(self._prev_root_q_substep,robot_q_meas),\
1949
+ dt=self._substep_dt)
1950
+ twist_w=torch.cat((root_v_avrg_w,
1951
+ root_omega_avrg_w),
1952
+ dim=1)
1953
+ if not base_loc:
1954
+ self._prev_root_p_substep[:, :]=robot_p_meas
1955
+ self._prev_root_q_substep[:, :]=robot_q_meas
1956
+ out[:, :]=twist_w
1957
+ # rotate using the current (end-of-substep) orientation for consistency with other signals
1958
+ world2base_frame(t_w=twist_w, q_b=robot_q_meas, t_out=out)
1959
+ self._prev_root_p_substep[:, :]=robot_p_meas
1960
+ self._prev_root_q_substep[:, :]=robot_q_meas
1961
+
1962
+ def _get_avrg_step_root_twist(self,
1963
+ out: torch.Tensor,
1964
+ base_loc: bool = True):
1965
+ # to be called after substeps of actions repeats
1966
+ robot_p_meas = self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)
1967
+ robot_q_meas = self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
1968
+
1969
+ dt=self._substep_dt*self._action_repeat # accounting for frame skipping
1970
+ root_v_avrg_w=(robot_p_meas-self._prev_root_p_step)/(dt)
1971
+ root_omega_avrg_w=quaternion_to_angular_velocity(q_diff=quaternion_difference(self._prev_root_q_step,robot_q_meas),\
1972
+ dt=dt)
1973
+ twist_w=torch.cat((root_v_avrg_w,
1974
+ root_omega_avrg_w),
1975
+ dim=1)
1976
+ if not base_loc:
1977
+ out[:, :]=twist_w
1978
+ # rotate using the current (end-of-step) orientation for consistency with other signals
1979
+ world2base_frame(t_w=twist_w, q_b=robot_q_meas, t_out=out)
1980
+
1981
+ def _get_avrg_rhc_root_twist(self,
1982
+ out: torch.Tensor,
1983
+ base_loc: bool = True):
1984
+
1985
+ rhc_root_p =self._rhc_cmds.root_state.get(data_type="p",gpu=self._use_gpu)
1986
+ rhc_root_q =self._rhc_cmds.root_state.get(data_type="q",gpu=self._use_gpu)
1987
+ rhc_root_p_pred =self._rhc_pred.root_state.get(data_type="p",gpu=self._use_gpu)
1988
+ rhc_root_q_pred =self._rhc_pred.root_state.get(data_type="q",gpu=self._use_gpu)
1989
+
1990
+ rhc_root_v_avrg_rhc_w=(rhc_root_p_pred-rhc_root_p)/self._pred_horizon_rhc
1991
+ rhc_root_omega_avrg_rhc_w=quaternion_to_angular_velocity(q_diff=quaternion_difference(rhc_root_q,rhc_root_q_pred),\
1992
+ dt=self._pred_horizon_rhc)
1993
+
1994
+ rhc_pred_avrg_twist_rhc_w = torch.cat((rhc_root_v_avrg_rhc_w,
1995
+ rhc_root_omega_avrg_rhc_w),
1996
+ dim=1)
1997
+ if not base_loc:
1998
+ out[:, :]=rhc_pred_avrg_twist_rhc_w
1999
+ # to rhc base frame (using first node as reference)
2000
+ world2base_frame(t_w=rhc_pred_avrg_twist_rhc_w, q_b=rhc_root_q, t_out=out)
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/twist_tracking_env.py ADDED
@@ -0,0 +1,1396 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict
2
+
3
+ import os
4
+
5
+ import torch
6
+
7
+ from EigenIPC.PyEigenIPC import VLevel, LogType, Journal
8
+
9
+ from mpc_hive.utilities.shared_data.rhc_data import RobotState, RhcStatus, RhcRefs
10
+ from mpc_hive.utilities.math_utils_torch import world2base_frame, base2world_frame, w2hor_frame
11
+
12
+ from aug_mpc.utils.sys_utils import PathsGetter
13
+ from aug_mpc.utils.timers import PeriodicTimer
14
+ from aug_mpc.utils.episodic_data import EpisodicData
15
+ from aug_mpc.utils.signal_smoother import ExponentialSignalSmoother
16
+ from aug_mpc.utils.math_utils import check_capsize
17
+ from aug_mpc.training_envs.training_env_base import AugMPCTrainingEnvBase
18
+
19
+ class TwistTrackingEnv(AugMPCTrainingEnvBase):
20
+ """Base AugMPC training env that tracks commanded twists by pushing velocity and contact targets into the RHC controller while handling locomotion rewards/resets."""
21
+
22
+ def __init__(self,
23
+ namespace: str,
24
+ actions_dim: int = 10,
25
+ verbose: bool = False,
26
+ vlevel: VLevel = VLevel.V1,
27
+ use_gpu: bool = True,
28
+ dtype: torch.dtype = torch.float32,
29
+ debug: bool = True,
30
+ override_agent_refs: bool = False,
31
+ timeout_ms: int = 60000,
32
+ env_opts: Dict = {}):
33
+
34
+ env_name = "LinVelTrack"
35
+ device = "cuda" if use_gpu else "cpu"
36
+
37
+ self._add_env_opt(env_opts, "srew_drescaling",
38
+ False)
39
+
40
+ self._add_env_opt(env_opts, "step_thresh", 0.) # when step action < thresh, a step is requested
41
+
42
+ # counters settings
43
+ self._add_env_opt(env_opts, "single_task_ref_per_episode",
44
+ True # if True, the task ref is constant over the episode (ie
45
+ # episodes are truncated when task is changed)
46
+ )
47
+ self._add_env_opt(env_opts, "add_angvel_ref_rand", default=True) # randomize also agular vel ref (just z component)
48
+
49
+ self._add_env_opt(env_opts, "episode_timeout_lb",
50
+ 1024)
51
+ self._add_env_opt(env_opts, "episode_timeout_ub",
52
+ 1024)
53
+ self._add_env_opt(env_opts, "n_steps_task_rand_lb",
54
+ 512)
55
+ self._add_env_opt(env_opts, "n_steps_task_rand_ub",
56
+ 512)
57
+ self._add_env_opt(env_opts, "use_random_safety_reset",
58
+ True)
59
+ self._add_env_opt(env_opts, "random_reset_freq",
60
+ 10) # a random reset once every n-episodes (per env)
61
+ self._add_env_opt(env_opts, "use_random_trunc",
62
+ True)
63
+ self._add_env_opt(env_opts, "random_trunc_freq",
64
+ env_opts["episode_timeout_ub"]*5) # to remove temporal correlations between envs
65
+ self._add_env_opt(env_opts, "random_trunc_freq_delta",
66
+ env_opts["episode_timeout_ub"]*2) # to randomize trunc frequency between envs
67
+
68
+ if not env_opts["single_task_ref_per_episode"]:
69
+ env_opts["random_reset_freq"]=int(env_opts["random_reset_freq"]/\
70
+ (env_opts["episode_timeout_lb"]/float(env_opts["n_steps_task_rand_lb"])))
71
+
72
+ self._add_env_opt(env_opts, "action_repeat", 1) # frame skipping (different agent action every action_repeat
73
+ # env substeps)
74
+
75
+ self._add_env_opt(env_opts, "n_preinit_steps", 1) # n steps of the controllers to properly initialize everything
76
+
77
+ self._add_env_opt(env_opts, "vec_ep_freq_metrics_db", 1) # n eps over which debug metrics are reported
78
+ self._add_env_opt(env_opts, "demo_envs_perc", 0.0)
79
+ self._add_env_opt(env_opts, "max_cmd_v", 1.5) # maximum cmd v for lin v actions (single component)
80
+ self._add_env_opt(env_opts, "max_cmd_omega", 1.0) # maximum cmd v for omega v actions (single component)
81
+
82
+ # action smoothing
83
+ self._add_env_opt(env_opts, "use_action_smoothing", False)
84
+ self._add_env_opt(env_opts, "smoothing_horizon_c", 0.01)
85
+ self._add_env_opt(env_opts, "smoothing_horizon_d", 0.03)
86
+
87
+ # whether to smooth vel error signal
88
+ self._add_env_opt(env_opts, "use_track_reward_smoother", False)
89
+ self._add_env_opt(env_opts, "smoothing_horizon_vel_err", 0.08)
90
+ self._add_env_opt(env_opts, "track_rew_smoother", None)
91
+
92
+ # rewards
93
+ self._reward_map={}
94
+ self._reward_lb_map={}
95
+
96
+ self._add_env_opt(env_opts, "reward_lb_default", -0.5)
97
+ self._add_env_opt(env_opts, "reward_ub_default", 1e6)
98
+
99
+ self._add_env_opt(env_opts, "task_error_reward_lb", -0.5)
100
+ self._add_env_opt(env_opts, "CoT_reward_lb", -0.5)
101
+ self._add_env_opt(env_opts, "power_reward_lb", -0.5)
102
+ self._add_env_opt(env_opts, "action_rate_reward_lb", -0.5)
103
+ self._add_env_opt(env_opts, "jnt_vel_reward_lb", -0.5)
104
+ self._add_env_opt(env_opts, "rhc_avrg_vel_reward_lb", -0.5)
105
+
106
+ self._add_env_opt(env_opts, "add_power_reward", False)
107
+ self._add_env_opt(env_opts, "add_CoT_reward", True)
108
+ self._add_env_opt(env_opts, "use_CoT_wrt_ref", False)
109
+ self._add_env_opt(env_opts, "add_action_rate_reward", True)
110
+ self._add_env_opt(env_opts, "add_jnt_v_reward", False)
111
+
112
+ self._add_env_opt(env_opts, "use_rhc_avrg_vel_tracking", False)
113
+
114
+ # task tracking
115
+ self._add_env_opt(env_opts, "use_relative_error", default=False) # use relative vel error (wrt current task norm)
116
+ self._add_env_opt(env_opts, "directional_tracking", default=True) # whether to compute tracking error based on reference direction
117
+ # if env_opts["add_angvel_ref_rand"]:
118
+ # env_opts["directional_tracking"]=False
119
+
120
+ self._add_env_opt(env_opts, "use_L1_norm", default=True) # whether to use L1 norm for the error (otherwise L2)
121
+ self._add_env_opt(env_opts, "use_exp_track_rew", default=True) # whether to use a reward of the form A*e^(B*x),
122
+ # otherwise A*(1-B*x)
123
+
124
+ self._add_env_opt(env_opts, "use_fail_idx_weight", default=False)
125
+ self._add_env_opt(env_opts, "task_track_offset_exp", default=1.0)
126
+ self._add_env_opt(env_opts, "task_track_scale_exp", default=5.0)
127
+ self._add_env_opt(env_opts, "task_track_offset", default=1.0)
128
+ self._add_env_opt(env_opts, "task_track_scale", default=1.5)
129
+ self._add_env_opt(env_opts, "task_track_front_weight", default=1.0)
130
+ self._add_env_opt(env_opts, "task_track_lat_weight", default=0.05)
131
+ self._add_env_opt(env_opts, "task_track_vert_weight", default=0.05)
132
+ self._add_env_opt(env_opts, "task_track_omega_z_weight", default=0.2)
133
+ self._add_env_opt(env_opts, "task_track_omega_x_weight", default=0.05)
134
+ self._add_env_opt(env_opts, "task_track_omega_y_weight", default=0.05)
135
+ # if env_opts["add_angvel_ref_rand"]:
136
+ # env_opts["task_track_omega_x_weight"]=0.0
137
+ # env_opts["task_track_omega_y_weight"]=0.0
138
+ # env_opts["task_track_omega_z_weight"]=1.0
139
+
140
+ # task pred tracking
141
+ self._add_env_opt(env_opts, "task_pred_track_offset", default=1.0)
142
+ self._add_env_opt(env_opts, "task_pred_track_scale", default=3.0)
143
+
144
+ # energy penalties
145
+ self._add_env_opt(env_opts, "CoT_offset", default=0.3)
146
+ self._add_env_opt(env_opts, "CoT_scale", default=0.3)
147
+ self._add_env_opt(env_opts, "power_offset", default=0.1)
148
+ self._add_env_opt(env_opts, "power_scale", default=8e-4)
149
+
150
+ # action rate penalty
151
+ self._add_env_opt(env_opts, "action_rate_offset", default=0.1)
152
+ self._add_env_opt(env_opts, "action_rate_scale", default=2.0)
153
+ self._add_env_opt(env_opts, "action_rate_rew_d_weight", default=0.1)
154
+ self._add_env_opt(env_opts, "action_rate_rew_c_weight", default=1.0)
155
+
156
+ # jnt vel penalty
157
+ self._add_env_opt(env_opts, "jnt_vel_offset", default=0.1)
158
+ self._add_env_opt(env_opts, "jnt_vel_scale", default=2.0)
159
+
160
+ # terminations
161
+ self._add_env_opt(env_opts, "add_term_mpc_capsize", default=False) # add termination based on mpc capsizing prediction
162
+
163
+ # observations
164
+ self._add_env_opt(env_opts, "rhc_fail_idx_scale", default=1.0)
165
+ self._add_env_opt(env_opts, "use_action_history", default=True) # whether to add information on past actions to obs
166
+ self._add_env_opt(env_opts, "add_prev_actions_stats_to_obs", default=False) # add actions std, mean + last action over a horizon to obs (if self._use_action_history True)
167
+ self._add_env_opt(env_opts, "actions_history_size", default=3)
168
+
169
+ self._add_env_opt(env_opts, "add_mpc_contact_f_to_obs", default=True) # add estimate vertical contact f to obs
170
+ self._add_env_opt(env_opts, "add_fail_idx_to_obs", default=True) # we need to obserse mpc failure idx to correlate it with terminations
171
+
172
+ self._add_env_opt(env_opts, "use_linvel_from_rhc", default=True) # no lin vel meas available, we use est. from mpc
173
+ self._add_env_opt(env_opts, "add_flight_info", default=True) # add feedback info on pos, remamining duration, length,
174
+ # apex and landing height of flight phases
175
+ self._add_env_opt(env_opts, "add_flight_settings", default=False) # add feedback info on current flight requests for mpc
176
+
177
+ self._add_env_opt(env_opts, "use_prob_based_stepping", default=False) # interpret actions as stepping prob (never worked)
178
+
179
+ self._add_env_opt(env_opts, "add_rhc_cmds_to_obs", default=True) # add the rhc cmds which are being applied now to the robot
180
+
181
+ if not "add_periodic_clock_to_obs" in env_opts:
182
+ # add a sin/cos clock to obs (useful if task is explicitly
183
+ # time-dependent)
184
+ self._add_env_opt(env_opts, "add_periodic_clock_to_obs", default=False)
185
+
186
+ self._add_env_opt(env_opts, "add_heightmap_obs", default=True)
187
+
188
+ # temporarily creating robot state client to get some data
189
+ robot_state_tmp = RobotState(namespace=namespace,
190
+ is_server=False,
191
+ safe=False,
192
+ verbose=verbose,
193
+ vlevel=vlevel,
194
+ with_gpu_mirror=False,
195
+ with_torch_view=False,
196
+ enable_height_sensor=env_opts["add_heightmap_obs"])
197
+ robot_state_tmp.run()
198
+ rhc_status_tmp = RhcStatus(is_server=False,
199
+ namespace=namespace,
200
+ verbose=verbose,
201
+ vlevel=vlevel,
202
+ with_torch_view=False,
203
+ with_gpu_mirror=False)
204
+ rhc_status_tmp.run()
205
+ rhc_refs_tmp = RhcRefs(namespace=namespace,
206
+ is_server=False,
207
+ safe=False,
208
+ verbose=verbose,
209
+ vlevel=vlevel,
210
+ with_gpu_mirror=False,
211
+ with_torch_view=False)
212
+ rhc_refs_tmp.run()
213
+ n_jnts = robot_state_tmp.n_jnts()
214
+ self._contact_names = robot_state_tmp.contact_names()
215
+ self._n_contacts = len(self._contact_names)
216
+ self._flight_info_size=rhc_refs_tmp.flight_info.n_cols
217
+ self._flight_setting_size=rhc_refs_tmp.flight_settings_req.n_cols
218
+ # height sensor metadata (if present)
219
+ self._height_grid_size = None
220
+ self._height_flat_dim = 0
221
+ if env_opts["add_heightmap_obs"]:
222
+ self._height_grid_size = robot_state_tmp.height_sensor.grid_size
223
+ self._height_flat_dim = robot_state_tmp.height_sensor.n_cols
224
+
225
+ robot_state_tmp.close()
226
+ rhc_status_tmp.close()
227
+ rhc_refs_tmp.close()
228
+
229
+ # defining obs dimension
230
+ obs_dim=3 # normalized gravity vector in base frame
231
+ obs_dim+=6 # meas twist in base frame
232
+ obs_dim+=2*n_jnts # joint pos + vel
233
+ if env_opts["add_mpc_contact_f_to_obs"]:
234
+ obs_dim+=3*self._n_contacts
235
+ obs_dim+=6 # twist reference in base frame frame
236
+ if env_opts["add_fail_idx_to_obs"]:
237
+ obs_dim+=1 # rhc controller failure index
238
+ if env_opts["add_term_mpc_capsize"]:
239
+ obs_dim+=3 # gravity vec from mpc
240
+ if env_opts["use_rhc_avrg_vel_tracking"]:
241
+ obs_dim+=6 # mpc avrg twist
242
+ if env_opts["add_flight_info"]: # contact pos, remaining duration, length, apex, landing height, landing dx, dy
243
+ obs_dim+=self._flight_info_size
244
+ if env_opts["add_flight_settings"]:
245
+ obs_dim+=self._flight_setting_size
246
+ if env_opts["add_rhc_cmds_to_obs"]:
247
+ obs_dim+=3*n_jnts
248
+ if env_opts["use_action_history"]:
249
+ if env_opts["add_prev_actions_stats_to_obs"]:
250
+ obs_dim+=3*actions_dim # previous agent actions statistics (mean, std + last action)
251
+ else: # full action history
252
+ obs_dim+=env_opts["actions_history_size"]*actions_dim
253
+ if env_opts["use_action_smoothing"]:
254
+ obs_dim+=actions_dim # it's better to also add the smoothed actions as obs
255
+ if env_opts["add_periodic_clock_to_obs"]:
256
+ obs_dim+=2
257
+ if env_opts["add_heightmap_obs"]:
258
+ obs_dim+=self._height_flat_dim
259
+ # Agent task reference
260
+ self._add_env_opt(env_opts, "use_pof0", default=True) # with some prob, references will be null
261
+ self._add_env_opt(env_opts, "pof0_linvel", default=0.3) # [0, 1] prob of both linvel and omega refs being null(from bernoulli distr)
262
+ self._add_env_opt(env_opts, "pof0_omega", default=0.3) # [0, 1] prob of both linvel and omega refs being null(from bernoulli distr)
263
+ self._add_env_opt(env_opts, "max_linvel_ref", default=0.3) # m/s
264
+ self._add_env_opt(env_opts, "max_angvel_ref", default=0.0) # rad/s
265
+ if env_opts["add_angvel_ref_rand"]:
266
+ env_opts["max_angvel_ref"]=0.4
267
+
268
+ # ready to init base class
269
+ self._this_child_path = os.path.abspath(__file__)
270
+ AugMPCTrainingEnvBase.__init__(self,
271
+ namespace=namespace,
272
+ obs_dim=obs_dim,
273
+ actions_dim=actions_dim,
274
+ env_name=env_name,
275
+ verbose=verbose,
276
+ vlevel=vlevel,
277
+ use_gpu=use_gpu,
278
+ dtype=dtype,
279
+ debug=debug,
280
+ override_agent_refs=override_agent_refs,
281
+ timeout_ms=timeout_ms,
282
+ env_opts=env_opts)
283
+
284
+ def _custom_post_init(self):
285
+
286
+ device = "cuda" if self._use_gpu else "cpu"
287
+
288
+ self._update_jnt_blacklist() # update blacklist for joints
289
+
290
+ # constant base-frame unit vectors (reuse to avoid per-call allocations)
291
+ self._base_x_dir = torch.zeros((self._n_envs, 3), dtype=self._dtype, device=device)
292
+ self._base_x_dir[:, 0] = 1.0
293
+ self._base_y_dir = torch.zeros((self._n_envs, 3), dtype=self._dtype, device=device)
294
+ self._base_y_dir[:, 1] = 1.0
295
+
296
+ self._twist_ref_lb = torch.full((1, 6), dtype=self._dtype, device=device,
297
+ fill_value=-1.5)
298
+ self._twist_ref_ub = torch.full((1, 6), dtype=self._dtype, device=device,
299
+ fill_value=1.5)
300
+
301
+ # task reference parameters (world frame)
302
+ # lin vel
303
+ self._twist_ref_lb[0, 0] = -self._env_opts["max_linvel_ref"]
304
+ self._twist_ref_lb[0, 1] = -self._env_opts["max_linvel_ref"]
305
+ self._twist_ref_lb[0, 2] = 0.0
306
+ self._twist_ref_ub[0, 0] = self._env_opts["max_linvel_ref"]
307
+ self._twist_ref_ub[0, 1] = self._env_opts["max_linvel_ref"]
308
+ self._twist_ref_ub[0, 2] = 0.0
309
+ # angular vel
310
+ self._twist_ref_lb[0, 3] = 0.0
311
+ self._twist_ref_lb[0, 4] = 0.0
312
+ self._twist_ref_lb[0, 5] = -self._env_opts["max_angvel_ref"]
313
+ self._twist_ref_ub[0, 3] = 0.0
314
+ self._twist_ref_ub[0, 4] = 0.0
315
+ self._twist_ref_ub[0, 5] = self._env_opts["max_angvel_ref"]
316
+
317
+ self._twist_ref_offset = (self._twist_ref_ub + self._twist_ref_lb)/2.0
318
+ self._twist_ref_scale = (self._twist_ref_ub - self._twist_ref_lb)/2.0
319
+
320
+ # adding some custom db info
321
+ agent_twist_ref = self._agent_refs.rob_refs.root_state.get(data_type="twist",gpu=False)
322
+ agent_twist_ref_data = EpisodicData("AgentTwistRefs", agent_twist_ref,
323
+ ["v_x", "v_y", "v_z", "omega_x", "omega_y", "omega_z"],
324
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
325
+ store_transitions=self._full_db,
326
+ max_ep_duration=self._max_ep_length())
327
+ rhc_fail_idx = EpisodicData("RhcFailIdx", self._rhc_fail_idx(gpu=False), ["rhc_fail_idx"],
328
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
329
+ store_transitions=self._full_db,
330
+ max_ep_duration=self._max_ep_length())
331
+
332
+ f_names=[]
333
+ for contact in self._contact_names:
334
+ f_names.append(f"fc_{contact}_x_base_loc")
335
+ f_names.append(f"fc_{contact}_y_base_loc")
336
+ f_names.append(f"fc_{contact}_z_base_loc")
337
+ rhc_contact_f = EpisodicData("RhcContactForces",
338
+ self._rhc_cmds.contact_wrenches.get(data_type="f",gpu=False),
339
+ f_names,
340
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
341
+ store_transitions=self._full_db,
342
+ max_ep_duration=self._max_ep_length())
343
+
344
+ self._pow_db_data=torch.full(size=(self._n_envs,2),
345
+ dtype=self._dtype, device="cpu",
346
+ fill_value=-1.0)
347
+ power_db = EpisodicData("Power",
348
+ self._pow_db_data,
349
+ ["CoT", "W"],
350
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
351
+ store_transitions=self._full_db,
352
+ max_ep_duration=self._max_ep_length())
353
+
354
+ self._track_error_db=torch.full_like(agent_twist_ref, fill_value=0.0)
355
+ task_err_db = EpisodicData("TrackingError",
356
+ agent_twist_ref,
357
+ ["e_vx", "e_vy", "e_vz", "e_omegax", "e_omegay", "e_omegaz"],
358
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
359
+ store_transitions=self._full_db,
360
+ max_ep_duration=self._max_ep_length())
361
+
362
+ self._add_custom_db_data(db_data=agent_twist_ref_data)
363
+ self._add_custom_db_data(db_data=rhc_fail_idx)
364
+ self._add_custom_db_data(db_data=rhc_contact_f)
365
+ self._add_custom_db_data(db_data=power_db)
366
+ self._add_custom_db_data(db_data=task_err_db)
367
+
368
+ # rewards
369
+ self._task_err_weights = torch.full((1, 6), dtype=self._dtype, device=device,
370
+ fill_value=0.0)
371
+ if self._env_opts["directional_tracking"]:
372
+ self._task_err_weights[0, 0] = self._env_opts["task_track_front_weight"] # frontal
373
+ self._task_err_weights[0, 1] = self._env_opts["task_track_lat_weight"] # lateral
374
+ self._task_err_weights[0, 2] = self._env_opts["task_track_vert_weight"] # vertical
375
+ self._task_err_weights[0, 3] = self._env_opts["task_track_omega_x_weight"]
376
+ self._task_err_weights[0, 4] = self._env_opts["task_track_omega_y_weight"]
377
+ self._task_err_weights[0, 5] = self._env_opts["task_track_omega_z_weight"]
378
+ else:
379
+ self._task_err_weights[0, 0] = self._env_opts["task_track_front_weight"]
380
+ self._task_err_weights[0, 1] = self._env_opts["task_track_front_weight"]
381
+ self._task_err_weights[0, 2] = 0.1*self._env_opts["task_track_front_weight"]
382
+ self._task_err_weights[0, 3] = self._env_opts["task_track_omega_x_weight"]
383
+ self._task_err_weights[0, 4] = self._env_opts["task_track_omega_y_weight"]
384
+ self._task_err_weights[0, 5] = self._env_opts["task_track_omega_z_weight"]
385
+
386
+ self._task_pred_err_weights = torch.full((1, 6), dtype=self._dtype, device=device,
387
+ fill_value=0.0)
388
+ if self._env_opts["directional_tracking"]:
389
+ self._task_pred_err_weights[0, 0] = self._env_opts["task_track_front_weight"]
390
+ self._task_pred_err_weights[0, 1] = self._env_opts["task_track_lat_weight"]
391
+ self._task_pred_err_weights[0, 2] = self._env_opts["task_track_vert_weight"]
392
+ self._task_pred_err_weights[0, 3] = self._env_opts["task_track_omega_x_weight"]
393
+ self._task_pred_err_weights[0, 4] = self._env_opts["task_track_omega_y_weight"]
394
+ self._task_pred_err_weights[0, 5] = self._env_opts["task_track_omega_z_weight"]
395
+ else:
396
+ self._task_pred_err_weights[0, 0] = self._env_opts["task_track_front_weight"]
397
+ self._task_pred_err_weights[0, 1] = self._env_opts["task_track_front_weight"]
398
+ self._task_pred_err_weights[0, 2] = 0.1*self._env_opts["task_track_front_weight"]
399
+ self._task_pred_err_weights[0, 3] = self._env_opts["task_track_omega_x_weight"]
400
+ self._task_pred_err_weights[0, 4] = self._env_opts["task_track_omega_y_weight"]
401
+ self._task_pred_err_weights[0, 5] = self._env_opts["task_track_omega_z_weight"]
402
+
403
+ self._power_penalty_weights = torch.full((1, self._n_jnts), dtype=self._dtype, device=device,
404
+ fill_value=1.0)
405
+ self._power_penalty_weights_sum = torch.sum(self._power_penalty_weights).item()
406
+ subr_names=self._get_rewards_names() # initializes
407
+
408
+ # reward clipping
409
+ self._reward_thresh_lb[:, :] = self._env_opts["reward_lb_default"]
410
+ self._reward_thresh_ub[:, :]= self._env_opts["reward_ub_default"]
411
+
412
+ for reward_name, env_opt_key in self._reward_lb_map.items():
413
+ if reward_name in self._reward_map:
414
+ self._reward_thresh_lb[:, self._reward_map[reward_name]] = self._env_opts[env_opt_key]
415
+
416
+ # obs bounds
417
+ self._obs_threshold_lb = -1e3 # used for clipping observations
418
+ self._obs_threshold_ub = 1e3
419
+
420
+ # actions
421
+ if not self._env_opts["use_prob_based_stepping"]:
422
+ self._is_continuous_actions[6:10]=False
423
+
424
+ v_cmd_max = self._env_opts["max_cmd_v"]
425
+ omega_cmd_max = self._env_opts["max_cmd_omega"]
426
+ self._actions_lb[:, 0:3] = -v_cmd_max
427
+ self._actions_ub[:, 0:3] = v_cmd_max
428
+ self._actions_lb[:, 3:6] = -omega_cmd_max # twist cmds
429
+ self._actions_ub[:, 3:6] = omega_cmd_max
430
+ if "contact_flag_start" in self._actions_map:
431
+ idx=self._actions_map["contact_flag_start"]
432
+ if self._env_opts["use_prob_based_stepping"]:
433
+ self._actions_lb[:, idx:idx+self._n_contacts] = 0.0 # contact flags
434
+ self._actions_ub[:, idx:idx+self._n_contacts] = 1.0
435
+ else:
436
+ self._actions_lb[:, idx:idx+self._n_contacts] = -1.0
437
+ self._actions_ub[:, idx:idx+self._n_contacts] = 1.0
438
+
439
+ self.default_action[:, :] = (self._actions_ub+self._actions_lb)/2.0
440
+ # self.default_action[:, ~self._is_continuous_actions] = 1.0
441
+ self.safe_action[:, :] = self.default_action
442
+ if "contact_flag_start" in self._actions_map: # safe actions for contacts is 1 (keep contact)
443
+ idx=self._actions_map["contact_flag_start"]
444
+ self.safe_action[:, idx:idx+self._n_contacts] = 1.0
445
+
446
+ # assign obs bounds (useful if not using automatic obs normalization)
447
+ obs_names=self._get_obs_names()
448
+ obs_patterns=["gn",
449
+ "linvel",
450
+ "omega",
451
+ "q_jnt",
452
+ "v_jnt",
453
+ "fc",
454
+ "rhc_fail",
455
+ "rhc_cmd_q",
456
+ "rhc_cmd_v",
457
+ "rhc_cmd_eff",
458
+ "flight_pos"
459
+ ]
460
+ obs_ubs=[1.0,
461
+ 5*v_cmd_max,
462
+ 5*omega_cmd_max,
463
+ 2*torch.pi,
464
+ 30.0,
465
+ 2.0,
466
+ 1.0,
467
+ 2*torch.pi,
468
+ 30.0,
469
+ 200.0,
470
+ self._n_nodes_rhc.mean().item()]
471
+ obs_lbs=[-1.0,
472
+ -5*v_cmd_max,
473
+ -5*omega_cmd_max,
474
+ -2*torch.pi,
475
+ -30.0,
476
+ -2.0,
477
+ 0.0,
478
+ -2*torch.pi,
479
+ -30.0,
480
+ -200.0,
481
+ 0.0]
482
+ obs_bounds = {name: (lb, ub) for name, lb, ub in zip(obs_patterns, obs_lbs, obs_ubs)}
483
+
484
+ for i in range(len(obs_names)):
485
+ obs_name=obs_names[i]
486
+ for pattern in obs_patterns:
487
+ if pattern in obs_name:
488
+ lb=obs_bounds[pattern][0]
489
+ ub=obs_bounds[pattern][1]
490
+ self._obs_lb[:, i]=lb
491
+ self._obs_ub[:, i]=ub
492
+ break
493
+
494
+ # handle action memory buffer in obs
495
+ if self._env_opts["use_action_history"]: # just history stats
496
+ if self._env_opts["add_prev_actions_stats_to_obs"]:
497
+ i=0
498
+ prev_actions_idx = next((i for i, s in enumerate(obs_names) if "_prev_act" in s), None)
499
+ prev_actions_mean_idx=next((i for i, s in enumerate(obs_names) if "_avrg_act" in s), None)
500
+ prev_actions_std_idx=next((i for i, s in enumerate(obs_names) if "_std_act" in s), None)
501
+
502
+ # assume actions are always normalized in [-1, 1] by agent
503
+ if prev_actions_idx is not None:
504
+ self._obs_lb[:, prev_actions_idx:prev_actions_idx+self.actions_dim()]=-1.0
505
+ self._obs_ub[:, prev_actions_idx:prev_actions_idx+self.actions_dim()]=1.0
506
+ if prev_actions_mean_idx is not None:
507
+ self._obs_lb[:, prev_actions_mean_idx:prev_actions_mean_idx+self.actions_dim()]=-1.0
508
+ self._obs_ub[:, prev_actions_mean_idx:prev_actions_mean_idx+self.actions_dim()]=1.0
509
+ if prev_actions_std_idx is not None:
510
+ self._obs_lb[:, prev_actions_std_idx:prev_actions_std_idx+self.actions_dim()]=0
511
+ self._obs_ub[:, prev_actions_std_idx:prev_actions_std_idx+self.actions_dim()]=1.0
512
+
513
+ else: # full history
514
+ i=0
515
+ first_action_mem_buffer_idx = next((i for i, s in enumerate(obs_names) if "_m1_act" in s), None)
516
+ if first_action_mem_buffer_idx is not None:
517
+ action_idx_start_idx_counter=first_action_mem_buffer_idx
518
+ for j in range(self._env_opts["actions_history_size"]):
519
+ self._obs_lb[:, action_idx_start_idx_counter:action_idx_start_idx_counter+self.actions_dim()]=-1.0
520
+ self._obs_ub[:, action_idx_start_idx_counter:action_idx_start_idx_counter+self.actions_dim()]=1.0
521
+ action_idx_start_idx_counter+=self.actions_dim()
522
+
523
+ # some aux data to avoid allocations at training runtime
524
+ self._rhc_twist_cmd_rhc_world=self._robot_state.root_state.get(data_type="twist",gpu=self._use_gpu).detach().clone()
525
+ self._rhc_twist_cmd_rhc_h=self._rhc_twist_cmd_rhc_world.detach().clone()
526
+ self._agent_twist_ref_current_w=self._rhc_twist_cmd_rhc_world.detach().clone()
527
+ self._agent_twist_ref_current_base_loc=self._rhc_twist_cmd_rhc_world.detach().clone()
528
+ self._substep_avrg_root_twist_base_loc=self._rhc_twist_cmd_rhc_world.detach().clone()
529
+ self._step_avrg_root_twist_base_loc=self._rhc_twist_cmd_rhc_world.detach().clone()
530
+ self._root_twist_avrg_rhc_base_loc=self._rhc_twist_cmd_rhc_world.detach().clone()
531
+ self._root_twist_avrg_rhc_base_loc_next=self._rhc_twist_cmd_rhc_world.detach().clone()
532
+
533
+ self._random_thresh_contacts=torch.rand((self._n_envs,self._n_contacts), device=device)
534
+ # aux data
535
+ self._task_err_scaling = torch.zeros((self._n_envs, 1),dtype=self._dtype,device=device)
536
+
537
+ self._pof1_b_linvel= torch.full(size=(self._n_envs,1),dtype=self._dtype,device=device,fill_value=1-self._env_opts["pof0_linvel"])
538
+ self._pof1_b_omega = torch.full(size=(self._n_envs,1),dtype=self._dtype,device=device,fill_value=1-self._env_opts["pof0_omega"])
539
+ self._bernoulli_coeffs_linvel = self._pof1_b_linvel.clone()
540
+ self._bernoulli_coeffs_linvel[:, :] = 1.0
541
+ self._bernoulli_coeffs_omega = self._pof1_b_omega.clone()
542
+ self._bernoulli_coeffs_omega[:, :] = 1.0
543
+
544
+ # smoothing
545
+ self._track_rew_smoother=None
546
+ if self._env_opts["use_track_reward_smoother"]:
547
+ sub_reward_proxy=self._sub_rewards.get_torch_mirror(gpu=self._use_gpu)[:, 0:1]
548
+ smoothing_dt=self._substep_dt
549
+ if not self._is_substep_rew[self._reward_map["task_error"]]: # assuming first reward is tracking
550
+ smoothing_dt=self._substep_dt*self._action_repeat
551
+ self._track_rew_smoother=ExponentialSignalSmoother(
552
+ name=self.__class__.__name__+"VelErrorSmoother",
553
+ signal=sub_reward_proxy, # same dimension of vel error
554
+ update_dt=smoothing_dt,
555
+ smoothing_horizon=self._env_opts["smoothing_horizon_vel_err"],
556
+ target_smoothing=0.5,
557
+ debug=self._is_debug,
558
+ dtype=self._dtype,
559
+ use_gpu=self._use_gpu)
560
+
561
+ # if we need the action rate, we also need the action history
562
+ if self._env_opts["add_action_rate_reward"]:
563
+ if not self._env_opts["use_action_history"]:
564
+ Journal.log(self.__class__.__name__,
565
+ "_custom_post_init",
566
+ "add_action_rate_reward is True, but ",
567
+ LogType.EXCEP,
568
+ throw_when_excep=True)
569
+
570
+ history_size=self._env_opts["actions_history_size"]
571
+ if history_size < 2:
572
+ Journal.log(self.__class__.__name__,
573
+ "_custom_post_init",
574
+ f"add_action_rate_reward requires actions history ({history_size}) to be >=2!",
575
+ LogType.EXCEP,
576
+ throw_when_excep=True)
577
+
578
+ # add periodic timer if required
579
+ self._periodic_clock=None
580
+ if self._env_opts["add_periodic_clock_to_obs"]:
581
+ self._add_env_opt(self._env_opts, "clock_period",
582
+ default=int(1.5*self._action_repeat*self.task_rand_timeout_bounds()[1])) # correcting with n substeps
583
+ # (we are using the _substep_abs_counter counter)
584
+ self._periodic_clock=PeriodicTimer(counter=self._substep_abs_counter,
585
+ period=self._env_opts["clock_period"],
586
+ dtype=self._dtype,
587
+ device=self._device)
588
+
589
+ def get_file_paths(self):
590
+ paths=AugMPCTrainingEnvBase.get_file_paths(self)
591
+ paths.append(self._this_child_path)
592
+ return paths
593
+
594
+ def get_aux_dir(self):
595
+ aux_dirs = []
596
+ path_getter = PathsGetter()
597
+ aux_dirs.append(path_getter.RHCDIR)
598
+ return aux_dirs
599
+
600
+ def _get_reward_scaling(self):
601
+ if self._env_opts["single_task_ref_per_episode"]:
602
+ return self._env_opts["n_steps_task_rand_ub"]
603
+ else:
604
+ return self._env_opts["episode_timeout_ub"]
605
+
606
+ def _max_ep_length(self):
607
+ if self._env_opts["single_task_ref_per_episode"]:
608
+ return self._env_opts["n_steps_task_rand_ub"]
609
+ else:
610
+ return self._env_opts["episode_timeout_ub"]
611
+
612
+ def _check_sub_truncations(self):
613
+ # overrides parent
614
+ sub_truncations = self._sub_truncations.get_torch_mirror(gpu=self._use_gpu)
615
+ sub_truncations[:, 0:1] = self._ep_timeout_counter.time_limits_reached()
616
+ if self._env_opts["single_task_ref_per_episode"]:
617
+ sub_truncations[:, 1:2] = self._task_rand_counter.time_limits_reached()
618
+
619
+ def _check_sub_terminations(self):
620
+ # default behaviour-> to be overriden by child
621
+ sub_terminations = self._sub_terminations.get_torch_mirror(gpu=self._use_gpu)
622
+
623
+ # terminate if mpc just failed
624
+ sub_terminations[:, 0:1] = self._rhc_status.fails.get_torch_mirror(gpu=self._use_gpu)
625
+
626
+ # check if robot is capsizing
627
+ robot_q_meas = self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
628
+ check_capsize(quat=robot_q_meas,max_angle=self._max_pitch_angle,
629
+ output_t=self._is_capsized)
630
+ sub_terminations[:, 1:2] = self._is_capsized
631
+
632
+ if self._env_opts["add_term_mpc_capsize"]:
633
+ # check if robot is about to capsize accordin to MPC
634
+ robot_q_pred = self._rhc_cmds.root_state.get(data_type="q",gpu=self._use_gpu)
635
+ check_capsize(quat=robot_q_pred,max_angle=self._max_pitch_angle,
636
+ output_t=self._is_rhc_capsized)
637
+ sub_terminations[:, 2:3] = self._is_rhc_capsized
638
+
639
+ def _custom_reset(self):
640
+ return None
641
+
642
+ def reset(self):
643
+ AugMPCTrainingEnvBase.reset(self)
644
+
645
+ def _pre_substep(self):
646
+ pass
647
+
648
+ def _custom_post_step(self,episode_finished):
649
+ # executed after checking truncations and terminations and remote env reset
650
+ if self._use_gpu:
651
+ time_to_rand_or_ep_finished = torch.logical_or(self._task_rand_counter.time_limits_reached().cuda(),episode_finished)
652
+ self.randomize_task_refs(env_indxs=time_to_rand_or_ep_finished.flatten())
653
+ else:
654
+ time_to_rand_or_ep_finished = torch.logical_or(self._task_rand_counter.time_limits_reached(),episode_finished)
655
+ self.randomize_task_refs(env_indxs=time_to_rand_or_ep_finished.flatten())
656
+ # task refs are randomized in world frame -> we rotate them in base local
657
+ # (not super efficient, we should do it just for the finished envs)
658
+ self._update_loc_twist_refs()
659
+
660
+ if self._track_rew_smoother is not None: # reset smoother
661
+ self._track_rew_smoother.reset_all(to_be_reset=episode_finished.flatten(),
662
+ value=0.0)
663
+
664
+ def _custom_post_substp_pre_rew(self):
665
+ self._update_loc_twist_refs()
666
+
667
+ def _custom_post_substp_post_rew(self):
668
+ pass
669
+
670
+ def _update_loc_twist_refs(self):
671
+ # get fresh robot orientation
672
+ if not self._override_agent_refs:
673
+ robot_q = self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
674
+ # rotate agent ref from world to robot base
675
+ world2base_frame(t_w=self._agent_twist_ref_current_w, q_b=robot_q,
676
+ t_out=self._agent_twist_ref_current_base_loc)
677
+ # write it to agent refs tensors
678
+ self._agent_refs.rob_refs.root_state.set(data_type="twist", data=self._agent_twist_ref_current_base_loc,
679
+ gpu=self._use_gpu)
680
+
681
+ def _apply_actions_to_rhc(self):
682
+
683
+ self._set_rhc_refs()
684
+
685
+ self._write_rhc_refs()
686
+
687
+ def _set_rhc_refs(self):
688
+
689
+ action_to_be_applied = self.get_actual_actions() # see _get_action_names() to get
690
+ # the meaning of each component of this tensor
691
+
692
+ rhc_latest_twist_cmd = self._rhc_refs.rob_refs.root_state.get(data_type="twist", gpu=self._use_gpu)
693
+ rhc_latest_contact_ref = self._rhc_refs.contact_flags.get_torch_mirror(gpu=self._use_gpu)
694
+ rhc_latest_pos_ref = self._rhc_refs.rob_refs.contact_pos.get(data_type="p_z", gpu=self._use_gpu)
695
+ rhc_q=self._rhc_cmds.root_state.get(data_type="q",gpu=self._use_gpu) # this is always
696
+ # avaialble
697
+
698
+ # reference twist for MPC is assumed to always be specified in MPC's
699
+ # horizontal frame, while agent actions are interpreted as in MPC's
700
+ # base frame -> we need to rotate the actions into the horizontal frame
701
+ base2world_frame(t_b=action_to_be_applied[:, 0:6],q_b=rhc_q,t_out=self._rhc_twist_cmd_rhc_world)
702
+ w2hor_frame(t_w=self._rhc_twist_cmd_rhc_world,q_b=rhc_q,t_out=self._rhc_twist_cmd_rhc_h)
703
+
704
+ rhc_latest_twist_cmd[:, 0:6] = self._rhc_twist_cmd_rhc_h
705
+
706
+ # self._rhc_refs.rob_refs.root_state.set(data_type="p", data=rhc_latest_p_ref,
707
+ # gpu=self._use_gpu)
708
+ self._rhc_refs.rob_refs.root_state.set(data_type="twist", data=rhc_latest_twist_cmd,
709
+ gpu=self._use_gpu)
710
+
711
+ # contact flags
712
+ idx=self._actions_map["contact_flag_start"]
713
+ if self._env_opts["use_prob_based_stepping"]:
714
+ # encode actions as probs
715
+ self._random_thresh_contacts.uniform_() # random values in-place between 0 and 1
716
+ rhc_latest_contact_ref[:, :] = action_to_be_applied[:, idx:idx+self._n_contacts] >= self._random_thresh_contacts # keep contact with
717
+ # probability action_to_be_applied[:, 6:10]
718
+ else: # just use a threshold
719
+ rhc_latest_contact_ref[:, :] = action_to_be_applied[:, idx:idx+self._n_contacts] > self._env_opts["step_thresh"]
720
+ # actually apply actions to controller
721
+
722
+ def _write_rhc_refs(self):
723
+
724
+ if self._use_gpu:
725
+ # GPU->CPU --> we cannot use asynchronous data transfer since it's unsafe
726
+ self._rhc_refs.rob_refs.root_state.synch_mirror(from_gpu=True,non_blocking=False) # write from gpu to cpu mirror
727
+ self._rhc_refs.contact_flags.synch_mirror(from_gpu=True,non_blocking=False)
728
+ self._rhc_refs.rob_refs.contact_pos.synch_mirror(from_gpu=True,non_blocking=False)
729
+
730
+ self._rhc_refs.rob_refs.root_state.synch_all(read=False, retry=True) # write mirror to shared mem
731
+ self._rhc_refs.contact_flags.synch_all(read=False, retry=True)
732
+ self._rhc_refs.rob_refs.contact_pos.synch_all(read=False, retry=True)
733
+
734
+ def _override_refs(self,
735
+ env_indxs: torch.Tensor = None):
736
+
737
+ # runs at every post_step
738
+ self._agent_refs.rob_refs.root_state.synch_all(read=True,retry=True) # first read from mem
739
+ if self._use_gpu:
740
+ # copies latest refs to GPU
741
+ self._agent_refs.rob_refs.root_state.synch_mirror(from_gpu=False,non_blocking=False)
742
+
743
+ agent_linvel_ref_current=self._agent_refs.rob_refs.root_state.get(data_type="v",
744
+ gpu=self._use_gpu)
745
+
746
+ agent_yaw_omega_ref_current=self._agent_refs.rob_refs.root_state.get(data_type="omega",
747
+ gpu=self._use_gpu)
748
+
749
+ # self._p_trgt_w[:, :]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)[:, 0:2] + \
750
+ # agent_p_ref_current[:, 0:2]
751
+ self._agent_twist_ref_current_w[:, 0:3]=agent_linvel_ref_current # set linvel target
752
+
753
+ self._agent_twist_ref_current_w[:, 5:6]=agent_yaw_omega_ref_current[:, 2:3] # set yaw ang. vel target from shared mem
754
+
755
+ def _fill_substep_obs(self,
756
+ obs: torch.Tensor):
757
+
758
+ # measured stuff
759
+ robot_twist_meas_base_loc = self._robot_state.root_state.get(data_type="twist",gpu=self._use_gpu)
760
+ robot_jnt_v_meas = self._robot_state.jnts_state.get(data_type="v",gpu=self._use_gpu)
761
+
762
+ if self._env_opts["use_linvel_from_rhc"]:
763
+ # twist estimate from mpc
764
+ robot_twist_rhc_base_loc_next = self._rhc_cmds.root_state.get(data_type="twist",gpu=self._use_gpu)
765
+ obs[:, self._obs_map["linvel_meas"]:(self._obs_map["linvel_meas"]+3)] = robot_twist_rhc_base_loc_next[:, 0:3]
766
+ else:
767
+ obs[:, self._obs_map["linvel_meas"]:(self._obs_map["linvel_meas"]+3)] = robot_twist_meas_base_loc[:, 0:3]
768
+ obs[:, self._obs_map["omega_meas"]:(self._obs_map["omega_meas"]+3)] = robot_twist_meas_base_loc[:, 3:6]
769
+
770
+ obs[:, self._obs_map["v_jnt"]:(self._obs_map["v_jnt"]+self._n_jnts)] = robot_jnt_v_meas
771
+
772
+ def _fill_step_obs(self,
773
+ obs: torch.Tensor):
774
+
775
+ # measured stuff
776
+ robot_gravity_norm_base_loc = self._robot_state.root_state.get(data_type="gn",gpu=self._use_gpu)
777
+ robot_twist_meas_base_loc = self._robot_state.root_state.get(data_type="twist",gpu=self._use_gpu)
778
+ robot_jnt_q_meas = self._robot_state.jnts_state.get(data_type="q",gpu=self._use_gpu)
779
+ if self._jnt_q_blacklist_idxs is not None: # we don't want to read joint pos from blacklist
780
+ robot_jnt_q_meas[:, self._jnt_q_blacklist_idxs]=0.0
781
+ robot_jnt_v_meas = self._robot_state.jnts_state.get(data_type="v",gpu=self._use_gpu)
782
+
783
+ # twist estimate from mpc
784
+ robot_twist_rhc_base_loc_next = self._rhc_cmds.root_state.get(data_type="twist",gpu=self._use_gpu)
785
+ # cmds for jnt imp to be applied next
786
+ robot_jnt_q_rhc_applied_next=self._rhc_cmds.jnts_state.get(data_type="q",gpu=self._use_gpu)
787
+ robot_jnt_v_rhc_applied_next=self._rhc_cmds.jnts_state.get(data_type="v",gpu=self._use_gpu)
788
+ robot_jnt_eff_rhc_applied_next=self._rhc_cmds.jnts_state.get(data_type="eff",gpu=self._use_gpu)
789
+
790
+ flight_info_now = self._rhc_refs.flight_info.get(data_type="all",gpu=self._use_gpu)
791
+ flight_settings_now = self._rhc_refs.flight_settings_req.get(data_type="all",gpu=self._use_gpu)
792
+
793
+ # refs
794
+ agent_twist_ref = self._agent_refs.rob_refs.root_state.get(data_type="twist",gpu=self._use_gpu)
795
+
796
+ obs[:, self._obs_map["gn_base"]:(self._obs_map["gn_base"]+3)] = robot_gravity_norm_base_loc # norm. gravity vector in base frame
797
+
798
+ obs[:, self._obs_map["q_jnt"]:(self._obs_map["q_jnt"]+self._n_jnts)] = robot_jnt_q_meas # meas jnt pos
799
+ obs[:, self._obs_map["twist_ref"]:(self._obs_map["twist_ref"]+6)] = agent_twist_ref # high lev agent refs to be tracked
800
+
801
+ if self._env_opts["add_mpc_contact_f_to_obs"]:
802
+ n_forces=3*len(self._contact_names)
803
+ obs[:, self._obs_map["contact_f_mpc"]:(self._obs_map["contact_f_mpc"]+n_forces)] = self._rhc_cmds.contact_wrenches.get(data_type="f",gpu=self._use_gpu)
804
+ if self._env_opts["add_fail_idx_to_obs"]:
805
+ obs[:, self._obs_map["rhc_fail_idx"]:(self._obs_map["rhc_fail_idx"]+1)] = self._rhc_fail_idx(gpu=self._use_gpu)
806
+ if self._env_opts["add_term_mpc_capsize"]:
807
+ obs[:, self._obs_map["gn_base_mpc"]:(self._obs_map["gn_base_mpc"]+3)] = self._rhc_cmds.root_state.get(data_type="gn",gpu=self._use_gpu)
808
+ if self._env_opts["use_rhc_avrg_vel_tracking"]:
809
+ self._get_avrg_rhc_root_twist(out=self._root_twist_avrg_rhc_base_loc, base_loc=True)
810
+ obs[:, self._obs_map["avrg_twist_mpc"]:(self._obs_map["avrg_twist_mpc"]+6)] = self._root_twist_avrg_rhc_base_loc
811
+ if self._env_opts["add_flight_info"]:
812
+ obs[:, self._obs_map["flight_info"]:(self._obs_map["flight_info"]+self._flight_info_size)] = flight_info_now
813
+ if self._env_opts["add_flight_settings"]:
814
+ obs[:, self._obs_map["flight_settings_req"]:(self._obs_map["flight_settings_req"]+self._flight_setting_size)] = \
815
+ flight_settings_now
816
+
817
+ if self._env_opts["add_rhc_cmds_to_obs"]:
818
+ obs[:, self._obs_map["rhc_cmds_q"]:(self._obs_map["rhc_cmds_q"]+self._n_jnts)] = robot_jnt_q_rhc_applied_next
819
+ obs[:, self._obs_map["rhc_cmds_v"]:(self._obs_map["rhc_cmds_v"]+self._n_jnts)] = robot_jnt_v_rhc_applied_next
820
+ obs[:, self._obs_map["rhc_cmds_eff"]:(self._obs_map["rhc_cmds_eff"]+self._n_jnts)] = robot_jnt_eff_rhc_applied_next
821
+ if self._env_opts["use_action_history"]:
822
+ if self._env_opts["add_prev_actions_stats_to_obs"]: # just add last, std and mean to obs
823
+ obs[:, self._obs_map["action_history_prev"]:(self._obs_map["action_history_prev"]+self.actions_dim())]=self._act_mem_buffer.get(idx=0)
824
+ obs[:, self._obs_map["action_history_avrg"]:(self._obs_map["action_history_avrg"]+self.actions_dim())]=self._act_mem_buffer.mean(clone=False)
825
+ obs[:, self._obs_map["action_history_std"]:(self._obs_map["action_history_std"]+self.actions_dim())]=self._act_mem_buffer.std(clone=False)
826
+ else: # add whole memory buffer to obs
827
+ next_idx=self._obs_map["action_history"]
828
+ for i in range(self._env_opts["actions_history_size"]):
829
+ obs[:, next_idx:(next_idx+self.actions_dim())]=self._act_mem_buffer.get(idx=i) # get all (n_envs x (obs_dim x horizon))
830
+ next_idx+=self.actions_dim()
831
+
832
+ if self._env_opts["use_action_smoothing"]: # adding smoothed actions
833
+ obs[:, self._obs_map["action_smoothing"]:(self._obs_map["action_smoothing"]+self.actions_dim())]=self.get_actual_actions(normalized=True)
834
+ next_idx+=self.actions_dim()
835
+
836
+ if self._env_opts["add_periodic_clock_to_obs"]:
837
+ obs[:, next_idx:(next_idx+2)]=self._periodic_clock.get()
838
+ next_idx+=2
839
+ if self._env_opts["add_heightmap_obs"]:
840
+ hm = self._robot_state.height_sensor.get(gpu=self._use_gpu)
841
+ obs[:, self._obs_map["heightmap"]:(self._obs_map["heightmap"]+self._height_flat_dim)] = hm
842
+
843
+ def _get_custom_db_data(self,
844
+ episode_finished,
845
+ ignore_ep_end):
846
+ episode_finished = episode_finished.cpu()
847
+ self.custom_db_data["AgentTwistRefs"].update(
848
+ new_data=self._agent_refs.rob_refs.root_state.get(data_type="twist", gpu=False),
849
+ ep_finished=episode_finished,
850
+ ignore_ep_end=ignore_ep_end)
851
+ self.custom_db_data["RhcFailIdx"].update(new_data=self._rhc_fail_idx(gpu=False),
852
+ ep_finished=episode_finished,
853
+ ignore_ep_end=ignore_ep_end)
854
+ self.custom_db_data["RhcContactForces"].update(
855
+ new_data=self._rhc_cmds.contact_wrenches.get(data_type="f",gpu=False),
856
+ ep_finished=episode_finished,
857
+ ignore_ep_end=ignore_ep_end)
858
+ self.custom_db_data["Power"].update(
859
+ new_data=self._pow_db_data,
860
+ ep_finished=episode_finished,
861
+ ignore_ep_end=ignore_ep_end)
862
+ self.custom_db_data["TrackingError"].update(
863
+ new_data=self._track_error_db,
864
+ ep_finished=episode_finished,
865
+ ignore_ep_end=ignore_ep_end)
866
+
867
+ # reward functions
868
+ def _action_rate(self):
869
+ continuous_actions=self._is_continuous_actions
870
+ discrete_actions=~self._is_continuous_actions
871
+ n_c_actions=continuous_actions.sum().item()
872
+ n_d_actions=discrete_actions.sum().item()
873
+ actions_prev=self._act_mem_buffer.get(idx=1)
874
+ actions_now=self._act_mem_buffer.get(idx=0)
875
+ actions_rate=(actions_now-actions_prev) # actions already normalized
876
+ actions_rate_c=actions_rate[:, continuous_actions]
877
+ actions_rate_d=actions_rate[:, discrete_actions]
878
+
879
+ actions_rate_sqrd=None # assuming n_c_actions > 0 always
880
+ actions_rate_sqrd=self._env_opts["action_rate_rew_c_weight"]*torch.sum(actions_rate_c*actions_rate_c, dim=1, keepdim=True)/n_c_actions
881
+ if discrete_actions.any():
882
+ actions_rate_sqrd+=self._env_opts["action_rate_rew_d_weight"]*torch.sum(actions_rate_d*actions_rate_d, dim=1, keepdim=True)/n_d_actions
883
+ return actions_rate_sqrd
884
+
885
+ def _mech_pow(self, jnts_vel, jnts_effort, autoscaled: bool = False, drained: bool = True):
886
+ mech_pow_jnts=(jnts_effort*jnts_vel)*self._power_penalty_weights
887
+ if drained:
888
+ mech_pow_jnts.clamp_(0.0,torch.inf) # do not account for regenerative power
889
+ mech_pow_tot = torch.sum(mech_pow_jnts, dim=1, keepdim=True)
890
+ self._pow_db_data[:, 1:2]=mech_pow_tot.cpu()
891
+ if autoscaled:
892
+ mech_pow_tot=mech_pow_tot/self._power_penalty_weights_sum
893
+ return mech_pow_tot
894
+
895
+ def _cost_of_transport(self, jnts_vel, jnts_effort, v_norm, mass_weight: bool = False):
896
+ drained_mech_pow=self._mech_pow(jnts_vel=jnts_vel,
897
+ jnts_effort=jnts_effort,
898
+ drained=True)
899
+ CoT=drained_mech_pow/(v_norm+1e-2)
900
+ if mass_weight:
901
+ robot_weight=self._rhc_robot_weight
902
+ CoT=CoT/robot_weight
903
+
904
+ # add to db metrics
905
+ self._pow_db_data[:, 0:1]=CoT.cpu()
906
+ self._pow_db_data[:, 1:2]=drained_mech_pow.cpu()
907
+
908
+ return CoT
909
+
910
+ def _jnt_vel_penalty(self, jnts_vel):
911
+ weighted_jnt_vel = torch.sum(jnts_vel*jnts_vel, dim=1, keepdim=True)/self._n_jnts
912
+ return weighted_jnt_vel
913
+
914
+ def _rhc_fail_idx(self, gpu: bool):
915
+ rhc_fail_idx = self._rhc_status.rhc_fail_idx.get_torch_mirror(gpu=gpu)
916
+ return self._env_opts["rhc_fail_idx_scale"]*rhc_fail_idx
917
+
918
+ # basic L1 and L2 error functions
919
+ def _track_err_wmse(self, task_ref, task_meas, scaling, weights):
920
+ # weighted mean-squared error computation
921
+ task_error = (task_meas-task_ref)
922
+ # add to db metrics
923
+ self._track_error_db[:, :]=torch.abs(task_error)
924
+ scaled_error=task_error/scaling
925
+
926
+ task_wmse = torch.sum(scaled_error*scaled_error*weights, dim=1, keepdim=True)/torch.sum(weights).item()
927
+ return task_wmse # weighted mean square error (along task dimension)
928
+
929
+ def _track_err_dir_wmse(self, task_ref, task_meas, scaling, weights):
930
+ # weighted DIRECTIONAL mean-squared error computation
931
+ task_error = (task_meas-task_ref)
932
+ # add to db metrics
933
+ self._track_error_db[:, :]=torch.abs(task_error)
934
+ task_error=task_error/scaling
935
+
936
+ # projection along commanded direction and gravity, matching paper formulation
937
+ v_ref=task_ref[:, 0:3]
938
+ delta_v=task_error[:, 0:3]
939
+
940
+ v_ref_norm=torch.norm(v_ref, dim=1, keepdim=True)
941
+ cmd_dir=v_ref/(v_ref_norm+1e-8)
942
+ # fallback to measured direction if command is (near) zero to avoid degenerate projection
943
+ meas_dir=task_meas[:, 0:3]
944
+ meas_dir=meas_dir/(torch.norm(meas_dir, dim=1, keepdim=True)+1e-8)
945
+ cmd_dir=torch.where((v_ref_norm>1e-6), cmd_dir, meas_dir)
946
+
947
+ gravity_dir = self._robot_state.root_state.get(data_type="gn",gpu=self._use_gpu) # normalized gravity in base frame
948
+ gravity_dir = gravity_dir/(torch.norm(gravity_dir, dim=1, keepdim=True)+1e-8)
949
+
950
+ forward_error=torch.sum(delta_v*cmd_dir, dim=1, keepdim=True)
951
+ vertical_error=torch.sum(delta_v*gravity_dir, dim=1, keepdim=True)
952
+ lateral_vec=delta_v - vertical_error*gravity_dir - forward_error*cmd_dir
953
+ lateral_error=torch.norm(lateral_vec, dim=1, keepdim=True)
954
+
955
+ # angular directional components: use gravity as vertical, project base x onto the world xy plane for roll, and close the triad with pitch
956
+ base_x = self._base_x_dir
957
+ base_y = self._base_y_dir
958
+
959
+ roll_dir = base_x - torch.sum(base_x*gravity_dir, dim=1, keepdim=True)*gravity_dir
960
+ roll_norm = torch.norm(roll_dir, dim=1, keepdim=True)
961
+ roll_dir_alt = base_y - torch.sum(base_y*gravity_dir, dim=1, keepdim=True)*gravity_dir # fallback if base x is almost aligned with gravity
962
+ roll_norm_alt = torch.norm(roll_dir_alt, dim=1, keepdim=True)
963
+ use_alt_roll = roll_norm < 1e-6
964
+ roll_dir = torch.where(use_alt_roll, roll_dir_alt, roll_dir)
965
+ roll_norm = torch.where(use_alt_roll, roll_norm_alt, roll_norm)
966
+ roll_dir = roll_dir/(roll_norm+1e-8)
967
+
968
+ pitch_dir = torch.cross(gravity_dir, roll_dir, dim=1)
969
+ pitch_dir = pitch_dir/(torch.norm(pitch_dir, dim=1, keepdim=True)+1e-8)
970
+
971
+ delta_omega = task_error[:, 3:6]
972
+ omega_roll_error = torch.sum(delta_omega*roll_dir, dim=1, keepdim=True)
973
+ omega_pitch_error = torch.sum(delta_omega*pitch_dir, dim=1, keepdim=True)
974
+ omega_vertical_error = torch.sum(delta_omega*gravity_dir, dim=1, keepdim=True)
975
+
976
+ full_error=torch.cat((forward_error, lateral_error, vertical_error, omega_roll_error, omega_pitch_error, omega_vertical_error), dim=1)
977
+ task_wmse_dir = torch.sum(full_error*full_error*weights, dim=1, keepdim=True)/torch.sum(weights).item()
978
+ return task_wmse_dir # weighted mean square error (along task dimension)
979
+
980
+ # L2 errors
981
+ def _tracking_err_rel_wmse(self, task_ref, task_meas, weights, directional: bool = False):
982
+ ref_norm = task_ref.norm(dim=1,keepdim=True) # norm of the full twist reference
983
+ self._task_err_scaling[:, :] = ref_norm+1e-2
984
+ if directional:
985
+ task_rel_err_wmse=self._track_err_dir_wmse(task_ref=task_ref, task_meas=task_meas,
986
+ scaling=self._task_err_scaling, weights=weights)
987
+ else:
988
+ task_rel_err_wmse=self._track_err_wmse(task_ref=task_ref, task_meas=task_meas,
989
+ scaling=self._task_err_scaling, weights=weights)
990
+ return task_rel_err_wmse
991
+
992
+ def _tracking_err_wmse(self, task_ref, task_meas, weights, directional: bool = False):
993
+ self._task_err_scaling[:, :] = 1
994
+ if directional:
995
+ task_err_wmse = self._track_err_dir_wmse(task_ref=task_ref,
996
+ task_meas=task_meas, scaling=self._task_err_scaling, weights=weights)
997
+ else:
998
+ task_err_wmse = self._track_err_wmse(task_ref=task_ref,
999
+ task_meas=task_meas, scaling=self._task_err_scaling, weights=weights)
1000
+ return task_err_wmse
1001
+
1002
+ # L1 errors
1003
+ def _tracking_err_rel_lin(self, task_ref, task_meas, weights, directional):
1004
+ task_rel_err_wmse = self._tracking_err_rel_wmse(task_ref=task_ref,
1005
+ task_meas=task_meas, weights=weights, directional=directional)
1006
+ return task_rel_err_wmse.sqrt()
1007
+
1008
+ def _tracking_err_lin(self, task_ref, task_meas, weights, directional: bool = False):
1009
+ self._task_err_scaling[:, :] = 1
1010
+ task_err_wmse=self._tracking_err_wmse(task_ref=task_ref,
1011
+ task_meas=task_meas, weights=weights, directional=directional)
1012
+ return task_err_wmse.sqrt()
1013
+
1014
+ # reward computation over steps/substeps
1015
+ def _compute_step_rewards(self):
1016
+
1017
+ sub_rewards = self._sub_rewards.get_torch_mirror(gpu=self._use_gpu)
1018
+
1019
+ # tracking reward
1020
+ if self._env_opts["use_L1_norm"]: # linear errors
1021
+ task_error_fun = self._tracking_err_lin
1022
+ if self._env_opts["use_relative_error"]:
1023
+ task_error_fun = self._tracking_err_rel_lin
1024
+ else: # quadratic error
1025
+ task_error_fun = self._tracking_err_wmse
1026
+ if self._env_opts["use_relative_error"]:
1027
+ task_error_fun = self._tracking_err_rel_wmse
1028
+
1029
+ agent_task_ref_base_loc = self._agent_refs.rob_refs.root_state.get(data_type="twist",gpu=self._use_gpu) # high level agent refs (hybrid twist)
1030
+ self._get_avrg_step_root_twist(out=self._step_avrg_root_twist_base_loc, base_loc=True)
1031
+ task_error = task_error_fun(task_meas=self._step_avrg_root_twist_base_loc,
1032
+ task_ref=agent_task_ref_base_loc,
1033
+ weights=self._task_err_weights,
1034
+ directional=self._env_opts["directional_tracking"])
1035
+
1036
+ idx=self._reward_map["task_error"]
1037
+ if self._env_opts["use_exp_track_rew"]:
1038
+ sub_rewards[:, idx:(idx+1)] = \
1039
+ self._env_opts["task_track_offset_exp"]*torch.exp(-self._env_opts["task_track_scale_exp"]*task_error)
1040
+ else: # simple linear reward
1041
+ sub_rewards[:, idx:(idx+1)] = \
1042
+ self._env_opts["task_track_offset"]*(1.0-self._env_opts["task_track_scale"]*task_error)
1043
+
1044
+ if self._env_opts["use_fail_idx_weight"]: # add weight based on fail idx
1045
+ fail_idx=self._rhc_fail_idx(gpu=self._use_gpu)
1046
+ sub_rewards[:, idx:(idx+1)]=(1-fail_idx)*sub_rewards[:, idx:(idx+1)]
1047
+ if self._track_rew_smoother is not None: # smooth reward if required
1048
+ self._track_rew_smoother.update(new_signal=sub_rewards[:, 0:1])
1049
+ sub_rewards[:, idx:(idx+1)]=self._track_rew_smoother.get()
1050
+
1051
+ # action rate
1052
+ if self._env_opts["add_action_rate_reward"]:
1053
+ action_rate=self._action_rate()
1054
+ idx=self._reward_map["action_rate"]
1055
+ sub_rewards[:, idx:(idx+1)] = self._env_opts["action_rate_offset"]*(1.0-self._env_opts["action_rate_scale"]*action_rate)
1056
+
1057
+ # mpc vel tracking
1058
+ if self._env_opts["use_rhc_avrg_vel_tracking"]:
1059
+ self._get_avrg_rhc_root_twist(out=self._root_twist_avrg_rhc_base_loc_next,base_loc=True) # get estimated avrg vel
1060
+ # from MPC after stepping
1061
+ task_pred_error=task_error_fun(task_meas=self._root_twist_avrg_rhc_base_loc_next,
1062
+ task_ref=agent_task_ref_base_loc,
1063
+ weights=self._task_pred_err_weights,
1064
+ directional=self._env_opts["directional_tracking"])
1065
+ idx=self._reward_map["rhc_avrg_vel_error"]
1066
+ sub_rewards[:, idx:(idx+1)] = self._env_opts["task_pred_track_offset"]*torch.exp(-self._env_opts["task_pred_track_scale"]*task_pred_error)
1067
+
1068
+ def _compute_substep_rewards(self):
1069
+
1070
+ sub_rewards = self._sub_rewards.get_torch_mirror(gpu=self._use_gpu)
1071
+
1072
+ if self._env_opts["add_CoT_reward"] or self._env_opts["add_power_reward"]:
1073
+ jnts_vel = self._robot_state.jnts_state.get(data_type="v",gpu=self._use_gpu)
1074
+ jnts_effort = self._robot_state.jnts_state.get(data_type="eff",gpu=self._use_gpu)
1075
+
1076
+ if self._env_opts["add_CoT_reward"]:
1077
+ if self._env_opts["use_CoT_wrt_ref"]: # uses v ref norm for computing cot
1078
+ agent_task_ref_base_loc = self._agent_refs.rob_refs.root_state.get(data_type="twist",gpu=self._use_gpu)
1079
+ v_norm=torch.norm(agent_task_ref_base_loc, dim=1, keepdim=True)
1080
+ else: # uses measured velocity
1081
+ robot_twist_meas_base_loc = self._robot_state.root_state.get(data_type="twist",gpu=self._use_gpu)
1082
+ v_norm=torch.norm(robot_twist_meas_base_loc[:,0:3], dim=1, keepdim=True)
1083
+ CoT=self._cost_of_transport(jnts_vel=jnts_vel,jnts_effort=jnts_effort,v_norm=v_norm,
1084
+ mass_weight=True
1085
+ )
1086
+ idx=self._reward_map["CoT"]
1087
+ sub_rewards[:, idx:(idx+1)] = self._env_opts["CoT_offset"]*(1-self._env_opts["CoT_scale"]*CoT)
1088
+ if self._env_opts["add_power_reward"]:
1089
+ weighted_mech_power=self._mech_pow(jnts_vel=jnts_vel,jnts_effort=jnts_effort, drained=True)
1090
+ idx=self._reward_map["mech_pow"]
1091
+ sub_rewards[:, idx:(idx+1)] = self._env_opts["power_offset"]*(1-self._env_opts["power_scale"]*weighted_mech_power)
1092
+
1093
+ if self._env_opts["add_jnt_v_reward"]:
1094
+ jnts_vel = self._robot_state.jnts_state.get(data_type="v",gpu=self._use_gpu)
1095
+ jnt_v=self._jnt_vel_penalty(jnts_vel=jnts_vel)
1096
+ idx=self._reward_map["jnt_v"]
1097
+ sub_rewards[:, idx:(idx+1)] = self._env_opts["jnt_vel_offset"]*(1-self._env_opts["jnt_vel_scale"]*jnt_v)
1098
+
1099
+ def _randomize_task_refs(self,
1100
+ env_indxs: torch.Tensor = None):
1101
+
1102
+ # we randomize the reference in world frame, since it's much more intuitive
1103
+ # (it will be rotated in base frame when provided to the agent and used for rew
1104
+ # computation)
1105
+
1106
+ if self._env_opts["use_pof0"]: # sample from bernoulli distribution
1107
+ torch.bernoulli(input=self._pof1_b_linvel,out=self._bernoulli_coeffs_linvel) # by default bernoulli_coeffs are 1 if not self._env_opts["use_pof0"]
1108
+ torch.bernoulli(input=self._pof1_b_omega,out=self._bernoulli_coeffs_omega)
1109
+ if env_indxs is None:
1110
+ random_uniform=torch.full_like(self._agent_twist_ref_current_w, fill_value=0.0)
1111
+ torch.nn.init.uniform_(random_uniform, a=-1, b=1)
1112
+ self._agent_twist_ref_current_w[:, :] = random_uniform*self._twist_ref_scale + self._twist_ref_offset
1113
+ self._agent_twist_ref_current_w[:, 0:3] = self._agent_twist_ref_current_w[:, 0:3]*self._bernoulli_coeffs_linvel # linvel
1114
+ self._agent_twist_ref_current_w[:, 3:6] = self._agent_twist_ref_current_w[:, 3:6]*self._bernoulli_coeffs_omega # omega
1115
+ else:
1116
+ random_uniform=torch.full_like(self._agent_twist_ref_current_w[env_indxs, :], fill_value=0.0)
1117
+ torch.nn.init.uniform_(random_uniform, a=-1, b=1)
1118
+ self._agent_twist_ref_current_w[env_indxs, :] = random_uniform * self._twist_ref_scale + self._twist_ref_offset
1119
+ self._agent_twist_ref_current_w[env_indxs, 0:3] = self._agent_twist_ref_current_w[env_indxs, 0:3]*self._bernoulli_coeffs_linvel[env_indxs, :]
1120
+ self._agent_twist_ref_current_w[env_indxs, 3:6] = self._agent_twist_ref_current_w[env_indxs, 3:6]*self._bernoulli_coeffs_omega[env_indxs, :] # omega
1121
+
1122
+ def _get_obs_names(self):
1123
+
1124
+ obs_names = [""] * self.obs_dim()
1125
+
1126
+ # proprioceptive stream of obs
1127
+ next_idx=0
1128
+
1129
+ self._obs_map["gn_base"]=next_idx
1130
+ obs_names[0] = "gn_x_base_loc"
1131
+ obs_names[1] = "gn_y_base_loc"
1132
+ obs_names[2] = "gn_z_base_loc"
1133
+ next_idx+=3
1134
+
1135
+ self._obs_map["linvel_meas"]=next_idx
1136
+ obs_names[next_idx] = "linvel_x_base_loc"
1137
+ obs_names[next_idx+1] = "linvel_y_base_loc"
1138
+ obs_names[next_idx+2] = "linvel_z_base_loc"
1139
+ next_idx+=3
1140
+
1141
+ self._obs_map["omega_meas"]=next_idx
1142
+ obs_names[next_idx] = "omega_x_base_loc"
1143
+ obs_names[next_idx+1] = "omega_y_base_loc"
1144
+ obs_names[next_idx+2] = "omega_z_base_loc"
1145
+ next_idx+=3
1146
+
1147
+ jnt_names=self.get_observed_joints()
1148
+ self._obs_map["q_jnt"]=next_idx
1149
+ for i in range(self._n_jnts): # jnt obs (pos):
1150
+ obs_names[next_idx+i] = f"q_jnt_{jnt_names[i]}"
1151
+ next_idx+=self._n_jnts
1152
+
1153
+ self._obs_map["v_jnt"]=next_idx
1154
+ for i in range(self._n_jnts): # jnt obs (v):
1155
+ obs_names[next_idx+i] = f"v_jnt_{jnt_names[i]}"
1156
+ next_idx+=self._n_jnts
1157
+
1158
+ # references
1159
+ self._obs_map["twist_ref"]=next_idx
1160
+ obs_names[next_idx] = "linvel_x_ref_base_loc"
1161
+ obs_names[next_idx+1] = "linvel_y_ref_base_loc"
1162
+ obs_names[next_idx+2] = "linvel_z_ref_base_loc"
1163
+ obs_names[next_idx+3] = "omega_x_ref_base_loc"
1164
+ obs_names[next_idx+4] = "omega_y_ref_base_loc"
1165
+ obs_names[next_idx+5] = "omega_z_ref_base_loc"
1166
+ next_idx+=6
1167
+
1168
+ # contact forces
1169
+ if self._env_opts["add_mpc_contact_f_to_obs"]:
1170
+ i = 0
1171
+ self._obs_map["contact_f_mpc"]=next_idx
1172
+ for contact in self._contact_names:
1173
+ obs_names[next_idx+i] = f"fc_{contact}_x_base_loc"
1174
+ obs_names[next_idx+i+1] = f"fc_{contact}_y_base_loc"
1175
+ obs_names[next_idx+i+2] = f"fc_{contact}_z_base_loc"
1176
+ i+=3
1177
+ next_idx+=3*len(self._contact_names)
1178
+
1179
+ # data directly from MPC
1180
+ if self._env_opts["add_fail_idx_to_obs"]:
1181
+ self._obs_map["rhc_fail_idx"]=next_idx
1182
+ obs_names[next_idx] = "rhc_fail_idx"
1183
+ next_idx+=1
1184
+ if self._env_opts["add_term_mpc_capsize"]:
1185
+ self._obs_map["gn_base_mpc"]=next_idx
1186
+ obs_names[next_idx] = "gn_x_rhc_base_loc"
1187
+ obs_names[next_idx+1] = "gn_y_rhc_base_loc"
1188
+ obs_names[next_idx+2] = "gn_z_rhc_base_loc"
1189
+ next_idx+=3
1190
+ if self._env_opts["use_rhc_avrg_vel_tracking"]:
1191
+ self._obs_map["avrg_twist_mpc"]=next_idx
1192
+ obs_names[next_idx] = "linvel_x_avrg_rhc"
1193
+ obs_names[next_idx+1] = "linvel_y_avrg_rhc"
1194
+ obs_names[next_idx+2] = "linvel_z_avrg_rhc"
1195
+ obs_names[next_idx+3] = "omega_x_avrg_rhc"
1196
+ obs_names[next_idx+4] = "omega_y_avrg_rhc"
1197
+ obs_names[next_idx+5] = "omega_z_avrg_rhc"
1198
+ next_idx+=6
1199
+
1200
+ if self._env_opts["add_flight_info"]:
1201
+ self._obs_map["flight_info"]=next_idx
1202
+ for i in range(len(self._contact_names)):
1203
+ obs_names[next_idx+i] = "flight_pos_"+ self._contact_names[i]
1204
+ next_idx+=len(self._contact_names)
1205
+ for i in range(len(self._contact_names)):
1206
+ obs_names[next_idx+i] = "flight_len_remaining_"+ self._contact_names[i]
1207
+ next_idx+=len(self._contact_names)
1208
+ for i in range(len(self._contact_names)):
1209
+ obs_names[next_idx+i] = "flight_len_nominal_"+ self._contact_names[i]
1210
+ next_idx+=len(self._contact_names)
1211
+ for i in range(len(self._contact_names)):
1212
+ obs_names[next_idx+i] = "flight_apex_nominal_"+ self._contact_names[i]
1213
+ next_idx+=len(self._contact_names)
1214
+ for i in range(len(self._contact_names)):
1215
+ obs_names[next_idx+i] = "flight_end_nominal_"+ self._contact_names[i]
1216
+ next_idx+=len(self._contact_names)
1217
+
1218
+ if self._env_opts["add_flight_settings"]:
1219
+ self._obs_map["flight_settings_req"]=next_idx
1220
+ for i in range(len(self._contact_names)):
1221
+ obs_names[next_idx+i] = "flight_len_req_"+ self._contact_names[i]
1222
+ next_idx+=len(self._contact_names)
1223
+ for i in range(len(self._contact_names)):
1224
+ obs_names[next_idx+i] = "flight_apex_req_"+ self._contact_names[i]
1225
+ next_idx+=len(self._contact_names)
1226
+ for i in range(len(self._contact_names)):
1227
+ obs_names[next_idx+i] = "flight_end_req_"+ self._contact_names[i]
1228
+ next_idx+=len(self._contact_names)
1229
+
1230
+ if self._env_opts["add_rhc_cmds_to_obs"]:
1231
+ self._obs_map["rhc_cmds_q"]=next_idx
1232
+ for i in range(self._n_jnts): # jnt obs (pos):
1233
+ obs_names[next_idx+i] = f"rhc_cmd_q_{jnt_names[i]}"
1234
+ next_idx+=self._n_jnts
1235
+ self._obs_map["rhc_cmds_v"]=next_idx
1236
+ for i in range(self._n_jnts): # jnt obs (pos):
1237
+ obs_names[next_idx+i] = f"rhc_cmd_v_{jnt_names[i]}"
1238
+ next_idx+=self._n_jnts
1239
+ self._obs_map["rhc_cmds_eff"]=next_idx
1240
+ for i in range(self._n_jnts): # jnt obs (pos):
1241
+ obs_names[next_idx+i] = f"rhc_cmd_eff_{jnt_names[i]}"
1242
+ next_idx+=self._n_jnts
1243
+
1244
+ # previous actions info
1245
+ if self._env_opts["use_action_history"]:
1246
+ self._obs_map["action_history"]=next_idx
1247
+ action_names = self._get_action_names()
1248
+ if self._env_opts["add_prev_actions_stats_to_obs"]:
1249
+ self._obs_map["action_history_prev"]=next_idx
1250
+ for act_idx in range(self.actions_dim()):
1251
+ obs_names[next_idx+act_idx] = action_names[act_idx]+f"_prev_act"
1252
+ next_idx+=self.actions_dim()
1253
+ self._obs_map["action_history_avrg"]=next_idx
1254
+ for act_idx in range(self.actions_dim()):
1255
+ obs_names[next_idx+act_idx] = action_names[act_idx]+f"_avrg_act"
1256
+ next_idx+=self.actions_dim()
1257
+ self._obs_map["action_history_std"]=next_idx
1258
+ for act_idx in range(self.actions_dim()):
1259
+ obs_names[next_idx+act_idx] = action_names[act_idx]+f"_std_act"
1260
+ next_idx+=self.actions_dim()
1261
+ else:
1262
+ for i in range(self._env_opts["actions_history_size"]):
1263
+ for act_idx in range(self.actions_dim()):
1264
+ obs_names[next_idx+act_idx] = action_names[act_idx]+f"_m{i+1}_act"
1265
+ next_idx+=self.actions_dim()
1266
+
1267
+ if self._env_opts["use_action_smoothing"]:
1268
+ self._obs_map["action_smoothing"]=next_idx
1269
+ for smoothed_action in range(self.actions_dim()):
1270
+ obs_names[next_idx+smoothed_action] = action_names[smoothed_action]+f"_smoothed"
1271
+ next_idx+=self.actions_dim()
1272
+
1273
+ if self._env_opts["add_periodic_clock_to_obs"]:
1274
+ self._obs_map["clock"]=next_idx
1275
+ obs_names[next_idx] = "clock_cos"
1276
+ obs_names[next_idx+1] = "clock_sin"
1277
+ next_idx+=2
1278
+ if self._env_opts["add_heightmap_obs"] and self._height_grid_size is not None:
1279
+ self._obs_map["heightmap"]=next_idx
1280
+ gs = self._height_grid_size
1281
+ for r in range(gs):
1282
+ for c in range(gs):
1283
+ obs_names[next_idx] = f"height_r{r}_c{c}"
1284
+ next_idx += 1
1285
+
1286
+ return obs_names
1287
+
1288
+ def _set_substep_obs(self):
1289
+ # which obs are to be averaged over substeps?
1290
+
1291
+ self._is_substep_obs[self._obs_map["linvel_meas"]:self._obs_map["linvel_meas"]+3]=True
1292
+ self._is_substep_obs[self._obs_map["omega_meas"]:self._obs_map["omega_meas"]+3]=True
1293
+ self._is_substep_obs[self._obs_map["v_jnt"]:self._obs_map["v_jnt"]+self._n_jnts]=True # also good for noise
1294
+
1295
+ # self._is_substep_obs[self._obs_map["contact_f_mpc"]:self._obs_map["contact_f_mpc"]+3*len(self._contact_names)]=True
1296
+
1297
+ def _get_action_names(self):
1298
+
1299
+ action_names = [""] * self.actions_dim()
1300
+ action_names[0] = "vx_cmd" # twist commands from agent to RHC controller
1301
+ action_names[1] = "vy_cmd"
1302
+ action_names[2] = "vz_cmd"
1303
+ action_names[3] = "roll_omega_cmd"
1304
+ action_names[4] = "pitch_omega_cmd"
1305
+ action_names[5] = "yaw_omega_cmd"
1306
+
1307
+ next_idx=6
1308
+
1309
+ self._actions_map["contact_flag_start"]=next_idx
1310
+ for i in range(len(self._contact_names)):
1311
+ contact=self._contact_names[i]
1312
+ action_names[next_idx] = f"contact_flag_{contact}"
1313
+ next_idx+=1
1314
+
1315
+ return action_names
1316
+
1317
+ def _set_substep_rew(self):
1318
+
1319
+ # which rewards are to be computed at substeps frequency?
1320
+ self._is_substep_rew[self._reward_map["task_error"]]=False
1321
+ if self._env_opts["add_CoT_reward"]:
1322
+ self._is_substep_rew[self._reward_map["CoT"]]=True
1323
+ if self._env_opts["add_power_reward"]:
1324
+ self._is_substep_rew[self._reward_map["mech_pow"]]=True
1325
+ if self._env_opts["add_action_rate_reward"]:
1326
+ self._is_substep_rew[self._reward_map["action_rate"]]=False
1327
+ if self._env_opts["add_jnt_v_reward"]:
1328
+ self._is_substep_rew[self._reward_map["jnt_v"]]=True
1329
+
1330
+ if self._env_opts["use_rhc_avrg_vel_tracking"]:
1331
+ self._is_substep_rew[self._reward_map["rhc_avrg_vel_error"]]=False
1332
+
1333
+ def _get_rewards_names(self):
1334
+
1335
+ counter=0
1336
+ reward_names = []
1337
+
1338
+ # adding rewards
1339
+ reward_names.append("task_error")
1340
+ self._reward_map["task_error"]=counter
1341
+ self._reward_lb_map["task_error"]="task_error_reward_lb"
1342
+ counter+=1
1343
+ if self._env_opts["add_power_reward"] and self._env_opts["add_CoT_reward"]:
1344
+ Journal.log(self.__class__.__name__,
1345
+ "__init__",
1346
+ "Only one between CoT and power reward can be used!",
1347
+ LogType.EXCEP,
1348
+ throw_when_excep=True)
1349
+ if self._env_opts["add_CoT_reward"]:
1350
+ reward_names.append("CoT")
1351
+ self._reward_map["CoT"]=counter
1352
+ self._reward_lb_map["CoT"]="CoT_reward_lb"
1353
+ counter+=1
1354
+ if self._env_opts["add_power_reward"]:
1355
+ reward_names.append("mech_pow")
1356
+ self._reward_map["mech_pow"]=counter
1357
+ self._reward_lb_map["mech_pow"]="power_reward_lb"
1358
+ counter+=1
1359
+ if self._env_opts["add_action_rate_reward"]:
1360
+ reward_names.append("action_rate")
1361
+ self._reward_map["action_rate"]=counter
1362
+ self._reward_lb_map["action_rate"]="action_rate_reward_lb"
1363
+ counter+=1
1364
+ if self._env_opts["add_jnt_v_reward"]:
1365
+ reward_names.append("jnt_v")
1366
+ self._reward_map["jnt_v"]=counter
1367
+ self._reward_lb_map["jnt_v"]="jnt_vel_reward_lb"
1368
+ counter+=1
1369
+ if self._env_opts["use_rhc_avrg_vel_tracking"]:
1370
+ reward_names.append("rhc_avrg_vel_error")
1371
+ self._reward_map["rhc_avrg_vel_error"]=counter
1372
+ self._reward_lb_map["rhc_avrg_vel_error"]="rhc_avrg_vel_reward_lb"
1373
+ counter+=1
1374
+
1375
+ return reward_names
1376
+
1377
+ def _get_sub_trunc_names(self):
1378
+ sub_trunc_names = []
1379
+ sub_trunc_names.append("ep_timeout")
1380
+ if self._env_opts["single_task_ref_per_episode"]:
1381
+ sub_trunc_names.append("task_ref_rand")
1382
+ return sub_trunc_names
1383
+
1384
+ def _get_sub_term_names(self):
1385
+ # to be overridden by child class
1386
+ sub_term_names = []
1387
+ sub_term_names.append("rhc_failure")
1388
+ sub_term_names.append("robot_capsize")
1389
+ if self._env_opts["add_term_mpc_capsize"]:
1390
+ sub_term_names.append("rhc_capsize")
1391
+
1392
+ return sub_term_names
1393
+
1394
+ def _set_jnts_blacklist_pattern(self):
1395
+ # used to exclude pos measurement from wheels
1396
+ self._jnt_q_blacklist_patterns=["wheel"]
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/world_interface_base.py ADDED
@@ -0,0 +1,1486 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from aug_mpc.controllers.rhc.augmpc_cluster_server import AugMpcClusterServer
2
+ from aug_mpc.utils.shared_data.remote_stepping import RemoteStepperClnt
3
+ from aug_mpc.utils.shared_data.remote_stepping import RemoteResetClnt
4
+ from aug_mpc.utils.shared_data.remote_stepping import RemoteResetRequest
5
+ from aug_mpc.utils.jnt_imp_control_base import JntImpCntrlBase
6
+ from aug_mpc.utils.hybrid_quad_xrdf_gen import get_xrdf_cmds
7
+ from aug_mpc.utils.xrdf_gen import generate_srdf, generate_urdf
8
+ from aug_mpc.utils.math_utils import quaternion_difference
9
+ from aug_mpc.utils.custom_arg_parsing import extract_custom_xacro_args, merge_xacro_cmds
10
+
11
+ from aug_mpc.utils.filtering import FirstOrderFilter
12
+
13
+ from mpc_hive.utilities.homing import RobotHomer
14
+ from mpc_hive.utilities.shared_data.jnt_imp_control import JntImpCntrlData
15
+
16
+ from EigenIPC.PyEigenIPC import VLevel, Journal, LogType, dtype
17
+ from EigenIPC.PyEigenIPC import StringTensorServer
18
+ from EigenIPC.PyEigenIPCExt.wrappers.shared_data_view import SharedTWrapper
19
+
20
+ from typing import List, Union, Dict, TypeVar
21
+
22
+ import os
23
+ import inspect
24
+ import signal
25
+ import time
26
+
27
+ import numpy as np
28
+ import torch
29
+
30
+ from abc import ABC, abstractmethod
31
+
32
+ JntImpCntrlChild = TypeVar('JntImpCntrlChild', bound='JntImpCntrlBase')
33
+
34
+ class AugMPCWorldInterfaceBase(ABC):
35
+
36
+ def __init__(self,
37
+ robot_names: List[str],
38
+ robot_urdf_paths: List[str],
39
+ robot_srdf_paths: List[str],
40
+ jnt_imp_config_paths: List[str],
41
+ n_contacts: List[int],
42
+ cluster_dt: List[float],
43
+ use_remote_stepping: List[bool],
44
+ name: str = "AugMPCWorldInterfaceBase",
45
+ num_envs: int = 1,
46
+ debug = False,
47
+ verbose: bool = False,
48
+ vlevel: VLevel = VLevel.V1,
49
+ n_init_step: int = 0,
50
+ timeout_ms: int = 60000,
51
+ env_opts: Dict = None,
52
+ use_gpu: bool = True,
53
+ dtype: torch.dtype = torch.float32,
54
+ dump_basepath: str = "/tmp",
55
+ override_low_lev_controller: bool = False):
56
+
57
+ # checks on input args
58
+ # type checks
59
+ if not isinstance(robot_names, List):
60
+ exception = "robot_names must be a list!"
61
+ Journal.log(self.__class__.__name__,
62
+ "__init__",
63
+ exception,
64
+ LogType.EXCEP,
65
+ throw_when_excep = True)
66
+ if not isinstance(robot_urdf_paths, List):
67
+ exception = "robot_urdf_paths must be a list!"
68
+ Journal.log(self.__class__.__name__,
69
+ "__init__",
70
+ exception,
71
+ LogType.EXCEP,
72
+ throw_when_excep = True)
73
+ if not isinstance(robot_srdf_paths, List):
74
+ exception = "robot_srdf_paths must be a list!"
75
+ Journal.log(self.__class__.__name__,
76
+ "__init__",
77
+ exception,
78
+ LogType.EXCEP,
79
+ throw_when_excep = True)
80
+ if not isinstance(cluster_dt, List):
81
+ exception = "cluster_dt must be a list!"
82
+ Journal.log(self.__class__.__name__,
83
+ "__init__",
84
+ exception,
85
+ LogType.EXCEP,
86
+ throw_when_excep = True)
87
+ if not isinstance(use_remote_stepping, List):
88
+ exception = "use_remote_stepping must be a list!"
89
+ Journal.log(self.__class__.__name__,
90
+ "__init__",
91
+ exception,
92
+ LogType.EXCEP,
93
+ throw_when_excep = True)
94
+ if not isinstance(n_contacts, List):
95
+ exception = "n_contacts must be a list (of integers)!"
96
+ Journal.log(self.__class__.__name__,
97
+ "__init__",
98
+ exception,
99
+ LogType.EXCEP,
100
+ throw_when_excep = True)
101
+ if not isinstance(jnt_imp_config_paths, List):
102
+ exception = "jnt_imp_config_paths must be a list paths!"
103
+ Journal.log(self.__class__.__name__,
104
+ "__init__",
105
+ exception,
106
+ LogType.EXCEP,
107
+ throw_when_excep = True)
108
+
109
+ # dim checks
110
+ if not len(robot_urdf_paths) == len(robot_names):
111
+ exception = f"robot_urdf_paths has len {len(robot_urdf_paths)}" + \
112
+ f" while robot_names {len(robot_names)}"
113
+ Journal.log(self.__class__.__name__,
114
+ "__init__",
115
+ exception,
116
+ LogType.EXCEP,
117
+ throw_when_excep = True)
118
+ if not len(robot_srdf_paths) == len(robot_names):
119
+ exception = f"robot_srdf_paths has len {len(robot_srdf_paths)}" + \
120
+ f" while robot_names {len(robot_names)}"
121
+ Journal.log(self.__class__.__name__,
122
+ "__init__",
123
+ exception,
124
+ LogType.EXCEP,
125
+ throw_when_excep = True)
126
+ if not len(cluster_dt) == len(robot_names):
127
+ exception = f"cluster_dt has len {len(cluster_dt)}" + \
128
+ f" while robot_names {len(robot_names)}"
129
+ Journal.log(self.__class__.__name__,
130
+ "__init__",
131
+ exception,
132
+ LogType.EXCEP,
133
+ throw_when_excep = True)
134
+ if not len(use_remote_stepping) == len(robot_names):
135
+ exception = f"use_remote_stepping has len {len(use_remote_stepping)}" + \
136
+ f" while robot_names {len(robot_names)}"
137
+ Journal.log(self.__class__.__name__,
138
+ "__init__",
139
+ exception,
140
+ LogType.EXCEP,
141
+ throw_when_excep = True)
142
+ if not len(robot_srdf_paths) == len(robot_names):
143
+ exception = f"robot_srdf_paths has len {len(robot_srdf_paths)}" + \
144
+ f" while robot_names {len(robot_names)}"
145
+ Journal.log(self.__class__.__name__,
146
+ "__init__",
147
+ exception,
148
+ LogType.EXCEP,
149
+ throw_when_excep = True)
150
+ if not len(jnt_imp_config_paths) == len(robot_names):
151
+ exception = f"jnt_imp_config_paths has len {len(jnt_imp_config_paths)}" + \
152
+ f" while robot_names {len(robot_names)}"
153
+ Journal.log(self.__class__.__name__,
154
+ "__init__",
155
+ exception,
156
+ LogType.EXCEP,
157
+ throw_when_excep = True)
158
+
159
+ self._remote_exit_flag=None
160
+
161
+ self._name=name
162
+ self._num_envs=num_envs
163
+ self._debug=debug
164
+ self._verbose=verbose
165
+ self._vlevel=vlevel
166
+ self._force_reconnection=True
167
+ self._timeout_ms=timeout_ms
168
+ self._use_gpu=use_gpu
169
+ self._device = "cuda" if self._use_gpu else "cpu"
170
+ self._dtype=dtype
171
+ self._robot_names=robot_names
172
+ self._env_opts={}
173
+ self._env_opts["deact_when_failure"]=True
174
+ self._env_opts["filter_jnt_vel"]=False
175
+ self._env_opts["filter_cutoff_freq"]=10.0 # [Hz]
176
+ self._env_opts["filter_sampling_rate"]=100 # rate at which state is filtered [Hz]
177
+ self._env_opts["add_remote_exit_flag"]=False # add shared data server to trigger a remote exit
178
+
179
+ self._env_opts["enable_height_sensor"]=False
180
+ self._env_opts["height_sensor_resolution"]=0.16
181
+ self._env_opts["height_sensor_pixels"]=10
182
+ self._env_opts["height_sensor_lateral_offset"]=0.0
183
+ self._env_opts["height_sensor_forward_offset"]=0.0
184
+
185
+ self._filter_step_ssteps_freq=None
186
+
187
+ self._env_opts.update(env_opts)
188
+
189
+ self.step_counter = 0 # global step counter
190
+ self._n_init_steps = n_init_step # n steps to be performed before applying solutions from control clusters
191
+ self._srdf_dump_paths = robot_srdf_paths
192
+ self._homers = {}
193
+ self._homing = None
194
+ self._jnt_imp_cntrl_shared_data = {}
195
+ self._jnt_imp_controllers = {}
196
+ self._jnt_imp_config_paths = {}
197
+
198
+ # control cluster data
199
+ self.cluster_sim_step_counters = {}
200
+ self.cluster_servers = {}
201
+ self._trigger_sol = {}
202
+ self._wait_sol = {}
203
+ self._cluster_dt = {}
204
+ self._robot_urdf_paths={}
205
+ self._robot_srdf_paths={}
206
+ self._contact_names={}
207
+ self._num_contacts={}
208
+
209
+ for i in range(len(self._robot_names)):
210
+ robot_name = self._robot_names[i]
211
+ self._cluster_dt[robot_name]=cluster_dt[i]
212
+ self._robot_urdf_paths[robot_name]=robot_urdf_paths[i]
213
+ self._robot_srdf_paths[robot_name]=robot_srdf_paths[i]
214
+ self._contact_names[robot_name]=None
215
+ self._num_contacts[robot_name]=n_contacts[i]
216
+ self._jnt_imp_config_paths[robot_name]=jnt_imp_config_paths[i]
217
+ # db data
218
+ self.debug_data = {}
219
+ self.debug_data["time_to_step_world"] = np.nan
220
+ self.debug_data["time_to_get_states_from_env"] = np.nan
221
+ self.debug_data["cluster_sol_time"] = {}
222
+ self.debug_data["cluster_state_update_dt"] = {}
223
+ self.debug_data["sim_time"] = {}
224
+ self.debug_data["cluster_time"] = {}
225
+
226
+ self._env_timer = time.perf_counter()
227
+
228
+ # remote sim stepping options
229
+ self._timeout = timeout_ms # timeout for remote stepping
230
+ self._use_remote_stepping = use_remote_stepping
231
+ # should use remote stepping
232
+ self._remote_steppers = {}
233
+ self._remote_resetters = {}
234
+ self._remote_reset_requests = {}
235
+ self._is_first_trigger = {}
236
+
237
+ self._closed = False
238
+
239
+ self._this_child_path=os.path.abspath(inspect.getfile(self.__class__))
240
+ self._descr_dump_path=dump_basepath+"/"+f"{self.__class__.__name__}"
241
+ self._urdf_dump_paths = {}
242
+ self._srdf_dump_paths = {}
243
+ self.xrdf_cmd_vals = [] # by default empty, needs to be overriden by
244
+ # child class
245
+ self._world_iface_files_server=None
246
+
247
+ self._override_low_lev_controller=override_low_lev_controller
248
+
249
+ self._root_p = {}
250
+ self._root_q = {}
251
+ self._jnts_q = {}
252
+ self._root_p_prev = {} # used for num differentiation
253
+ self._root_q_prev = {} # used for num differentiation
254
+ self._jnts_q_prev = {} # used for num differentiation
255
+ self._root_v_prev = {} # used for num differentiation
256
+ self._root_omega_prev = {} # used for num differentiation
257
+ self._root_p_default = {}
258
+ self._root_q_default = {}
259
+ self._jnts_q_default = {}
260
+
261
+ self._gravity_normalized = {}
262
+ self._gravity_normalized_base_loc = {}
263
+
264
+ self._root_v = {}
265
+ self._root_v_base_loc = {}
266
+ self._root_v_default = {}
267
+ self._root_omega = {}
268
+ self._root_omega_base_loc = {}
269
+ self._root_omega_default = {}
270
+ self._root_a = {}
271
+ self._root_a_base_loc = {}
272
+ self._root_alpha = {}
273
+ self._root_alpha_base_loc = {}
274
+
275
+ self._jnts_v = {}
276
+ self._jnt_vel_filter = {}
277
+ self._jnts_v_default = {}
278
+ self._jnts_eff = {}
279
+ self._jnts_eff_default = {}
280
+
281
+ self._root_pos_offsets = {}
282
+ self._root_q_offsets = {}
283
+
284
+ self._parse_env_opts()
285
+
286
+ self._enable_height_shared = self._env_opts["enable_height_sensor"]
287
+ self._height_sensor_resolution = self._env_opts["height_sensor_resolution"]
288
+ self._height_sensor_pixels = self._env_opts["height_sensor_pixels"]
289
+
290
+ self._pre_setup() # child's method
291
+
292
+ self._init_world() # after this point all info from sim or robot is
293
+ # available
294
+
295
+ self._publish_world_interface_files()
296
+
297
+ self._setup()
298
+
299
+ self._exit_request=False
300
+ signal.signal(signal.SIGINT, self.signal_handler)
301
+
302
+ def signal_handler(self, sig, frame):
303
+ Journal.log(self.__class__.__name__,
304
+ "signal_handler",
305
+ "received SIGINT -> cleaning up",
306
+ LogType.WARN)
307
+ self._exit_request=True
308
+
309
+ def __del__(self):
310
+ self.close()
311
+
312
+ def is_closed(self):
313
+ return self._closed
314
+
315
+ def close(self) -> None:
316
+ if not self._closed:
317
+ for i in range(len(self._robot_names)):
318
+ if self._robot_names[i] in self.cluster_servers:
319
+ self.cluster_servers[self._robot_names[i]].close()
320
+ if self._use_remote_stepping[i]: # remote signaling
321
+ if self._robot_names[i] in self._remote_reset_requests:
322
+ self._remote_reset_requests[self._robot_names[i]].close()
323
+ self._remote_resetters[self._robot_names[i]].close()
324
+ self._remote_steppers[self._robot_names[i]].close()
325
+ if self._robot_names[i] in self._jnt_imp_cntrl_shared_data:
326
+ jnt_imp_shared_data=self._jnt_imp_cntrl_shared_data[self._robot_names[i]]
327
+ if jnt_imp_shared_data is not None:
328
+ jnt_imp_shared_data.close()
329
+ if self._remote_exit_flag is not None:
330
+ self._remote_exit_flag.close()
331
+ if self._world_iface_files_server is not None:
332
+ self._world_iface_files_server.close()
333
+ self._close()
334
+ self._closed=True
335
+
336
+ def _collect_world_interface_files(self):
337
+ files = [self._this_child_path]
338
+ # prefer generated URDF/SRDF if available, fallback to provided xacros
339
+ if len(self._urdf_dump_paths) > 0:
340
+ files.extend(list(self._urdf_dump_paths.values()))
341
+ else:
342
+ files.extend(list(self._robot_urdf_paths.values()))
343
+ if len(self._srdf_dump_paths) > 0:
344
+ files.extend(list(self._srdf_dump_paths.values()))
345
+ else:
346
+ files.extend(list(self._robot_srdf_paths.values()))
347
+ files.extend(list(self._jnt_imp_config_paths.values()))
348
+ # remove duplicates while preserving order
349
+ unique_files=[]
350
+ for f in files:
351
+ if f not in unique_files:
352
+ unique_files.append(f)
353
+ return unique_files
354
+
355
+ def _publish_world_interface_files(self):
356
+
357
+ if not any(self._use_remote_stepping):
358
+ return
359
+ self._world_iface_files_server=StringTensorServer(length=1,
360
+ basename="SharedWorldInterfaceFilesDropDir",
361
+ name_space=self._robot_names[0],
362
+ verbose=self._verbose,
363
+ vlevel=self._vlevel,
364
+ force_reconnection=True)
365
+ self._world_iface_files_server.run()
366
+ combined_paths=", ".join(self._collect_world_interface_files())
367
+ while not self._world_iface_files_server.write_vec([combined_paths], 0):
368
+ Journal.log(self.__class__.__name__,
369
+ "_publish_world_interface_files",
370
+ f"Failed to pub world interface files. Retrying...",
371
+ LogType.WARN)
372
+ time.sleep(0.1)
373
+ Journal.log(self.__class__.__name__,
374
+ "_publish_world_interface_files",
375
+ f"World interface files advertised: {combined_paths}",
376
+ LogType.STAT)
377
+
378
+ def _setup(self) -> None:
379
+
380
+ for i in range(len(self._robot_names)):
381
+ robot_name = self._robot_names[i]
382
+
383
+ # normalized gravity vector
384
+ self._gravity_normalized[robot_name]=torch.full_like(self._root_v[robot_name], fill_value=0.0)
385
+ self._gravity_normalized[robot_name][:, 2]=-1.0
386
+ self._gravity_normalized_base_loc[robot_name]=self._gravity_normalized[robot_name].detach().clone()
387
+
388
+ self.cluster_sim_step_counters[robot_name]=0
389
+ self._is_first_trigger[robot_name] = True
390
+ if not isinstance(self._cluster_dt[robot_name], (float)):
391
+ exception = f"cluster_dt[{i}] should be a float!"
392
+ Journal.log(self.__class__.__name__,
393
+ "_setup",
394
+ exception,
395
+ LogType.EXCEP,
396
+ throw_when_excep = True)
397
+ self._cluster_dt[robot_name] = self._cluster_dt[robot_name]
398
+ self._trigger_sol[robot_name] = True # allow first trigger
399
+ self._wait_sol[robot_name] = False
400
+
401
+ # initialize a lrhc cluster server for communicating with rhc controllers
402
+ self.cluster_servers[robot_name] = AugMpcClusterServer(cluster_size=self._num_envs,
403
+ cluster_dt=self._cluster_dt[robot_name],
404
+ control_dt=self.physics_dt(),
405
+ jnt_names=self._robot_jnt_names(robot_name=robot_name),
406
+ n_contacts=self._n_contacts(robot_name=robot_name),
407
+ contact_linknames=self._contact_names[robot_name],
408
+ verbose=self._verbose,
409
+ vlevel=self._vlevel,
410
+ debug=self._debug,
411
+ robot_name=robot_name,
412
+ use_gpu=self._use_gpu,
413
+ force_reconnection=self._force_reconnection,
414
+ timeout_ms=self._timeout,
415
+ enable_height_sensor=self._enable_height_shared,
416
+ height_grid_size=self._height_sensor_pixels,
417
+ height_grid_resolution=self._height_sensor_resolution)
418
+ self.cluster_servers[robot_name].run()
419
+ self.debug_data["cluster_sol_time"][robot_name] = np.nan
420
+ self.debug_data["cluster_state_update_dt"][robot_name] = np.nan
421
+ self.debug_data["sim_time"][robot_name] = np.nan
422
+ # remote sim stepping
423
+ if self._use_remote_stepping[i]:
424
+ self._remote_steppers[robot_name] = RemoteStepperClnt(namespace=robot_name,
425
+ verbose=self._debug,
426
+ vlevel=self._vlevel)
427
+ self._remote_resetters[robot_name] = RemoteResetClnt(namespace=robot_name,
428
+ verbose=self._debug,
429
+ vlevel=self._vlevel)
430
+ self._remote_reset_requests[robot_name] = RemoteResetRequest(namespace=robot_name,
431
+ n_env=self._num_envs,
432
+ is_server=True,
433
+ verbose=self._debug,
434
+ vlevel=self._vlevel,
435
+ force_reconnection=self._force_reconnection,
436
+ safe=False)
437
+ self._remote_steppers[robot_name].run()
438
+ self._remote_resetters[robot_name].run()
439
+ self._remote_reset_requests[robot_name].run()
440
+ else:
441
+ self._remote_steppers[robot_name] = None
442
+ self._remote_reset_requests[robot_name] = None
443
+ self._remote_resetters[robot_name] = None
444
+
445
+ self._homers[robot_name] = RobotHomer(srdf_path=self._srdf_dump_paths[robot_name],
446
+ jnt_names=self._robot_jnt_names(robot_name=robot_name),
447
+ filter=True)
448
+ robot_homing=torch.from_numpy(self._homers[robot_name].get_homing().reshape(1,-1))
449
+ if "cuda" in self._device:
450
+ robot_homing=robot_homing.cuda()
451
+ self._homing=robot_homing.repeat(self._num_envs, 1)
452
+
453
+ self._jnts_q_default[robot_name] = self._homing
454
+ self._set_jnts_to_homing(robot_name=robot_name)
455
+ self._set_root_to_defconfig(robot_name=robot_name)
456
+ self._reset_sim()
457
+
458
+ self._init_safe_cluster_actions(robot_name=robot_name)
459
+
460
+ Journal.log(self.__class__.__name__,
461
+ "_setup",
462
+ f"Will use joint impedance config at {self._jnt_imp_config_paths[robot_name]} for {robot_name}",
463
+ LogType.STAT)
464
+
465
+ self._jnt_imp_controllers[robot_name] = self._generate_jnt_imp_control(robot_name=robot_name)
466
+ self._jnt_imp_cntrl_shared_data[robot_name] = JntImpCntrlData(is_server=True,
467
+ n_envs=self._num_envs,
468
+ n_jnts=len(self._robot_jnt_names(robot_name=robot_name)),
469
+ jnt_names=self._robot_jnt_names(robot_name=robot_name),
470
+ namespace=robot_name,
471
+ verbose=self._verbose,
472
+ force_reconnection=self._force_reconnection,
473
+ vlevel=self._vlevel,
474
+ use_gpu=self._use_gpu,
475
+ safe=False)
476
+ self._jnt_imp_cntrl_shared_data[robot_name].run()
477
+
478
+ self._jnt_vel_filter[robot_name]=None
479
+ if self._env_opts["filter_jnt_vel"]:
480
+ self._jnt_vel_filter[robot_name]=FirstOrderFilter(dt=1.0/self._env_opts["filter_sampling_rate"],
481
+ filter_BW=self._env_opts["filter_cutoff_freq"],
482
+ rows=self._num_envs,
483
+ cols=len(self._robot_jnt_names(robot_name=robot_name)),
484
+ device=self._device,
485
+ dtype=self._dtype)
486
+
487
+ physics_rate=1.0/self.physics_dt()
488
+ self._filter_step_ssteps_freq=int(physics_rate/self._env_opts["filter_sampling_rate"])
489
+ if self._filter_step_ssteps_freq <=0:
490
+ Journal.log(self.__class__.__name__,
491
+ "_setup",
492
+ f"The filter_sampling_rate should be smaller that the physics rate ({physics_rate} Hz)",
493
+ LogType.EXCEP,
494
+ throw_when_excep=True)
495
+
496
+ for n in range(self._n_init_steps): # run some initialization steps
497
+ self._step_world()
498
+ if hasattr(self, "_zero_angular_velocities"):
499
+ self._zero_angular_velocities(robot_name=robot_name, env_indxs=None)
500
+ self._read_jnts_state_from_robot(robot_name=robot_name,
501
+ env_indxs=None)
502
+ self._read_root_state_from_robot(robot_name=robot_name,
503
+ env_indxs=None)
504
+ # allow child to perform additional warmup validations (e.g., terrain/tilt)
505
+ # retry_done = False
506
+ if hasattr(self, "_post_warmup_validation"):
507
+ failing = self._post_warmup_validation(robot_name=robot_name)
508
+ if failing is not None and failing.numel() > 0:
509
+ # retry: reset only failing envs, rerun warmup, revalidate once
510
+ failing = failing.to(self._device)
511
+ Journal.log(self.__class__.__name__,
512
+ "_setup",
513
+ f"Warmup validation failed for {robot_name}, envs indexes {failing.tolist()}",
514
+ LogType.EXCEP,
515
+ throw_when_excep=True)
516
+ else:
517
+ Journal.log(self.__class__.__name__,
518
+ "_setup",
519
+ f"Warmup validation passed for {robot_name}",
520
+ LogType.INFO)
521
+
522
+ # write some inits for all robots
523
+ self._update_root_offsets(robot_name)
524
+ self._synch_default_root_states(robot_name=robot_name)
525
+ epsi=0.03 # adding a bit of height to avoid initial penetration
526
+ self._root_p_default[robot_name][:, 2]=self._root_p_default[robot_name][:, 2]+epsi
527
+
528
+ self._reset(env_indxs=None,
529
+ robot_name=robot_name,
530
+ reset_cluster=True,
531
+ reset_cluster_counter=False,
532
+ randomize=True)
533
+
534
+ control_cluster=self.cluster_servers[robot_name]
535
+ self._set_state_to_cluster(robot_name=robot_name)
536
+ control_cluster.write_robot_state()
537
+ control_cluster.pre_trigger()
538
+ to_be_activated=control_cluster.get_inactive_controllers()
539
+ if to_be_activated is not None:
540
+ control_cluster.activate_controllers(
541
+ idxs=to_be_activated)
542
+
543
+ if self._use_remote_stepping[i]:
544
+ self._wait_for_remote_step_req(robot_name=robot_name)
545
+
546
+ self._set_startup_jnt_imp_gains(robot_name=robot_name) # set gains to
547
+ # startup config (usually lower)
548
+ control_cluster.trigger_solution()
549
+
550
+ if self._env_opts["add_remote_exit_flag"]:
551
+ self._remote_exit_flag=SharedTWrapper(namespace = self._robot_names[0],# use first robot as name
552
+ basename = "IbridoRemoteEnvExitFlag",
553
+ is_server = True,
554
+ n_rows = 1,
555
+ n_cols = 1,
556
+ verbose = True,
557
+ vlevel = self._vlevel,
558
+ safe = False,
559
+ dtype=dtype.Bool,
560
+ force_reconnection=True,
561
+ fill_value = False)
562
+ self._remote_exit_flag.run()
563
+
564
+ self._setup_done=True
565
+
566
+ def step(self) -> bool:
567
+
568
+ success=False
569
+
570
+ if self._remote_exit_flag is not None:
571
+ # check for exit request
572
+ self._remote_exit_flag.synch_all(read=True, retry = False)
573
+ self._exit_request=bool(self._remote_exit_flag.get_numpy_mirror()[0, 0].item())
574
+
575
+ if self._exit_request:
576
+ self.close()
577
+
578
+ if self.is_running() and (not self.is_closed()):
579
+ if self._debug:
580
+ self._pre_step_db()
581
+ self._env_timer=time.perf_counter()
582
+ self._step_world()
583
+ self.debug_data["time_to_step_world"] = \
584
+ time.perf_counter() - self._env_timer
585
+ self._post_world_step_db()
586
+ success=True
587
+ else:
588
+ self._pre_step()
589
+ self._step_world()
590
+ self._post_world_step()
591
+ success=True
592
+
593
+ return success
594
+
595
+ def render(self, mode:str="human") -> None:
596
+ self._render_sim(mode)
597
+
598
+ def reset(self,
599
+ env_indxs: torch.Tensor = None,
600
+ reset_cluster: bool = False,
601
+ reset_cluster_counter = False,
602
+ randomize: bool = False,
603
+ reset_sim: bool = False) -> None:
604
+
605
+ for i in range(len(self._robot_names)):
606
+ robot_name=self._robot_names[i]
607
+ self._reset(robot_name=robot_name,
608
+ env_indxs=env_indxs,
609
+ randomize=randomize,
610
+ reset_cluster=reset_cluster,
611
+ reset_cluster_counter=reset_cluster_counter)
612
+ self._set_startup_jnt_imp_gains(robot_name=robot_name,
613
+ env_indxs=env_indxs)
614
+
615
+ if reset_sim:
616
+ self._reset_sim()
617
+
618
+ def _reset_cluster(self,
619
+ robot_name: str,
620
+ env_indxs: torch.Tensor = None,
621
+ reset_cluster_counter: bool = False):
622
+
623
+ control_cluster = self.cluster_servers[robot_name]
624
+
625
+ control_cluster.reset_controllers(idxs=env_indxs)
626
+
627
+ self._set_state_to_cluster(robot_name=robot_name,
628
+ env_indxs=env_indxs)
629
+ control_cluster.write_robot_state() # writes to shared memory
630
+
631
+ if reset_cluster_counter:
632
+ self.cluster_sim_step_counters[robot_name] = 0
633
+
634
+ def _step_jnt_vel_filter(self,
635
+ robot_name: str,
636
+ env_indxs: torch.Tensor = None):
637
+
638
+ self._jnt_vel_filter[robot_name].update(refk=self.jnts_v(robot_name=robot_name, env_idxs=env_indxs),
639
+ idxs=env_indxs)
640
+
641
+ def _set_state_to_cluster(self,
642
+ robot_name: str,
643
+ env_indxs: torch.Tensor = None,
644
+ base_loc: bool = True):
645
+
646
+ if self._debug:
647
+ if not isinstance(env_indxs, (torch.Tensor, type(None))):
648
+ msg = "Provided env_indxs should be a torch tensor of indexes!"
649
+ raise Exception(f"[{self.__class__.__name__}]" + f"[{self.journal.exception}]: " + msg)
650
+
651
+ control_cluster = self.cluster_servers[robot_name]
652
+ # floating base
653
+ rhc_state = control_cluster.get_state()
654
+ # configuration
655
+ rhc_state.root_state.set(data=self.root_p_rel(robot_name=robot_name, env_idxs=env_indxs),
656
+ data_type="p", robot_idxs = env_indxs, gpu=self._use_gpu)
657
+ rhc_state.root_state.set(data=self.root_q(robot_name=robot_name, env_idxs=env_indxs),
658
+ data_type="q", robot_idxs = env_indxs, gpu=self._use_gpu)
659
+
660
+ # twist
661
+ rhc_state.root_state.set(data=self.root_v(robot_name=robot_name, env_idxs=env_indxs,base_loc=base_loc),
662
+ data_type="v", robot_idxs = env_indxs, gpu=self._use_gpu)
663
+ rhc_state.root_state.set(data=self.root_omega(robot_name=robot_name, env_idxs=env_indxs,base_loc=base_loc),
664
+ data_type="omega", robot_idxs = env_indxs, gpu=self._use_gpu)
665
+
666
+ # angular accc.
667
+ rhc_state.root_state.set(data=self.root_a(robot_name=robot_name, env_idxs=env_indxs,base_loc=base_loc),
668
+ data_type="a", robot_idxs = env_indxs, gpu=self._use_gpu)
669
+ rhc_state.root_state.set(data=self.root_alpha(robot_name=robot_name, env_idxs=env_indxs,base_loc=base_loc),
670
+ data_type="alpha", robot_idxs = env_indxs, gpu=self._use_gpu)
671
+
672
+ # gravity vec
673
+ rhc_state.root_state.set(data=self.gravity(robot_name=robot_name, env_idxs=env_indxs,base_loc=base_loc),
674
+ data_type="gn", robot_idxs = env_indxs, gpu=self._use_gpu)
675
+
676
+ # joints
677
+ rhc_state.jnts_state.set(data=self.jnts_q(robot_name=robot_name, env_idxs=env_indxs),
678
+ data_type="q", robot_idxs = env_indxs, gpu=self._use_gpu)
679
+
680
+ v_jnts=self.jnts_v(robot_name=robot_name, env_idxs=env_indxs)
681
+ if self._jnt_vel_filter[robot_name] is not None: # apply filtering
682
+ v_jnts=self._jnt_vel_filter[robot_name].get(idxs=env_indxs)
683
+ rhc_state.jnts_state.set(data=v_jnts,
684
+ data_type="v", robot_idxs = env_indxs, gpu=self._use_gpu)
685
+ rhc_state.jnts_state.set(data=self.jnts_eff(robot_name=robot_name, env_idxs=env_indxs),
686
+ data_type="eff", robot_idxs = env_indxs, gpu=self._use_gpu)
687
+
688
+ # height map
689
+ if self._enable_height_shared:
690
+ hdata = self._height_imgs[robot_name]
691
+ if env_indxs is not None:
692
+ hdata = hdata[env_indxs]
693
+ flat = hdata.reshape(hdata.shape[0], -1)
694
+ rhc_state.height_sensor.set(data=flat, data_type=None, robot_idxs=env_indxs, gpu=self._use_gpu)
695
+
696
+ # Updating contact state for selected contact links
697
+ self._update_contact_state(robot_name=robot_name, env_indxs=env_indxs)
698
+
699
+ def _update_contact_state(self,
700
+ robot_name: str,
701
+ env_indxs: torch.Tensor = None):
702
+ for i in range(0, self.cluster_servers[robot_name].n_contact_sensors()):
703
+ contact_link = self.cluster_servers[robot_name].contact_linknames()[i]
704
+ f_contact = self._get_contact_f(robot_name=robot_name,
705
+ contact_link=contact_link,
706
+ env_indxs=env_indxs)
707
+ if f_contact is not None:
708
+ self.cluster_servers[robot_name].get_state().contact_wrenches.set(data=f_contact, data_type="f",
709
+ contact_name=contact_link,
710
+ robot_idxs = env_indxs,
711
+ gpu=self._use_gpu)
712
+
713
+ def _init_safe_cluster_actions(self,
714
+ robot_name: str):
715
+
716
+ # this does not actually write on shared memory,
717
+ # but it's enough to get safe actions for the simulator before the
718
+ # cluster starts to receive data from the controllers
719
+ control_cluster = self.cluster_servers[robot_name]
720
+ rhc_cmds = control_cluster.get_actions()
721
+ n_jnts = rhc_cmds.n_jnts()
722
+
723
+ null_action = torch.zeros((self._num_envs, n_jnts),
724
+ dtype=self._dtype,
725
+ device=self._device)
726
+ rhc_cmds.jnts_state.set(data=self._homing, data_type="q", gpu=self._use_gpu)
727
+ rhc_cmds.jnts_state.set(data=null_action, data_type="v", gpu=self._use_gpu)
728
+ rhc_cmds.jnts_state.set(data=null_action, data_type="eff", gpu=self._use_gpu)
729
+
730
+ def _pre_step_db(self) -> None:
731
+
732
+ # cluster step logic here
733
+ for i in range(len(self._robot_names)):
734
+ robot_name = self._robot_names[i]
735
+
736
+ if self._override_low_lev_controller:
737
+ # if overriding low-lev jnt imp. this has to run at the highest
738
+ # freq possible
739
+ start=time.perf_counter()
740
+ self._read_jnts_state_from_robot(robot_name=robot_name)
741
+ self.debug_data["time_to_get_states_from_env"]= time.perf_counter()-start
742
+
743
+ self._write_state_to_jnt_imp(robot_name=robot_name)
744
+ self._apply_cmds_to_jnt_imp_control(robot_name=robot_name)
745
+
746
+ if self._jnt_vel_filter[robot_name] is not None and \
747
+ (self.cluster_sim_step_counters[robot_name]+1) % self._filter_step_ssteps_freq == 0:
748
+ # filter joint vel at a fixed frequency wrt sim steps
749
+ if not self._override_low_lev_controller:
750
+ # we need a fresh sensor reading
751
+ self._read_jnts_state_from_robot(robot_name=robot_name)
752
+ self._step_jnt_vel_filter(robot_name=robot_name, env_indxs=None)
753
+
754
+ control_cluster = self.cluster_servers[robot_name]
755
+ if control_cluster.is_cluster_instant(self.cluster_sim_step_counters[robot_name]):
756
+ control_cluster.wait_for_solution() # this is blocking
757
+ failed = control_cluster.get_failed_controllers(gpu=self._use_gpu)
758
+ self._set_cluster_actions(robot_name=robot_name) # write last cmds to low level control
759
+ if not self._override_low_lev_controller:
760
+ self._apply_cmds_to_jnt_imp_control(robot_name=robot_name) # apply to robot
761
+ # we can update the jnt state just at the rate at which the cluster needs it
762
+ start=time.perf_counter()
763
+ self._read_jnts_state_from_robot(robot_name=robot_name, env_indxs=None)
764
+ else:
765
+ # read state necessary for cluster
766
+ start=time.perf_counter()
767
+ self._read_root_state_from_robot(robot_name=robot_name,
768
+ env_indxs=None)
769
+ self.debug_data["time_to_get_states_from_env"]= time.perf_counter()-start
770
+ start=time.perf_counter()
771
+ self._set_state_to_cluster(robot_name=robot_name,
772
+ env_indxs=None)
773
+ control_cluster.write_robot_state()
774
+ self.debug_data["cluster_state_update_dt"][robot_name] = time.perf_counter()-start
775
+
776
+ self._update_jnt_imp_cntrl_shared_data() # only if debug_mode_jnt_imp is enabled
777
+
778
+ if self._use_remote_stepping[i]:
779
+ self._remote_steppers[robot_name].ack() # signal cluster stepping is finished
780
+ if failed is not None and self._env_opts["deact_when_failure"]: # deactivate robot completely
781
+ self._deactivate(env_indxs=failed,
782
+ robot_name=robot_name)
783
+
784
+ self._process_remote_reset_req(robot_name=robot_name) # wait for remote reset request (blocking)
785
+ self._wait_for_remote_step_req(robot_name=robot_name)
786
+ else:
787
+ if failed is not None:
788
+ self._reset(env_indxs=failed,
789
+ robot_name=robot_name,
790
+ reset_cluster=True,
791
+ reset_cluster_counter=False,
792
+ randomize=True)
793
+ self._set_startup_jnt_imp_gains(robot_name=robot_name,
794
+ env_indxs=failed)
795
+
796
+ control_cluster.activate_controllers(idxs=control_cluster.get_inactive_controllers())
797
+
798
+ control_cluster.pre_trigger() # performs pre-trigger steps, like retrieving
799
+ # values of some rhc flags on shared memory
800
+ control_cluster.trigger_solution() # trigger only active controllers
801
+
802
+ def _pre_step(self) -> None:
803
+
804
+ # cluster step logic here
805
+ for i in range(len(self._robot_names)):
806
+ robot_name = self._robot_names[i]
807
+ if self._override_low_lev_controller:
808
+ # if overriding low-lev jnt imp. this has to run at the highest
809
+ # freq possible
810
+ self._read_jnts_state_from_robot(robot_name=robot_name)
811
+ self._write_state_to_jnt_imp(robot_name=robot_name)
812
+ self._apply_cmds_to_jnt_imp_control(robot_name=robot_name)
813
+
814
+ if self._jnt_vel_filter[robot_name] is not None and \
815
+ (self.cluster_sim_step_counters[robot_name]+1) % self._filter_step_ssteps_freq == 0:
816
+ # filter joint vel at a fixed frequency wrt sim steps
817
+ if not self._override_low_lev_controller:
818
+ # we need a fresh sensor reading
819
+ self._read_jnts_state_from_robot(robot_name=robot_name)
820
+ self._step_jnt_vel_filter(robot_name=robot_name, env_indxs=None)
821
+
822
+ control_cluster = self.cluster_servers[robot_name]
823
+ if control_cluster.is_cluster_instant(self.cluster_sim_step_counters[robot_name]):
824
+ control_cluster.wait_for_solution() # this is blocking
825
+ failed = control_cluster.get_failed_controllers(gpu=self._use_gpu)
826
+ self._set_cluster_actions(robot_name=robot_name) # set last cmds to low level control
827
+ if not self._override_low_lev_controller:
828
+ self._apply_cmds_to_jnt_imp_control(robot_name=robot_name) # apply to robot
829
+ # we can update the jnt state just at the rate at which the cluster needs it
830
+ self._read_jnts_state_from_robot(robot_name=robot_name, env_indxs=None)
831
+ # read state necessary for cluster
832
+ self._read_root_state_from_robot(robot_name=robot_name,
833
+ env_indxs=None)
834
+ # write last robot state to the cluster of controllers
835
+ self._set_state_to_cluster(robot_name=robot_name,
836
+ env_indxs=None)
837
+ control_cluster.write_robot_state() # write on shared mem
838
+
839
+ if self._use_remote_stepping[i]:
840
+ self._remote_steppers[robot_name].ack() # signal cluster stepping is finished
841
+ if failed is not None and self._env_opts["deact_when_failure"]:
842
+ self._deactivate(env_indxs=failed,
843
+ robot_name=robot_name)
844
+ self._process_remote_reset_req(robot_name=robot_name) # wait for remote reset request (blocking)
845
+ self._wait_for_remote_step_req(robot_name=robot_name)
846
+ else:
847
+ if failed is not None:
848
+ self._reset(env_indxs=failed,
849
+ robot_name=robot_name,
850
+ reset_cluster=True,
851
+ reset_cluster_counter=False,
852
+ randomize=True)
853
+ self._set_startup_jnt_imp_gains(robot_name=robot_name,
854
+ env_indxs=failed)
855
+ control_cluster.activate_controllers(idxs=control_cluster.get_inactive_controllers())
856
+
857
+ control_cluster.pre_trigger() # performs pre-trigger steps, like retrieving
858
+ # values of some rhc flags on shared memory
859
+ control_cluster.trigger_solution() # trigger only active controllers
860
+
861
+ def _post_world_step_db(self) -> bool:
862
+
863
+ for i in range(len(self._robot_names)):
864
+ robot_name = self._robot_names[i]
865
+ control_cluster = self.cluster_servers[robot_name]
866
+ self.cluster_sim_step_counters[robot_name]+=1 # this has to be update with sim freq
867
+ if self._debug:
868
+ self.debug_data["sim_time"][robot_name]=self._get_world_time(robot_name=robot_name)
869
+ self.debug_data["cluster_sol_time"][robot_name] = \
870
+ control_cluster.solution_time()
871
+
872
+ self.step_counter +=1
873
+
874
+ def _get_world_time(self, robot_name: str):
875
+ return self.cluster_sim_step_counters[robot_name]*self.physics_dt()
876
+
877
+ def _post_world_step(self) -> bool:
878
+
879
+ for i in range(len(self._robot_names)):
880
+ robot_name = self._robot_names[i]
881
+ self.cluster_sim_step_counters[robot_name]+=1
882
+ self.step_counter +=1
883
+
884
+ def _reset(self,
885
+ robot_name: str,
886
+ env_indxs: torch.Tensor = None,
887
+ randomize: bool = False,
888
+ reset_cluster: bool = False,
889
+ reset_cluster_counter = False):
890
+
891
+ # resets the state of target robot and env to the defaults
892
+ self._reset_state(env_indxs=env_indxs,
893
+ robot_name=robot_name,
894
+ randomize=randomize)
895
+ # and jnt imp. controllers
896
+ self._reset_jnt_imp_control(robot_name=robot_name,
897
+ env_indxs=env_indxs)
898
+
899
+ self._read_jnts_state_from_robot(robot_name=robot_name,
900
+ env_indxs=env_indxs)
901
+ self._read_root_state_from_robot(robot_name=robot_name,
902
+ env_indxs=env_indxs)
903
+
904
+ if self._jnt_vel_filter[robot_name] is not None:
905
+ self._jnt_vel_filter[robot_name].reset(idxs=env_indxs)
906
+
907
+ if reset_cluster: # reset controllers remotely
908
+ self._reset_cluster(env_indxs=env_indxs,
909
+ robot_name=robot_name,
910
+ reset_cluster_counter=reset_cluster_counter)
911
+
912
+ def _randomize_yaw(self,
913
+ robot_name: str,
914
+ env_indxs: torch.Tensor = None):
915
+
916
+ root_q_default = self._root_q_default[robot_name]
917
+ if env_indxs is None:
918
+ env_indxs = torch.arange(root_q_default.shape[0])
919
+
920
+ num_indices = env_indxs.shape[0]
921
+ yaw_angles = torch.rand((num_indices,),
922
+ device=root_q_default.device) * 2 * torch.pi # uniformly distributed random angles
923
+
924
+ # Compute cos and sin once
925
+ cos_half = torch.cos(yaw_angles / 2)
926
+ root_q_default[env_indxs, :] = torch.stack((cos_half,
927
+ torch.zeros_like(cos_half),
928
+ torch.zeros_like(cos_half),
929
+ torch.sin(yaw_angles / 2)), dim=1).reshape(num_indices, 4)
930
+
931
+ def _deactivate(self,
932
+ robot_name: str,
933
+ env_indxs: torch.Tensor = None):
934
+
935
+ # deactivate jnt imp controllers for given robots and envs (makes the robot fall)
936
+ self._jnt_imp_controllers[robot_name].deactivate(robot_indxs=env_indxs)
937
+
938
+ def _n_contacts(self, robot_name: str) -> List[int]:
939
+ return self._num_contacts[robot_name]
940
+
941
+ def root_p(self,
942
+ robot_name: str,
943
+ env_idxs: torch.Tensor = None):
944
+
945
+ if env_idxs is None:
946
+ return self._root_p[robot_name]
947
+ else:
948
+ return self._root_p[robot_name][env_idxs, :]
949
+
950
+ def root_p_rel(self,
951
+ robot_name: str,
952
+ env_idxs: torch.Tensor = None):
953
+
954
+ rel_pos = torch.sub(self.root_p(robot_name=robot_name,
955
+ env_idxs=env_idxs),
956
+ self._root_pos_offsets[robot_name][env_idxs, :])
957
+ return rel_pos
958
+
959
+ def root_q(self,
960
+ robot_name: str,
961
+ env_idxs: torch.Tensor = None):
962
+
963
+ if env_idxs is None:
964
+ return self._root_q[robot_name]
965
+ else:
966
+ return self._root_q[robot_name][env_idxs, :]
967
+
968
+ def root_q_rel(self,
969
+ robot_name: str,
970
+ env_idxs: torch.Tensor = None):
971
+
972
+ rel_q = quaternion_difference(self._root_q_offsets[robot_name][env_idxs, :],
973
+ self.root_q(robot_name=robot_name,
974
+ env_idxs=env_idxs))
975
+ return rel_q
976
+
977
+ def root_v(self,
978
+ robot_name: str,
979
+ env_idxs: torch.Tensor = None,
980
+ base_loc: bool = True):
981
+
982
+ root_v=self._root_v[robot_name]
983
+ if base_loc:
984
+ root_v=self._root_v_base_loc[robot_name]
985
+ if env_idxs is None:
986
+ return root_v
987
+ else:
988
+ return root_v[env_idxs, :]
989
+
990
+ def root_omega(self,
991
+ robot_name: str,
992
+ env_idxs: torch.Tensor = None,
993
+ base_loc: bool = True):
994
+
995
+ root_omega=self._root_omega[robot_name]
996
+ if base_loc:
997
+ root_omega=self._root_omega_base_loc[robot_name]
998
+ if env_idxs is None:
999
+ return root_omega
1000
+ else:
1001
+ return root_omega[env_idxs, :]
1002
+
1003
+ def root_a(self,
1004
+ robot_name: str,
1005
+ env_idxs: torch.Tensor = None,
1006
+ base_loc: bool = True):
1007
+
1008
+ root_a=self._root_a[robot_name]
1009
+ if base_loc:
1010
+ root_a=self._root_a_base_loc[robot_name]
1011
+ if env_idxs is None:
1012
+ return root_a
1013
+ else:
1014
+ return root_a[env_idxs, :]
1015
+
1016
+ def root_alpha(self,
1017
+ robot_name: str,
1018
+ env_idxs: torch.Tensor = None,
1019
+ base_loc: bool = True):
1020
+
1021
+ root_alpha=self._root_alpha[robot_name]
1022
+ if base_loc:
1023
+ root_alpha=self._root_alpha_base_loc[robot_name]
1024
+ if env_idxs is None:
1025
+ return root_alpha
1026
+ else:
1027
+ return root_alpha[env_idxs, :]
1028
+
1029
+ def gravity(self,
1030
+ robot_name: str,
1031
+ env_idxs: torch.Tensor = None,
1032
+ base_loc: bool = True):
1033
+
1034
+ gravity_loc=self._gravity_normalized[robot_name]
1035
+ if base_loc:
1036
+ gravity_loc=self._gravity_normalized_base_loc[robot_name]
1037
+ if env_idxs is None:
1038
+ return gravity_loc
1039
+ else:
1040
+ return gravity_loc[env_idxs, :]
1041
+
1042
+ def jnts_q(self,
1043
+ robot_name: str,
1044
+ env_idxs: torch.Tensor = None):
1045
+
1046
+ if env_idxs is None:
1047
+ return self._jnts_q[robot_name]
1048
+ else:
1049
+ return self._jnts_q[robot_name][env_idxs, :]
1050
+
1051
+ def jnts_v(self,
1052
+ robot_name: str,
1053
+ env_idxs: torch.Tensor = None):
1054
+
1055
+ if env_idxs is None:
1056
+ return self._jnts_v[robot_name]
1057
+ else:
1058
+ return self._jnts_v[robot_name][env_idxs, :]
1059
+
1060
+ def jnts_eff(self,
1061
+ robot_name: str,
1062
+ env_idxs: torch.Tensor = None): # (measured) efforts
1063
+
1064
+ if env_idxs is None:
1065
+ return self._jnts_eff[robot_name]
1066
+ else:
1067
+ return self._jnts_eff[robot_name][env_idxs, :]
1068
+
1069
+ def _wait_for_remote_step_req(self,
1070
+ robot_name: str):
1071
+ if not self._remote_steppers[robot_name].wait(self._timeout):
1072
+ self.close()
1073
+ Journal.log(self.__class__.__name__,
1074
+ "_wait_for_remote_step_req",
1075
+ "Didn't receive any remote step req within timeout!",
1076
+ LogType.EXCEP,
1077
+ throw_when_excep = True)
1078
+
1079
+ def _process_remote_reset_req(self,
1080
+ robot_name: str):
1081
+
1082
+ if not self._remote_resetters[robot_name].wait(self._timeout):
1083
+ self.close()
1084
+ Journal.log(self.__class__.__name__,
1085
+ "_process_remote_reset_req",
1086
+ "Didn't receive any remote reset req within timeout!",
1087
+ LogType.EXCEP,
1088
+ throw_when_excep = True)
1089
+
1090
+ reset_requests = self._remote_reset_requests[robot_name]
1091
+ reset_requests.synch_all(read=True, retry=True) # read reset requests from shared mem
1092
+ to_be_reset = reset_requests.to_be_reset(gpu=self._use_gpu)
1093
+ if to_be_reset is not None:
1094
+ self._reset(env_indxs=to_be_reset,
1095
+ robot_name=robot_name,
1096
+ reset_cluster=True,
1097
+ reset_cluster_counter=False,
1098
+ randomize=True)
1099
+ self._set_startup_jnt_imp_gains(robot_name=robot_name,
1100
+ env_indxs=to_be_reset)
1101
+ control_cluster = self.cluster_servers[robot_name]
1102
+ control_cluster.activate_controllers(idxs=to_be_reset) # activate controllers
1103
+ # (necessary if failed)
1104
+
1105
+ self._remote_resetters[robot_name].ack() # signal reset performed
1106
+
1107
+ def _update_jnt_imp_cntrl_shared_data(self):
1108
+ if self._debug:
1109
+ for i in range(0, len(self._robot_names)):
1110
+ robot_name = self._robot_names[i]
1111
+ # updating all the jnt impedance data - > this may introduce some overhead
1112
+ imp_data = self._jnt_imp_cntrl_shared_data[robot_name].imp_data_view
1113
+ # set data
1114
+ imp_data.set(data_type="pos_err",
1115
+ data=self._jnt_imp_controllers[robot_name].pos_err(),
1116
+ gpu=self._use_gpu)
1117
+ imp_data.set(data_type="vel_err",
1118
+ data=self._jnt_imp_controllers[robot_name].vel_err(),
1119
+ gpu=self._use_gpu)
1120
+ imp_data.set(data_type="pos_gains",
1121
+ data=self._jnt_imp_controllers[robot_name].pos_gains(),
1122
+ gpu=self._use_gpu)
1123
+ imp_data.set(data_type="vel_gains",
1124
+ data=self._jnt_imp_controllers[robot_name].vel_gains(),
1125
+ gpu=self._use_gpu)
1126
+ imp_data.set(data_type="eff_ff",
1127
+ data=self._jnt_imp_controllers[robot_name].eff_ref(),
1128
+ gpu=self._use_gpu)
1129
+ imp_data.set(data_type="pos",
1130
+ data=self._jnt_imp_controllers[robot_name].pos(),
1131
+ gpu=self._use_gpu)
1132
+ imp_data.set(data_type="pos_ref",
1133
+ data=self._jnt_imp_controllers[robot_name].pos_ref(),
1134
+ gpu=self._use_gpu)
1135
+ imp_data.set(data_type="vel",
1136
+ data=self._jnt_imp_controllers[robot_name].vel(),
1137
+ gpu=self._use_gpu)
1138
+ imp_data.set(data_type="vel_ref",
1139
+ data=self._jnt_imp_controllers[robot_name].vel_ref(),
1140
+ gpu=self._use_gpu)
1141
+ imp_data.set(data_type="eff",
1142
+ data=self._jnt_imp_controllers[robot_name].eff(),
1143
+ gpu=self._use_gpu)
1144
+ imp_data.set(data_type="imp_eff",
1145
+ data=self._jnt_imp_controllers[robot_name].imp_eff(),
1146
+ gpu=self._use_gpu)
1147
+ # copy from GPU to CPU if using gpu
1148
+ if self._use_gpu:
1149
+ imp_data.synch_mirror(from_gpu=True,non_blocking=True)
1150
+ # even if it's from GPU->CPu we can use non-blocking since it's just for db
1151
+ # purposes
1152
+ # write copies to shared memory
1153
+ imp_data.synch_all(read=False, retry=False)
1154
+
1155
+ def _set_startup_jnt_imp_gains(self,
1156
+ robot_name:str,
1157
+ env_indxs: torch.Tensor = None):
1158
+
1159
+ startup_p_gains=self._jnt_imp_controllers[robot_name].startup_p_gains()
1160
+ startup_d_gains=self._jnt_imp_controllers[robot_name].startup_d_gains()
1161
+
1162
+ if env_indxs is not None:
1163
+ self._jnt_imp_controllers[robot_name].set_gains(robot_indxs=env_indxs,
1164
+ pos_gains=startup_p_gains[env_indxs, :],
1165
+ vel_gains=startup_d_gains[env_indxs, :])
1166
+ else:
1167
+ self._jnt_imp_controllers[robot_name].set_gains(robot_indxs=env_indxs,
1168
+ pos_gains=startup_p_gains[:, :],
1169
+ vel_gains=startup_d_gains[:, :])
1170
+
1171
+ def _write_state_to_jnt_imp(self,
1172
+ robot_name: str):
1173
+
1174
+ # always update ,imp. controller internal state (jnt imp control is supposed to be
1175
+ # always running)
1176
+ self._jnt_imp_controllers[robot_name].update_state(pos=self.jnts_q(robot_name=robot_name),
1177
+ vel = self.jnts_v(robot_name=robot_name),
1178
+ eff = self.jnts_eff(robot_name=robot_name))
1179
+
1180
+ def _set_cluster_actions(self,
1181
+ robot_name):
1182
+ control_cluster = self.cluster_servers[robot_name]
1183
+ actions=control_cluster.get_actions()
1184
+ active_controllers=control_cluster.get_active_controllers(gpu=self._use_gpu)
1185
+
1186
+ if active_controllers is not None:
1187
+ self._jnt_imp_controllers[robot_name].set_refs(
1188
+ pos_ref=actions.jnts_state.get(data_type="q", gpu=self._use_gpu)[active_controllers, :],
1189
+ vel_ref=actions.jnts_state.get(data_type="v", gpu=self._use_gpu)[active_controllers, :],
1190
+ eff_ref=actions.jnts_state.get(data_type="eff", gpu=self._use_gpu)[active_controllers, :],
1191
+ robot_indxs=active_controllers)
1192
+
1193
+ def _jnt_imp_reset_overrride(self, robot_name:str):
1194
+ # to be overriden
1195
+ pass
1196
+
1197
+ def _apply_cmds_to_jnt_imp_control(self, robot_name:str):
1198
+
1199
+ self._jnt_imp_controllers[robot_name].apply_cmds()
1200
+
1201
+ def _update_root_offsets(self,
1202
+ robot_name: str,
1203
+ env_indxs: torch.Tensor = None):
1204
+
1205
+ if self._debug:
1206
+ for_robots = ""
1207
+ if env_indxs is not None:
1208
+ if not isinstance(env_indxs, torch.Tensor):
1209
+ msg = "Provided env_indxs should be a torch tensor of indexes!"
1210
+ Journal.log(self.__class__.__name__,
1211
+ "update_root_offsets",
1212
+ msg,
1213
+ LogType.EXCEP,
1214
+ throw_when_excep = True)
1215
+ if self._use_gpu:
1216
+ if not env_indxs.device.type == "cuda":
1217
+ error = "Provided env_indxs should be on GPU!"
1218
+ Journal.log(self.__class__.__name__,
1219
+ "_step_jnt_imp_control",
1220
+ error,
1221
+ LogType.EXCEP,
1222
+ True)
1223
+ else:
1224
+ if not env_indxs.device.type == "cpu":
1225
+ error = "Provided env_indxs should be on CPU!"
1226
+ Journal.log(self.__class__.__name__,
1227
+ "_step_jnt_imp_control",
1228
+ error,
1229
+ LogType.EXCEP,
1230
+ True)
1231
+ for_robots = f"for robot {robot_name}, indexes: " + str(env_indxs.tolist())
1232
+ if self._verbose:
1233
+ Journal.log(self.__class__.__name__,
1234
+ "update_root_offsets",
1235
+ f"updating root offsets " + for_robots,
1236
+ LogType.STAT,
1237
+ throw_when_excep = True)
1238
+
1239
+ # only planar position used
1240
+ if env_indxs is None:
1241
+ self._root_pos_offsets[robot_name][:, 0:2] = self._root_p[robot_name][:, 0:2]
1242
+ self._root_q_offsets[robot_name][:, :] = self._root_q[robot_name]
1243
+
1244
+ else:
1245
+ self._root_pos_offsets[robot_name][env_indxs, 0:2] = self._root_p[robot_name][env_indxs, 0:2]
1246
+ self._root_q_offsets[robot_name][env_indxs, :] = self._root_q[robot_name][env_indxs, :]
1247
+
1248
+ def _reset_jnt_imp_control(self,
1249
+ robot_name: str,
1250
+ env_indxs: torch.Tensor = None):
1251
+
1252
+ if self._debug:
1253
+ for_robots = ""
1254
+ if env_indxs is not None:
1255
+ if not isinstance(env_indxs, torch.Tensor):
1256
+ Journal.log(self.__class__.__name__,
1257
+ "reset_jnt_imp_control",
1258
+ "Provided env_indxs should be a torch tensor of indexes!",
1259
+ LogType.EXCEP,
1260
+ throw_when_excep = True)
1261
+ if self._use_gpu:
1262
+ if not env_indxs.device.type == "cuda":
1263
+ error = "Provided env_indxs should be on GPU!"
1264
+ Journal.log(self.__class__.__name__,
1265
+ "_step_jnt_imp_control",
1266
+ error,
1267
+ LogType.EXCEP,
1268
+ True)
1269
+ else:
1270
+ if not env_indxs.device.type == "cpu":
1271
+ error = "Provided env_indxs should be on CPU!"
1272
+ Journal.log(self.__class__.__name__,
1273
+ "_step_jnt_imp_control",
1274
+ error,
1275
+ LogType.EXCEP,
1276
+ True)
1277
+ for_robots = f"for robot {robot_name}, indexes: " + str(env_indxs)
1278
+
1279
+ if self._verbose:
1280
+ Journal.log(self.__class__.__name__,
1281
+ "reset_jnt_imp_control",
1282
+ f"resetting joint impedances " + for_robots,
1283
+ LogType.STAT,
1284
+ throw_when_excep = True)
1285
+
1286
+ # resets all internal data, refs to defaults
1287
+ self._jnt_imp_controllers[robot_name].reset(robot_indxs=env_indxs)
1288
+
1289
+ #restore jnt imp refs to homing
1290
+ if env_indxs is None:
1291
+ self._jnt_imp_controllers[robot_name].set_refs(pos_ref=self._homing[:, :],
1292
+ robot_indxs = None)
1293
+ else:
1294
+ self._jnt_imp_controllers[robot_name].set_refs(pos_ref=self._homing[env_indxs, :],
1295
+ robot_indxs = env_indxs)
1296
+
1297
+ # self._write_state_to_jnt_imp(robot_name=robot_name)
1298
+ # actually applies reset commands to the articulation
1299
+ self._write_state_to_jnt_imp(robot_name=robot_name)
1300
+ self._jnt_imp_reset_overrride(robot_name=robot_name)
1301
+ self._apply_cmds_to_jnt_imp_control(robot_name=robot_name)
1302
+
1303
+ def _synch_default_root_states(self,
1304
+ robot_name: str,
1305
+ env_indxs: torch.Tensor = None):
1306
+
1307
+ if self._debug:
1308
+ for_robots = ""
1309
+ if env_indxs is not None:
1310
+ if not isinstance(env_indxs, torch.Tensor):
1311
+ msg = "Provided env_indxs should be a torch tensor of indexes!"
1312
+ Journal.log(self.__class__.__name__,
1313
+ "synch_default_root_states",
1314
+ msg,
1315
+ LogType.EXCEP,
1316
+ throw_when_excep = True)
1317
+ if self._use_gpu:
1318
+ if not env_indxs.device.type == "cuda":
1319
+ error = "Provided env_indxs should be on GPU!"
1320
+ Journal.log(self.__class__.__name__,
1321
+ "_step_jnt_imp_control",
1322
+ error,
1323
+ LogType.EXCEP,
1324
+ True)
1325
+ else:
1326
+ if not env_indxs.device.type == "cpu":
1327
+ error = "Provided env_indxs should be on CPU!"
1328
+ Journal.log(self.__class__.__name__,
1329
+ "_step_jnt_imp_control",
1330
+ error,
1331
+ LogType.EXCEP,
1332
+ True)
1333
+ for_robots = f"for robot {robot_name}, indexes: " + str(env_indxs.tolist())
1334
+ if self._verbose:
1335
+ Journal.log(self.__class__.__name__,
1336
+ "synch_default_root_states",
1337
+ f"updating default root states " + for_robots,
1338
+ LogType.STAT,
1339
+ throw_when_excep = True)
1340
+
1341
+ if env_indxs is None:
1342
+ self._root_p_default[robot_name][:, :] = self._root_p[robot_name]
1343
+ self._root_q_default[robot_name][:, :] = self._root_q[robot_name]
1344
+ else:
1345
+ self._root_p_default[robot_name][env_indxs, :] = self._root_p[robot_name][env_indxs, :]
1346
+ self._root_q_default[robot_name][env_indxs, :] = self._root_q[robot_name][env_indxs, :]
1347
+
1348
+ def _generate_rob_descriptions(self,
1349
+ robot_name: str,
1350
+ urdf_path: str,
1351
+ srdf_path: str):
1352
+
1353
+ custom_xacro_args=extract_custom_xacro_args(self._env_opts)
1354
+ Journal.log(self.__class__.__name__,
1355
+ "_generate_rob_descriptions",
1356
+ "generating URDF for robot "+ f"{robot_name}, from URDF {urdf_path}...",
1357
+ LogType.STAT,
1358
+ throw_when_excep = True)
1359
+ xrdf_cmds=self._xrdf_cmds(robot_name=robot_name)
1360
+ xrdf_cmds=merge_xacro_cmds(prev_cmds=xrdf_cmds,
1361
+ new_cmds=custom_xacro_args)
1362
+ self._urdf_dump_paths[robot_name]=generate_urdf(robot_name=robot_name,
1363
+ xacro_path=urdf_path,
1364
+ dump_path=self._descr_dump_path,
1365
+ xrdf_cmds=xrdf_cmds)
1366
+ Journal.log(self.__class__.__name__,
1367
+ "_generate_rob_descriptions",
1368
+ "generating SRDF for robot "+ f"{robot_name}, from SRDF {srdf_path}...",
1369
+ LogType.STAT,
1370
+ throw_when_excep = True)
1371
+ # we also generate SRDF files, which are useful for control
1372
+ self._srdf_dump_paths[robot_name]=generate_srdf(robot_name=robot_name,
1373
+ xacro_path=srdf_path,
1374
+ dump_path=self._descr_dump_path,
1375
+ xrdf_cmds=xrdf_cmds)
1376
+
1377
+ def _xrdf_cmds(self, robot_name:str):
1378
+ urdfpath=self._robot_urdf_paths[robot_name]
1379
+ # we assume directory tree of the robot package is like
1380
+ # robot-ros-pkg/robot_urdf/urdf/robot.urdf.xacro
1381
+ parts = urdfpath.split('/')
1382
+ urdf_descr_root_path = '/'.join(parts[:-2])
1383
+ cmds = get_xrdf_cmds(urdf_descr_root_path=urdf_descr_root_path)
1384
+ return cmds
1385
+
1386
+ @abstractmethod
1387
+ def current_tstep(self) -> int:
1388
+ pass
1389
+
1390
+ @abstractmethod
1391
+ def current_time(self) -> float:
1392
+ pass
1393
+
1394
+ @abstractmethod
1395
+ def is_running(self) -> bool:
1396
+ pass
1397
+
1398
+ @abstractmethod
1399
+ def _get_contact_f(self,
1400
+ robot_name: str,
1401
+ contact_link: str,
1402
+ env_indxs: torch.Tensor) -> torch.Tensor:
1403
+ return None
1404
+
1405
+ @abstractmethod
1406
+ def physics_dt(self) -> float:
1407
+ pass
1408
+
1409
+ @abstractmethod
1410
+ def rendering_dt(self) -> float:
1411
+ pass
1412
+
1413
+ @abstractmethod
1414
+ def set_physics_dt(self, physics_dt:float):
1415
+ pass
1416
+
1417
+ @abstractmethod
1418
+ def set_rendering_dt(self, rendering_dt:float):
1419
+ pass
1420
+
1421
+ @abstractmethod
1422
+ def _robot_jnt_names(self, robot_name: str) -> List[str]:
1423
+ pass
1424
+
1425
+ @abstractmethod
1426
+ def _read_root_state_from_robot(self,
1427
+ robot_name: str,
1428
+ env_indxs: torch.Tensor = None):
1429
+ pass
1430
+
1431
+ @abstractmethod
1432
+ def _read_jnts_state_from_robot(self,
1433
+ robot_name: str,
1434
+ env_indxs: torch.Tensor = None):
1435
+ pass
1436
+
1437
+ @abstractmethod
1438
+ def _init_robots_state(self):
1439
+ pass
1440
+
1441
+ @abstractmethod
1442
+ def _reset_state(self,
1443
+ robot_name: str,
1444
+ env_indxs: torch.Tensor = None,
1445
+ randomize: bool = False):
1446
+ pass
1447
+
1448
+ @abstractmethod
1449
+ def _init_world(self):
1450
+ pass
1451
+
1452
+ @abstractmethod
1453
+ def _reset_sim(self) -> None:
1454
+ pass
1455
+
1456
+ @abstractmethod
1457
+ def _set_jnts_to_homing(self, robot_name: str):
1458
+ pass
1459
+
1460
+ @abstractmethod
1461
+ def _set_root_to_defconfig(self, robot_name: str):
1462
+ pass
1463
+
1464
+ @abstractmethod
1465
+ def _parse_env_opts(self):
1466
+ pass
1467
+
1468
+ @abstractmethod
1469
+ def _pre_setup(self):
1470
+ pass
1471
+
1472
+ @abstractmethod
1473
+ def _generate_jnt_imp_control(self) -> JntImpCntrlChild:
1474
+ pass
1475
+
1476
+ @abstractmethod
1477
+ def _render_sim(self, mode:str="human") -> None:
1478
+ pass
1479
+
1480
+ @abstractmethod
1481
+ def _close(self) -> None:
1482
+ pass
1483
+
1484
+ @abstractmethod
1485
+ def _step_world(self) -> None:
1486
+ pass
bundles/centauro/d2026_01_19_h14_m54_s32-CentauroCloopPartialNoYawUbPercep_FakePosTrackEnvPhaseControl/xbot2_basic.yaml ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ XBotInterface:
2
+ urdf_path: $PWD/centauro.urdf
3
+ srdf_path: $PWD/centauro.srdf
4
+
5
+ ModelInterface:
6
+ model_type: RBDL
7
+ is_model_floating_base: true
8
+
9
+ motor_pd:
10
+ "j_arm*_1": [500, 10]
11
+ "j_arm*_2": [500, 10]
12
+ "j_arm*_3": [500, 10]
13
+ "j_arm*_4": [500, 10]
14
+ "j_arm*_5": [100, 5]
15
+ "j_arm*_6": [100, 5]
16
+ "j_arm*_7": [100, 5]
17
+ "hip_yaw_*": [3000, 30]
18
+ "hip_pitch_*": [3000, 30]
19
+ "knee_pitch_*": [3000, 30]
20
+ "ankle_pitch_*": [1000, 10]
21
+ "ankle_yaw_*": [300, 10]
22
+ "neck_pitch": [10, 1]
23
+ "neck_yaw": [10, 1]
24
+ "torso_yaw": [1000, 30]
25
+ "j_wheel_*": [0, 30]
26
+ "velodyne_*": [10, 1]
27
+ "d435_*": [10, 1]
28
+
29
+ startup_motor_pd:
30
+ "j_arm*_1": [500, 30]
31
+ "j_arm*_2": [500, 30]
32
+ "j_arm*_3": [500, 30]
33
+ "j_arm*_4": [500, 30]
34
+ "j_arm*_5": [100, 5]
35
+ "j_arm*_6": [100, 5]
36
+ "j_arm*_7": [100, 5]
37
+ "hip_yaw_*": [800, 80]
38
+ "hip_pitch_*": [800, 80]
39
+ "knee_pitch_*": [800, 80]
40
+ "ankle_pitch_*": [800, 80]
41
+ "ankle_yaw_*": [480, 50]
42
+ "neck_pitch": [10, 1]
43
+ "neck_yaw": [10, 1]
44
+ "torso_yaw": [800, 80]
45
+ "j_wheel_*": [0, 30]
46
+ "velodyne_*": [10, 1]
47
+ "d435_*": [10, 1]
48
+
49
+ motor_vel:
50
+ j_wheel_*: [1]
51
+ neck_velodyne: [1]
52
+
53
+ # hal
54
+ xbotcore_device_configs:
55
+ sim: $PWD/hal/centauro_gz.yaml
56
+ dummy: $PWD/hal/centauro_dummy.yaml
57
+
58
+ # threads
59
+ xbotcore_threads:
60
+ rt_main: {sched: fifo , prio: 60, period: 0.001}
61
+ nrt_main: {sched: other, prio: 0 , period: 0.005}
62
+
63
+ # plugins
64
+ xbotcore_plugins:
65
+
66
+ homing:
67
+ thread: rt_main
68
+ type: homing
69
+
70
+ ros_io: {thread: nrt_main, type: ros_io}
71
+
72
+ ros_control: {thread: nrt_main, type: ros_control, params: {autostart: {type: bool, value: true}}}
73
+
74
+ # global parameters
75
+ xbotcore_param:
76
+ /xbot/hal/joint_safety/filter_autostart: {value: true, type: bool}
77
+ # /xbot/hal/joint_safety/filter_cutoff_hz: {value: 1.0, type: double}
78
+ /xbot/hal/joint_safety/filter_safe_cutoff_hz: {value: 5.0, type: double}
79
+ /xbot/hal/joint_safety/filter_medium_cutoff_hz: {value: 10.0, type: double}
80
+ /xbot/hal/joint_safety/filter_fast_cutoff_hz: {value: 15.0, type: double}
81
+ /xbot/hal/enable_safety: {value: false, type: bool}
bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/bundle.yaml ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ bundle_format: augmpc_model_bundle_v1
2
+ bundle_name: d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv
3
+ checkpoint_file: d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv_model
4
+ preserved_training_cfgs:
5
+ - ibrido_run__2026_02_21_13_59_20_ID/training_cfg_centauro_ub_cloop.sh
6
+ framework:
7
+ repos:
8
+ AugMPC:
9
+ commit: f2ff28085ea76d2b548841de6f363f7183480f86
10
+ branch: ibrido
11
+ remote: git@github.com:AndrePatri/AugMPC.git
12
+ dirty: false
13
+ AugMPCEnvs:
14
+ commit: 46c5d4161cb4b249b3a2e50c93c6bc2aa087f027
15
+ branch: ibrido
16
+ remote: git@github.com:AndrePatri/AugMPCEnvs.git
17
+ dirty: false
18
+ AugMPCModels:
19
+ commit: 8b15c800c0f5684c61fdaf4847156ff71df61ebc
20
+ branch: main
21
+ remote: https://huggingface.co/AndrePatri/AugMPCModels
22
+ dirty: true
23
+ CentauroHybridMPC:
24
+ commit: 640889d4c3b9c8d360b5a3ccde6fc2bd8f139891
25
+ branch: ibrido
26
+ remote: git@github.com:ADVRHumanoids/CentauroHybridMPC.git
27
+ dirty: false
28
+ EigenIPC:
29
+ commit: 7c1c3ecd08716e61ed164a6e0bb788aa716705ca
30
+ branch: devel
31
+ remote: git@github.com:AndrePatri/EigenIPC.git
32
+ dirty: false
33
+ IBRIDO:
34
+ commit: 0ceb5f3e0508b6ecdce12bcc8f0dcbcde8f29a93
35
+ branch: main
36
+ remote: git@github.com:AndrePatri/IBRIDO.git
37
+ dirty: false
38
+ IsaacLab:
39
+ commit: a520a883ce996d855cc9d5255d71fd1c1307633f
40
+ branch: HEAD
41
+ remote: git@github.com:isaac-sim/IsaacLab.git
42
+ dirty: true
43
+ KyonRLStepping:
44
+ commit: 2bea2b8d70078974869df0549e90fc27ff31f851
45
+ branch: ibrido
46
+ remote: git@github.com:ADVRHumanoids/KyonRLStepping.git
47
+ dirty: false
48
+ MPCHive:
49
+ commit: 45b4fc692850cef607020dda2e32fd708e7fff62
50
+ branch: devel
51
+ remote: git@github.com:AndrePatri/MPCHive.git
52
+ dirty: false
53
+ MPCViz:
54
+ commit: 806d03efcc9d8ab1fb04991a159c19ba89bfb85b
55
+ branch: ros2_humble
56
+ remote: git@github.com:AndrePatri/MPCViz.git
57
+ dirty: false
58
+ adarl:
59
+ commit: f585499e49fa05fdd070a77f3211c0996599b87b
60
+ branch: ibrido
61
+ remote: git@github.com:c-rizz/adarl.git
62
+ dirty: false
63
+ casadi:
64
+ commit: 79cebe3b416dee22abc87de0076b80a5b97bd345
65
+ branch: optional_float
66
+ remote: git@github.com:AndrePatri/casadi.git
67
+ dirty: true
68
+ horizon:
69
+ commit: 3b084317709ff9b88d4a07ac5436f5988b39eece
70
+ branch: ibrido
71
+ remote: git@github.com:AndrePatri/horizon.git
72
+ dirty: true
73
+ iit-centauro-ros-pkg:
74
+ commit: 6069807ebb37a6d7df39430a02685e09ed9b167a
75
+ branch: ibrido_ros2
76
+ remote: git@github.com:AndrePatri/iit-centauro-ros-pkg.git
77
+ dirty: false
78
+ iit-dagana-ros-pkg:
79
+ commit: f7ecd6c84a0b7f3320c1b7de6449cbcd4445d2fe
80
+ branch: ibrido_ros2
81
+ remote: git@github.com:AndrePatri/iit-dagana-ros-pkg.git
82
+ dirty: false
83
+ iit-kyon-description:
84
+ commit: 50fd7c8909b3ddfd1ebbe67c61d6b775b86df6b1
85
+ branch: ibrido_ros2
86
+ remote: git@github.com:ADVRHumanoids/iit-kyon-ros-pkg.git
87
+ dirty: false
88
+ iit-kyon-description-mpc:
89
+ commit: 3a92bee28405172e8f6c628b4498703724d35bf5
90
+ branch: ibrido_ros2
91
+ remote: git@github.com:ADVRHumanoids/iit-kyon-ros-pkg.git
92
+ dirty: false
93
+ iit-kyon-ros-pkg:
94
+ commit: 298917efffb63dbca540e0aedbd21b41bf393fbf
95
+ branch: ibrido_ros2_simple
96
+ remote: git@github.com:ADVRHumanoids/iit-kyon-ros-pkg.git
97
+ dirty: false
98
+ phase_manager:
99
+ commit: 9925d18c0d7a55d013cbaa4998c61d85a3a8944f
100
+ branch: ibrido
101
+ remote: git@github.com:AndrePatri/phase_manager.git
102
+ dirty: false
103
+ unitree_ros:
104
+ commit: c75a622b8782d11dd6aa4c2ebd3b0f9c13a56aae
105
+ branch: ibrido
106
+ remote: git@github.com:AndrePatri/unitree_ros.git
107
+ dirty: true
bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/centauro_big_wheels_ub_2026_02_21_13_59_20_ID.srdf ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" ?>
2
+ <!-- =================================================================================== -->
3
+ <!-- | This document was autogenerated by xacro from /root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_srdf/srdf/centauro.srdf.xacro | -->
4
+ <!-- | EDITING THIS FILE BY HAND IS NOT RECOMMENDED | -->
5
+ <!-- =================================================================================== -->
6
+ <!--This does not replace URDF, and is not an extension of URDF.
7
+ This is a format for representing semantic information about the robot structure.
8
+ A URDF file must exist for this robot as well, where the joints and the links that are referenced are defined
9
+ -->
10
+ <robot name="centauro">
11
+ <!-- none, ball, dagana_fixed, dagana -->
12
+ <group name="base">
13
+ <link name="pelvis"/>
14
+ </group>
15
+ <group name="imu_sensors">
16
+ <link name="imu_link"/>
17
+ </group>
18
+ <group name="front_left_leg">
19
+ <chain base_link="pelvis" tip_link="wheel_1"/>
20
+ </group>
21
+ <group name="front_right_leg">
22
+ <chain base_link="pelvis" tip_link="wheel_2"/>
23
+ </group>
24
+ <group name="rear_right_leg">
25
+ <chain base_link="pelvis" tip_link="wheel_4"/>
26
+ </group>
27
+ <group name="rear_left_leg">
28
+ <chain base_link="pelvis" tip_link="wheel_3"/>
29
+ </group>
30
+ <group name="left_arm">
31
+ <chain base_link="torso_2" tip_link="arm1_8"/>
32
+ </group>
33
+ <group name="right_arm">
34
+ <chain base_link="torso_2" tip_link="arm2_8"/>
35
+ </group>
36
+ <group name="torso">
37
+ <chain base_link="pelvis" tip_link="torso_2"/>
38
+ </group>
39
+ <group name="velodyne">
40
+ <chain base_link="torso_2" tip_link="velodyne_motor"/>
41
+ </group>
42
+ <group name="d435_head">
43
+ <chain base_link="torso_2" tip_link="d435_head_motor"/>
44
+ </group>
45
+ <group name="chains">
46
+ <group name="front_left_leg"/>
47
+ <group name="front_right_leg"/>
48
+ <group name="rear_right_leg"/>
49
+ <group name="rear_left_leg"/>
50
+ <group name="left_arm"/>
51
+ <group name="right_arm"/>
52
+ <group name="torso"/>
53
+ <group name="velodyne"/>
54
+ <group name="d435_head"/>
55
+ </group>
56
+ <!-- Homing posture -->
57
+ <!-- <group_state name="home" group="chains">
58
+ <joint name="hip_yaw_1" value="-0.746874"/>
59
+ <joint name="hip_pitch_1" value="-1.25409"/>
60
+ <joint name="knee_pitch_1" value="-1.55576"/>
61
+ <joint name="ankle_pitch_1" value="-0.301666"/>
62
+ <joint name="ankle_yaw_1" value="0.746874"/>
63
+ <joint name="hip_yaw_2" value="0.746874"/>
64
+ <joint name="hip_pitch_2" value="1.25409"/>
65
+ <joint name="knee_pitch_2" value="1.55576"/>
66
+ <joint name="ankle_pitch_2" value="0.301666"/>
67
+ <joint name="ankle_yaw_2" value="-0.746874"/>
68
+ <joint name="hip_yaw_3" value="0.746874"/>
69
+ <joint name="hip_pitch_3" value="1.25409"/>
70
+ <joint name="knee_pitch_3" value="1.55576"/>
71
+ <joint name="ankle_pitch_3" value="0.301667"/>
72
+ <joint name="ankle_yaw_3" value="-0.746874"/>
73
+ <joint name="hip_yaw_4" value="-0.746874"/>
74
+ <joint name="hip_pitch_4" value="-1.25409"/>
75
+ <joint name="knee_pitch_4" value="-1.55576"/>
76
+ <joint name="ankle_pitch_4" value="-0.301667"/>
77
+ <joint name="ankle_yaw_4" value="0.746874"/>
78
+ <joint name="torso_yaw" value="3.56617e-13"/>
79
+ <joint name="velodyne_joint" value="0"/>
80
+ <joint name="d435_head_joint" value="0"/>
81
+ <joint name="j_arm1_1" value="0.520149"/>
82
+ <joint name="j_arm1_2" value="0.320865"/>
83
+ <joint name="j_arm1_3" value="0.274669"/>
84
+ <joint name="j_arm1_4" value="-2.23604"/>
85
+ <joint name="j_arm1_5" value="0.0500815"/>
86
+ <joint name="j_arm1_6" value="-0.781461"/>
87
+ <joint name="j_arm2_1" value="0.520149"/>
88
+ <joint name="j_arm2_2" value="-0.320865"/>
89
+ <joint name="j_arm2_3" value="-0.274669"/>
90
+ <joint name="j_arm2_4" value="-2.23604"/>
91
+ <joint name="j_arm2_5" value="-0.0500815"/>
92
+ <joint name="j_arm2_6" value="-0.781461"/>
93
+ <joint name="j_wheel_1" value="0.0"/>
94
+ <joint name="j_wheel_2" value="0.0"/>
95
+ <joint name="j_wheel_3" value="0.0"/>
96
+ <joint name="j_wheel_4" value="0.0"/>
97
+ <xacro:if value="${END_EFFECTOR_LEFT == 'dagana'}">
98
+ <joint name="dagana_1_claw_joint" value="0"/>
99
+ </xacro:if>
100
+ <xacro:if value="${END_EFFECTOR_RIGHT == 'dagana'}">
101
+ <joint name="dagana_2_claw_joint" value="0"/>
102
+ </xacro:if>
103
+
104
+ </group_state> -->
105
+ <!-- Balanced weight homing -->
106
+ <group_state group="chains" name="home">
107
+ <joint name="hip_yaw_1" value="-0.5"/>
108
+ <joint name="hip_pitch_1" value="-1.37"/>
109
+ <joint name="knee_pitch_1" value="-1.34"/>
110
+ <joint name="ankle_pitch_1" value="-0.03"/>
111
+ <joint name="ankle_yaw_1" value="0.6"/>
112
+ <joint name="hip_yaw_2" value="0.5"/>
113
+ <joint name="hip_pitch_2" value="1.37"/>
114
+ <joint name="knee_pitch_2" value="1.34"/>
115
+ <joint name="ankle_pitch_2" value="-0.03"/>
116
+ <joint name="ankle_yaw_2" value="-0.6"/>
117
+ <joint name="hip_yaw_3" value="1.28"/>
118
+ <joint name="hip_pitch_3" value="1.29"/>
119
+ <joint name="knee_pitch_3" value="1.71"/>
120
+ <joint name="ankle_pitch_3" value="0.43"/>
121
+ <joint name="ankle_yaw_3" value="-1.3"/>
122
+ <joint name="hip_yaw_4" value="-1.28"/>
123
+ <joint name="hip_pitch_4" value="-1.29"/>
124
+ <joint name="knee_pitch_4" value="-1.71"/>
125
+ <joint name="ankle_pitch_4" value="-0.43"/>
126
+ <joint name="ankle_yaw_4" value="1.3"/>
127
+ <joint name="torso_yaw" value="3.56617e-13"/>
128
+ <joint name="velodyne_joint" value="0"/>
129
+ <joint name="d435_head_joint" value="0"/>
130
+ <joint name="j_arm1_1" value="0.520149"/>
131
+ <joint name="j_arm1_2" value="0.320865"/>
132
+ <joint name="j_arm1_3" value="0.274669"/>
133
+ <joint name="j_arm1_4" value="-2.23604"/>
134
+ <joint name="j_arm1_5" value="0.0500815"/>
135
+ <joint name="j_arm1_6" value="-0.781461"/>
136
+ <joint name="j_arm2_1" value="0.520149"/>
137
+ <joint name="j_arm2_2" value="-0.320865"/>
138
+ <joint name="j_arm2_3" value="-0.274669"/>
139
+ <joint name="j_arm2_4" value="-2.23604"/>
140
+ <joint name="j_arm2_5" value="-0.0500815"/>
141
+ <joint name="j_arm2_6" value="-0.781461"/>
142
+ <joint name="j_wheel_1" value="0.0"/>
143
+ <joint name="j_wheel_2" value="0.0"/>
144
+ <joint name="j_wheel_3" value="0.0"/>
145
+ <joint name="j_wheel_4" value="0.0"/>
146
+ </group_state>
147
+ </robot>
bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/centauro_big_wheels_ub_2026_02_21_13_59_20_ID.urdf ADDED
@@ -0,0 +1,1569 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" ?>
2
+ <!-- =================================================================================== -->
3
+ <!-- | This document was autogenerated by xacro from /root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/urdf/centauro.urdf.xacro | -->
4
+ <!-- | EDITING THIS FILE BY HAND IS NOT RECOMMENDED | -->
5
+ <!-- =================================================================================== -->
6
+ <robot name="centauro">
7
+ <!-- none, ball, dagana_fixed, dagana -->
8
+ <!-- scaling facto for available peack torque link side, from 0 to 1 -->
9
+ <material name="black">
10
+ <color rgba="0.4 0.4 0.4 1"/>
11
+ </material>
12
+ <material name="dark">
13
+ <color rgba="0.1 0.1 0.1 1"/>
14
+ </material>
15
+ <material name="dark_grey">
16
+ <color rgba="0.2 0.2 0.2 1"/>
17
+ </material>
18
+ <!-- Note that the "23" suffix denotes the new bigger-size wheel -->
19
+ <!-- ??? -->
20
+ <!-- ??? -->
21
+ <!-- Origin -->
22
+ <!-- <xacro:property name="hip_yaw_lower" value="${[-2.827,-2.583, -2.583 ,-2.827]}"/>
23
+ <xacro:property name="hip_yaw_upper" value="${[ 2.583, 2.827, 2.827 , 2.583]}"/> -->
24
+ <link name="base_link"/>
25
+ <joint name="base_joint" type="fixed">
26
+ <parent link="base_link"/>
27
+ <child link="pelvis"/>
28
+ <origin rpy="0 0 0" xyz="0 0 0"/>
29
+ </joint>
30
+ <link name="pelvis">
31
+ <inertial>
32
+ <origin xyz="-0.0016320207 0.0026534004000000003 0.079167428"/>
33
+ <mass value="26.584529"/>
34
+ <inertia ixx="0.39195477" ixy="0.024781258999999996" ixz="0.017108606999999998" iyy="1.0000708999999999" iyz="0.0052627106000000005" izz="1.0679988999999999"/>
35
+ </inertial>
36
+ <visual>
37
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
38
+ <geometry>
39
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/pelvis.stl" scale="0.001 0.001 0.001"/>
40
+ </geometry>
41
+ </visual>
42
+ <collision>
43
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
44
+ <geometry>
45
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/pelvis_reduced.stl" scale="0.001 0.001 0.001"/>
46
+ </geometry>
47
+ </collision>
48
+ </link>
49
+ <link name="imu_link"/>
50
+ <joint name="imu_joint" type="fixed">
51
+ <parent link="pelvis"/>
52
+ <origin rpy="0.0 0.0 0.0" xyz="0.248316 0.0 0.015"/>
53
+ <child link="imu_link"/>
54
+ </joint>
55
+ <!-- torso links -->
56
+ <link name="torso_2">
57
+ <inertial>
58
+ <origin xyz="-0.019497794 0.0045317835 0.13768283"/>
59
+ <mass value="12.553731"/>
60
+ <inertia ixx="0.063643392" ixy="8.939e-05" ixz="-0.00086873" iyy="0.02680235" iyz="-4.657e-05" izz="0.04743015"/>
61
+ </inertial>
62
+ <visual>
63
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
64
+ <geometry>
65
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/link1_no_head.stl" scale="0.001 0.001 0.001"/>
66
+ </geometry>
67
+ </visual>
68
+ <collision>
69
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
70
+ <geometry>
71
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/link1_no_head.stl" scale="0.001 0.001 0.001"/>
72
+ </geometry>
73
+ </collision>
74
+ </link>
75
+ <!-- /torso links -->
76
+ <!-- torso joints -->
77
+ <joint name="torso_yaw" type="revolute">
78
+ <parent link="pelvis"/>
79
+ <origin xyz="0.2 0.0 0.256"/>
80
+ <child link="torso_2"/>
81
+ <axis xyz="0 0 1"/>
82
+ <limit effort="147" lower="-2.618" upper="2.618" velocity="5.7"/>
83
+ <!-- TODO -->
84
+ </joint>
85
+ <!-- /torso joints -->
86
+ <!-- /macro arms -->
87
+ <!-- LINKS -->
88
+ <!-- shoulder yaw-roll-->
89
+ <link name="arm1_1">
90
+ <inertial>
91
+ <origin xyz="-0.0074457212 0.03410796 0.00010978102"/>
92
+ <mass value="1.9628675"/>
93
+ <inertia ixx="0.0053547717" ixy="0.00036428926" ixz="1.5089568e-05" iyy="0.0033923328" iyz="5.5692312e-05" izz="0.0068921413"/>
94
+ </inertial>
95
+ <visual>
96
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
97
+ <geometry>
98
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/ShoulderPitch.STL" scale="0.001 -0.001 0.001"/>
99
+ </geometry>
100
+ <material name="dark_grey"/>
101
+ </visual>
102
+ <collision>
103
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
104
+ <geometry>
105
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/ShoulderPitch.STL" scale="0.001 -0.001 0.001"/>
106
+ </geometry>
107
+ </collision>
108
+ </link>
109
+ <!-- shoulder yaw-roll-->
110
+ <!-- shoulder roll-pitch-->
111
+ <link name="arm1_2">
112
+ <inertial>
113
+ <origin xyz="0.058142302 5.7450803e-05 -0.077477683"/>
114
+ <mass value="1.8595811"/>
115
+ <inertia ixx="0.013776643" ixy="3.7788675e-05" ixz="0.0037690171" iyy="0.015677464" iyz="-9.4893549e-06" izz="0.0046317657"/>
116
+ </inertial>
117
+ <visual>
118
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
119
+ <geometry>
120
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/ShoulderRoll.STL" scale="0.001 -0.001 0.001"/>
121
+ </geometry>
122
+ </visual>
123
+ <collision>
124
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
125
+ <geometry>
126
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/ShoulderRoll.STL" scale="0.001 -0.001 0.001"/>
127
+ </geometry>
128
+ </collision>
129
+ </link>
130
+ <!-- shoulder roll-pitch-->
131
+ <!-- shoulder-elbow -->
132
+ <link name="arm1_3">
133
+ <inertial>
134
+ <origin xyz="0.014625194 0.0008172672 -0.028333545"/>
135
+ <mass value="1.6678109"/>
136
+ <inertia ixx="0.0064480435" ixy="-0.00015639093" ixz="0.0012205359" iyy="0.0073372077" iyz="8.9941532e-05" izz="0.0036738448"/>
137
+ </inertial>
138
+ <visual>
139
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
140
+ <geometry>
141
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/ShoulderYaw.STL" scale="0.001 -0.001 0.001"/>
142
+ </geometry>
143
+ <material name="dark_grey"/>
144
+ </visual>
145
+ <collision>
146
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
147
+ <geometry>
148
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/ShoulderYaw.STL" scale="0.001 -0.001 0.001"/>
149
+ </geometry>
150
+ </collision>
151
+ </link>
152
+ <!-- /shoulder-elbow -->
153
+ <!-- elbow yaw-pitch -->
154
+ <link name="arm1_4">
155
+ <inertial>
156
+ <origin xyz="-0.0076833067 -0.040302205 -0.043492779"/>
157
+ <mass value="1.3157289"/>
158
+ <inertia ixx="0.004330394" ixy="-0.00011737391" ixz="-0.00041923199" iyy="0.0038539919" iyz="-0.00079573038" izz="0.0017594689"/>
159
+ </inertial>
160
+ <visual>
161
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
162
+ <geometry>
163
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/Elbow.STL" scale="0.001 -0.001 0.001"/>
164
+ </geometry>
165
+ </visual>
166
+ <collision>
167
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
168
+ <geometry>
169
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/Elbow.STL" scale="0.001 -0.001 0.001"/>
170
+ </geometry>
171
+ </collision>
172
+ </link>
173
+ <!-- /elbow yaw-pitch -->
174
+ <!-- elbow-wrist -->
175
+ <link name="arm1_5">
176
+ <inertial>
177
+ <origin xyz="-0.00011079615 0.011590836 -0.07816026"/>
178
+ <mass value="1.4908547"/>
179
+ <inertia ixx="0.0085692128" ixy="1.7856252e-05" ixz="1.9379365e-05" iyy="0.0077454159" iyz="-0.00032860094" izz="0.0027441921"/>
180
+ </inertial>
181
+ <visual>
182
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
183
+ <geometry>
184
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/Forearm.STL" scale="0.001 -0.001 0.001"/>
185
+ </geometry>
186
+ <material name="dark_grey"/>
187
+ </visual>
188
+ <collision>
189
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
190
+ <geometry>
191
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/Forearm.STL" scale="0.001 -0.001 0.001"/>
192
+ </geometry>
193
+ </collision>
194
+ </link>
195
+ <!-- /elbow-wrist -->
196
+ <!-- wrist yaw-pitch -->
197
+ <link name="arm1_6">
198
+ <inertial>
199
+ <origin xyz="-4.6502396e-06 -0.038014094 -0.069926878"/>
200
+ <mass value="1.1263612"/>
201
+ <inertia ixx="0.0051871784" ixy="2.724437e-05" ixz="2.2833496e-06" iyy="0.0048037789" iyz="-0.00072165653" izz="0.0012771388"/>
202
+ </inertial>
203
+ <visual>
204
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
205
+ <geometry>
206
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/wrist_pitch_right.stl" scale="0.001 -0.001 0.001"/>
207
+ </geometry>
208
+ </visual>
209
+ <collision>
210
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
211
+ <geometry>
212
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/wrist_pitch_right.stl" scale="0.001 -0.001 0.001"/>
213
+ </geometry>
214
+ </collision>
215
+ </link>
216
+ <!-- /wrist yaw-pitch -->
217
+ <!-- <link name="arm${arm_num}_7">
218
+ <inertial>
219
+ <origin xyz="${Wrist_3_x} ${rot*Wrist_3_y} ${Wrist_3_z}"/>
220
+ <mass value="${Wrist_3_mass}"/>
221
+ <inertia ixx="${Wrist_3_xx}" ixy="${rot*Wrist_3_xy}" ixz="${Wrist_3_xz}" iyy="${Wrist_3_yy}" iyz="${rot*Wrist_3_yz}" izz="${Wrist_3_zz}"/>
222
+ </inertial>
223
+
224
+ <visual>
225
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
226
+ <geometry>
227
+ <mesh filename="${MESH_PATH}/ForearmYaw.STL" scale="0.001 ${-rot*0.001} 0.001" />
228
+ </geometry>
229
+ <material name="dark_grey"/>
230
+ </visual>
231
+
232
+ <collision>
233
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
234
+ <geometry>
235
+ <mesh filename="${MESH_PATH}/simple/ForearmYaw.STL" scale="0.001 ${-rot*0.001} 0.001" />
236
+ </geometry>
237
+ </collision>
238
+ </link> -->
239
+ <!-- /LINKS -->
240
+ <!-- JOINTS -->
241
+ <!-- shoulder yaw -->
242
+ <joint name="j_arm1_1" type="revolute">
243
+ <parent link="torso_2"/>
244
+ <child link="arm1_1"/>
245
+ <origin rpy="0.174533 0.0 -0.5235988" xyz="0.0457475 0.169137 0.116626"/>
246
+ <axis xyz="0 1 0"/>
247
+ <limit effort="147.0" lower="-3.312" upper="1.615" velocity="3.86"/>
248
+ </joint>
249
+ <!-- /shoulder yaw -->
250
+ <!-- shoulder roll -->
251
+ <joint name="j_arm1_2" type="revolute">
252
+ <parent link="arm1_1"/>
253
+ <child link="arm1_2"/>
254
+ <origin rpy="-0.1745327 0.0 0.0" xyz="-0.09015 0.062 0.0"/>
255
+ <axis xyz="1 0 0"/>
256
+ <limit effort="147.0" lower="0.02" upper="3.431" velocity="3.86"/>
257
+ </joint>
258
+ <!-- /shoulder roll -->
259
+ <!-- shoulder pitch-->
260
+ <joint name="j_arm1_3" type="revolute">
261
+ <parent link="arm1_2"/>
262
+ <child link="arm1_3"/>
263
+ <origin xyz="0.09015 0.0 -0.21815"/>
264
+ <axis xyz="0 0 1"/>
265
+ <limit effort="147.0" lower="-2.552" upper="2.566" velocity="6.06"/>
266
+ </joint>
267
+ <!-- /shoulder pitch -->
268
+ <!-- elbow yaw -->
269
+ <joint name="j_arm1_4" type="revolute">
270
+ <parent link="arm1_3"/>
271
+ <child link="arm1_4"/>
272
+ <origin xyz="0.045 0.05515 -0.074"/>
273
+ <axis xyz="0 1 0"/>
274
+ <limit effort="147.0" lower="-2.465" upper="0.28" velocity="6.06"/>
275
+ </joint>
276
+ <!-- /elbow yaw -->
277
+ <!-- elbow pitch-->
278
+ <joint name="j_arm1_5" type="revolute">
279
+ <parent link="arm1_4"/>
280
+ <child link="arm1_5"/>
281
+ <origin xyz="-0.015 -0.05515 -0.095"/>
282
+ <axis xyz="0 0 1"/>
283
+ <limit effort="55.0" lower="-2.569" upper="2.562" velocity="11.72"/>
284
+ </joint>
285
+ <!-- /elbow pitch-->
286
+ <!-- wrist yaw-->
287
+ <joint name="j_arm1_6" type="revolute">
288
+ <parent link="arm1_5"/>
289
+ <child link="arm1_6"/>
290
+ <origin xyz="0.0 0.049 -0.156"/>
291
+ <axis xyz="0 1 0"/>
292
+ <limit effort="55.0" lower="-1.529" upper="1.509" velocity="11.72"/>
293
+ </joint>
294
+ <!-- /wrist yaw-->
295
+ <!-- wrist pitch-->
296
+ <!-- <joint name="j_arm${arm_num}_7" type="fixed">
297
+ <parent link="arm${arm_num}_6"/>
298
+ <child link="arm${arm_num}_7"/>
299
+ <origin xyz="${Wrist_3_Ox} ${rot*Wrist_3_Oy} ${Wrist_3_Oz}"/>
300
+ <axis xyz="0 0 1"/>
301
+ <limit lower="${j_arm_7_lower[arm_num-1]}" upper="${j_arm_7_upper[arm_num-1]}" effort="${j_arm_7_torque[arm_num-1]}" velocity="${j_arm_7_velocity[arm_num-1]}"/>
302
+ </joint> -->
303
+ <!-- wrist pitch-->
304
+ <!-- force-troque sensor -->
305
+ <!-- end-effector -->
306
+ <!-- wrist pitch-->
307
+ <!-- <joint name="j_ft_${arm_num}" type="fixed">
308
+ <parent link="arm${arm_num}_7"/>
309
+ <child link="ft_arm${arm_num}"/>
310
+ <origin xyz="${Ft_arm_Ox} ${Ft_arm_Oy} ${Ft_arm_Oz}" rpy="${Ft_arm_roll} ${Ft_arm_pitch} ${Ft_arm_yaw}" />
311
+ <axis xyz="0 0 1"/>
312
+ <limit lower="0.0" upper="0.0" effort="0" velocity="0"/>
313
+ </joint> -->
314
+ <!-- wrist pitch-->
315
+ <!-- <link name="ft_arm${arm_num}">
316
+ <inertial>
317
+ <origin xyz="${Ft_arm_x} ${rot*Ft_arm_y} ${Ft_arm_z}"/>
318
+ <mass value="${Ft_arm_mass}"/>
319
+ <inertia ixx="${Ft_arm_xx}" ixy="${rot*Ft_arm_xy}" ixz="${Ft_arm_xz}" iyy="${Ft_arm_yy}" iyz="${rot*Ft_arm_yz}" izz="${Ft_arm_zz}"/>
320
+ </inertial> -->
321
+ <!--visual>
322
+ <origin xyz="0.0 0.0 0.0" rpy="${PI} 0.0 0.0"/>
323
+ <geometry>
324
+ <mesh filename="${MESH_PATH}/Wrist.STL" scale="0.001 ${-rot*0.001} 0.001" />
325
+ </geometry>
326
+ </visual-->
327
+ <!--collision>
328
+ <origin xyz="0.0 0.0 0.0" rpy="${PI} 0.0 0.0"/>
329
+ <geometry>
330
+ <mesh filename="${MESH_PATH}/simple/Wrist.STL" scale="0.001 ${-rot*0.001} 0.001" />
331
+ </geometry>
332
+ </collision-->
333
+ <!-- </link> -->
334
+ <!-- LINKS -->
335
+ <!-- shoulder yaw-roll-->
336
+ <link name="arm2_1">
337
+ <inertial>
338
+ <origin xyz="-0.0074457212 -0.03410796 0.00010978102"/>
339
+ <mass value="1.9628675"/>
340
+ <inertia ixx="0.0053547717" ixy="-0.00036428926" ixz="1.5089568e-05" iyy="0.0033923328" iyz="-5.5692312e-05" izz="0.0068921413"/>
341
+ </inertial>
342
+ <visual>
343
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
344
+ <geometry>
345
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/ShoulderPitch.STL" scale="0.001 0.001 0.001"/>
346
+ </geometry>
347
+ <material name="dark_grey"/>
348
+ </visual>
349
+ <collision>
350
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
351
+ <geometry>
352
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/ShoulderPitch.STL" scale="0.001 0.001 0.001"/>
353
+ </geometry>
354
+ </collision>
355
+ </link>
356
+ <!-- shoulder yaw-roll-->
357
+ <!-- shoulder roll-pitch-->
358
+ <link name="arm2_2">
359
+ <inertial>
360
+ <origin xyz="0.058142302 -5.7450803e-05 -0.077477683"/>
361
+ <mass value="1.8595811"/>
362
+ <inertia ixx="0.013776643" ixy="-3.7788675e-05" ixz="0.0037690171" iyy="0.015677464" iyz="9.4893549e-06" izz="0.0046317657"/>
363
+ </inertial>
364
+ <visual>
365
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
366
+ <geometry>
367
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/ShoulderRoll.STL" scale="0.001 0.001 0.001"/>
368
+ </geometry>
369
+ </visual>
370
+ <collision>
371
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
372
+ <geometry>
373
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/ShoulderRoll.STL" scale="0.001 0.001 0.001"/>
374
+ </geometry>
375
+ </collision>
376
+ </link>
377
+ <!-- shoulder roll-pitch-->
378
+ <!-- shoulder-elbow -->
379
+ <link name="arm2_3">
380
+ <inertial>
381
+ <origin xyz="0.014625194 -0.0008172672 -0.028333545"/>
382
+ <mass value="1.6678109"/>
383
+ <inertia ixx="0.0064480435" ixy="0.00015639093" ixz="0.0012205359" iyy="0.0073372077" iyz="-8.9941532e-05" izz="0.0036738448"/>
384
+ </inertial>
385
+ <visual>
386
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
387
+ <geometry>
388
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/ShoulderYaw.STL" scale="0.001 0.001 0.001"/>
389
+ </geometry>
390
+ <material name="dark_grey"/>
391
+ </visual>
392
+ <collision>
393
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
394
+ <geometry>
395
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/ShoulderYaw.STL" scale="0.001 0.001 0.001"/>
396
+ </geometry>
397
+ </collision>
398
+ </link>
399
+ <!-- /shoulder-elbow -->
400
+ <!-- elbow yaw-pitch -->
401
+ <link name="arm2_4">
402
+ <inertial>
403
+ <origin xyz="-0.0076833067 0.040302205 -0.043492779"/>
404
+ <mass value="1.3157289"/>
405
+ <inertia ixx="0.004330394" ixy="0.00011737391" ixz="-0.00041923199" iyy="0.0038539919" iyz="0.00079573038" izz="0.0017594689"/>
406
+ </inertial>
407
+ <visual>
408
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
409
+ <geometry>
410
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/Elbow.STL" scale="0.001 0.001 0.001"/>
411
+ </geometry>
412
+ </visual>
413
+ <collision>
414
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
415
+ <geometry>
416
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/Elbow.STL" scale="0.001 0.001 0.001"/>
417
+ </geometry>
418
+ </collision>
419
+ </link>
420
+ <!-- /elbow yaw-pitch -->
421
+ <!-- elbow-wrist -->
422
+ <link name="arm2_5">
423
+ <inertial>
424
+ <origin xyz="-0.00011079615 -0.011590836 -0.07816026"/>
425
+ <mass value="1.4908547"/>
426
+ <inertia ixx="0.0085692128" ixy="-1.7856252e-05" ixz="1.9379365e-05" iyy="0.0077454159" iyz="0.00032860094" izz="0.0027441921"/>
427
+ </inertial>
428
+ <visual>
429
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
430
+ <geometry>
431
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/Forearm.STL" scale="0.001 0.001 0.001"/>
432
+ </geometry>
433
+ <material name="dark_grey"/>
434
+ </visual>
435
+ <collision>
436
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
437
+ <geometry>
438
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/Forearm.STL" scale="0.001 0.001 0.001"/>
439
+ </geometry>
440
+ </collision>
441
+ </link>
442
+ <!-- /elbow-wrist -->
443
+ <!-- wrist yaw-pitch -->
444
+ <link name="arm2_6">
445
+ <inertial>
446
+ <origin xyz="-4.6502396e-06 0.038014094 -0.069926878"/>
447
+ <mass value="1.1263612"/>
448
+ <inertia ixx="0.0051871784" ixy="-2.724437e-05" ixz="2.2833496e-06" iyy="0.0048037789" iyz="0.00072165653" izz="0.0012771388"/>
449
+ </inertial>
450
+ <visual>
451
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
452
+ <geometry>
453
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/wrist_pitch_right.stl" scale="0.001 0.001 0.001"/>
454
+ </geometry>
455
+ </visual>
456
+ <collision>
457
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
458
+ <geometry>
459
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/wrist_pitch_right.stl" scale="0.001 0.001 0.001"/>
460
+ </geometry>
461
+ </collision>
462
+ </link>
463
+ <!-- /wrist yaw-pitch -->
464
+ <!-- <link name="arm${arm_num}_7">
465
+ <inertial>
466
+ <origin xyz="${Wrist_3_x} ${rot*Wrist_3_y} ${Wrist_3_z}"/>
467
+ <mass value="${Wrist_3_mass}"/>
468
+ <inertia ixx="${Wrist_3_xx}" ixy="${rot*Wrist_3_xy}" ixz="${Wrist_3_xz}" iyy="${Wrist_3_yy}" iyz="${rot*Wrist_3_yz}" izz="${Wrist_3_zz}"/>
469
+ </inertial>
470
+
471
+ <visual>
472
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
473
+ <geometry>
474
+ <mesh filename="${MESH_PATH}/ForearmYaw.STL" scale="0.001 ${-rot*0.001} 0.001" />
475
+ </geometry>
476
+ <material name="dark_grey"/>
477
+ </visual>
478
+
479
+ <collision>
480
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
481
+ <geometry>
482
+ <mesh filename="${MESH_PATH}/simple/ForearmYaw.STL" scale="0.001 ${-rot*0.001} 0.001" />
483
+ </geometry>
484
+ </collision>
485
+ </link> -->
486
+ <!-- /LINKS -->
487
+ <!-- JOINTS -->
488
+ <!-- shoulder yaw -->
489
+ <joint name="j_arm2_1" type="revolute">
490
+ <parent link="torso_2"/>
491
+ <child link="arm2_1"/>
492
+ <origin rpy="-0.174533 0.0 0.5235988" xyz="0.0457475 -0.169137 0.116626"/>
493
+ <axis xyz="0 1 0"/>
494
+ <limit effort="147.0" lower="-3.3458" upper="1.6012" velocity="3.86"/>
495
+ </joint>
496
+ <!-- /shoulder yaw -->
497
+ <!-- shoulder roll -->
498
+ <joint name="j_arm2_2" type="revolute">
499
+ <parent link="arm2_1"/>
500
+ <child link="arm2_2"/>
501
+ <origin rpy="0.1745327 0.0 0.0" xyz="-0.09015 -0.062 0.0"/>
502
+ <axis xyz="1 0 0"/>
503
+ <limit effort="147.0" lower="-3.4258" upper="-0.0138" velocity="3.86"/>
504
+ </joint>
505
+ <!-- /shoulder roll -->
506
+ <!-- shoulder pitch-->
507
+ <joint name="j_arm2_3" type="revolute">
508
+ <parent link="arm2_2"/>
509
+ <child link="arm2_3"/>
510
+ <origin xyz="0.09015 0.0 -0.21815"/>
511
+ <axis xyz="0 0 1"/>
512
+ <limit effort="147.0" lower="-2.5614" upper="2.5606" velocity="6.06"/>
513
+ </joint>
514
+ <!-- /shoulder pitch -->
515
+ <!-- elbow yaw -->
516
+ <joint name="j_arm2_4" type="revolute">
517
+ <parent link="arm2_3"/>
518
+ <child link="arm2_4"/>
519
+ <origin xyz="0.045 -0.05515 -0.074"/>
520
+ <axis xyz="0 1 0"/>
521
+ <limit effort="147.0" lower="-2.4794" upper="0.2886" velocity="6.06"/>
522
+ </joint>
523
+ <!-- /elbow yaw -->
524
+ <!-- elbow pitch-->
525
+ <joint name="j_arm2_5" type="revolute">
526
+ <parent link="arm2_4"/>
527
+ <child link="arm2_5"/>
528
+ <origin xyz="-0.015 0.05515 -0.095"/>
529
+ <axis xyz="0 0 1"/>
530
+ <limit effort="55.0" lower="-2.5394" upper="2.5546" velocity="11.72"/>
531
+ </joint>
532
+ <!-- /elbow pitch-->
533
+ <!-- wrist yaw-->
534
+ <joint name="j_arm2_6" type="revolute">
535
+ <parent link="arm2_5"/>
536
+ <child link="arm2_6"/>
537
+ <origin xyz="0.0 -0.049 -0.156"/>
538
+ <axis xyz="0 1 0"/>
539
+ <limit effort="55.0" lower="-1.5154" upper="1.5156" velocity="11.72"/>
540
+ </joint>
541
+ <!-- /wrist yaw-->
542
+ <!-- wrist pitch-->
543
+ <!-- <joint name="j_arm${arm_num}_7" type="fixed">
544
+ <parent link="arm${arm_num}_6"/>
545
+ <child link="arm${arm_num}_7"/>
546
+ <origin xyz="${Wrist_3_Ox} ${rot*Wrist_3_Oy} ${Wrist_3_Oz}"/>
547
+ <axis xyz="0 0 1"/>
548
+ <limit lower="${j_arm_7_lower[arm_num-1]}" upper="${j_arm_7_upper[arm_num-1]}" effort="${j_arm_7_torque[arm_num-1]}" velocity="${j_arm_7_velocity[arm_num-1]}"/>
549
+ </joint> -->
550
+ <!-- wrist pitch-->
551
+ <!-- force-troque sensor -->
552
+ <!-- end-effector -->
553
+ <!-- wrist pitch-->
554
+ <!-- <joint name="j_ft_${arm_num}" type="fixed">
555
+ <parent link="arm${arm_num}_7"/>
556
+ <child link="ft_arm${arm_num}"/>
557
+ <origin xyz="${Ft_arm_Ox} ${Ft_arm_Oy} ${Ft_arm_Oz}" rpy="${Ft_arm_roll} ${Ft_arm_pitch} ${Ft_arm_yaw}" />
558
+ <axis xyz="0 0 1"/>
559
+ <limit lower="0.0" upper="0.0" effort="0" velocity="0"/>
560
+ </joint> -->
561
+ <!-- wrist pitch-->
562
+ <!-- <link name="ft_arm${arm_num}">
563
+ <inertial>
564
+ <origin xyz="${Ft_arm_x} ${rot*Ft_arm_y} ${Ft_arm_z}"/>
565
+ <mass value="${Ft_arm_mass}"/>
566
+ <inertia ixx="${Ft_arm_xx}" ixy="${rot*Ft_arm_xy}" ixz="${Ft_arm_xz}" iyy="${Ft_arm_yy}" iyz="${rot*Ft_arm_yz}" izz="${Ft_arm_zz}"/>
567
+ </inertial> -->
568
+ <!--visual>
569
+ <origin xyz="0.0 0.0 0.0" rpy="${PI} 0.0 0.0"/>
570
+ <geometry>
571
+ <mesh filename="${MESH_PATH}/Wrist.STL" scale="0.001 ${-rot*0.001} 0.001" />
572
+ </geometry>
573
+ </visual-->
574
+ <!--collision>
575
+ <origin xyz="0.0 0.0 0.0" rpy="${PI} 0.0 0.0"/>
576
+ <geometry>
577
+ <mesh filename="${MESH_PATH}/simple/Wrist.STL" scale="0.001 ${-rot*0.001} 0.001" />
578
+ </geometry>
579
+ </collision-->
580
+ <!-- </link> -->
581
+ <link name="ball1">
582
+ <inertial>
583
+ <origin rpy="0 0 0" xyz="-1.0072498e-05 3.7590658e-05 0.019772332"/>
584
+ <mass value="1.27"/>
585
+ <inertia ixx="0.0023061092" ixy="2.9181758e-07" ixz="-2.1246683999999997e-07" iyy="0.0023053155000000002" iyz="7.9290978e-07" izz="0.0030675615999999997"/>
586
+ </inertial>
587
+ <visual>
588
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
589
+ <geometry name="ball1_visual">
590
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/BallHand.stl" scale="0.001 0.001 0.001"/>
591
+ </geometry>
592
+ <material name="dark_grey"/>
593
+ </visual>
594
+ <collision>
595
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
596
+ <geometry name="ball1_collision">
597
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/BallHand.stl" scale="0.001 0.001 0.001"/>
598
+ </geometry>
599
+ </collision>
600
+ </link>
601
+ <link name="ball1_tip"/>
602
+ <joint name="j_arm1_8" type="fixed">
603
+ <parent link="arm1_6"/>
604
+ <child link="ball1"/>
605
+ <origin rpy="0. 0. 0." xyz="0. -0.05 -0.105"/>
606
+ </joint>
607
+ <joint name="j_ball1_tip" type="fixed">
608
+ <parent link="ball1"/>
609
+ <child link="ball1_tip"/>
610
+ <origin rpy="0 0 0" xyz="0 0 -0.07"/>
611
+ </joint>
612
+ <!-- add armX_8 link for legacy reasons (same as ballX) -->
613
+ <joint name="j_ball1_fixed" type="fixed">
614
+ <parent link="ball1"/>
615
+ <child link="arm1_8"/>
616
+ </joint>
617
+ <link name="arm1_8">
618
+ <inertial>
619
+ <mass value="0"/>
620
+ <inertia ixx="0" ixy="0" ixz="0" iyy="0" iyz="0" izz="0"/>
621
+ </inertial>
622
+ </link>
623
+ <link name="ball2">
624
+ <inertial>
625
+ <origin rpy="0 0 0" xyz="-1.0072498e-05 3.7590658e-05 0.019772332"/>
626
+ <mass value="1.27"/>
627
+ <inertia ixx="0.0023061092" ixy="2.9181758e-07" ixz="-2.1246683999999997e-07" iyy="0.0023053155000000002" iyz="7.9290978e-07" izz="0.0030675615999999997"/>
628
+ </inertial>
629
+ <visual>
630
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
631
+ <geometry name="ball2_visual">
632
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/BallHand.stl" scale="0.001 0.001 0.001"/>
633
+ </geometry>
634
+ <material name="dark_grey"/>
635
+ </visual>
636
+ <collision>
637
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
638
+ <geometry name="ball2_collision">
639
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/BallHand.stl" scale="0.001 0.001 0.001"/>
640
+ </geometry>
641
+ </collision>
642
+ </link>
643
+ <link name="ball2_tip"/>
644
+ <joint name="j_arm2_8" type="fixed">
645
+ <parent link="arm2_6"/>
646
+ <child link="ball2"/>
647
+ <origin rpy="0. 0. 0." xyz="0. 0.05 -0.105"/>
648
+ </joint>
649
+ <joint name="j_ball2_tip" type="fixed">
650
+ <parent link="ball2"/>
651
+ <child link="ball2_tip"/>
652
+ <origin rpy="0 0 0" xyz="0 0 -0.07"/>
653
+ </joint>
654
+ <!-- add armX_8 link for legacy reasons (same as ballX) -->
655
+ <joint name="j_ball2_fixed" type="fixed">
656
+ <parent link="ball2"/>
657
+ <child link="arm2_8"/>
658
+ </joint>
659
+ <link name="arm2_8">
660
+ <inertial>
661
+ <mass value="0"/>
662
+ <inertia ixx="0" ixy="0" ixz="0" iyy="0" iyz="0" izz="0"/>
663
+ </inertial>
664
+ </link>
665
+ <!-- LINKS -->
666
+ <link name="neck_1">
667
+ <inertial>
668
+ <origin xyz="0 0 0"/>
669
+ <mass value="0.33878686"/>
670
+ <!-- MASS/INERTIA TO DO -->
671
+ <inertia ixx="0.0003718568" ixy="-9.42983e-05" ixz="-2.80644e-05" iyy="0.0001886879" iyz="-1.54534e-05" izz="0.0005023443"/>
672
+ </inertial>
673
+ <visual>
674
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
675
+ <geometry>
676
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/head-base_mesh.stl" scale="0.001 0.001 0.001"/>
677
+ </geometry>
678
+ </visual>
679
+ <collision>
680
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
681
+ <geometry>
682
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/head-base_mesh.stl" scale="0.001 0.001 0.001"/>
683
+ </geometry>
684
+ </collision>
685
+ </link>
686
+ <link name="velodyne_motor">
687
+ <inertial>
688
+ <origin xyz="0 0 0"/>
689
+ <mass value="0.33878686"/>
690
+ <!-- MASS/INERTIA TO DO -->
691
+ <inertia ixx="0.0003718568" ixy="-9.42983e-05" ixz="-2.80644e-05" iyy="0.0001886879" iyz="-1.54534e-05" izz="0.0005023443"/>
692
+ </inertial>
693
+ <visual>
694
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
695
+ <geometry>
696
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/lidar_mesh.stl" scale="0.001 0.001 0.001"/>
697
+ </geometry>
698
+ </visual>
699
+ <collision>
700
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
701
+ <geometry>
702
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/lidar_mesh.stl" scale="0.001 0.001 0.001"/>
703
+ </geometry>
704
+ </collision>
705
+ </link>
706
+ <link name="d435_head_motor">
707
+ <inertial>
708
+ <origin xyz="0 0 0"/>
709
+ <mass value="0.33878686"/>
710
+ <!-- MASS/INERTIA TO DO -->
711
+ <inertia ixx="0.0003718568" ixy="-9.42983e-05" ixz="-2.80644e-05" iyy="0.0001886879" iyz="-1.54534e-05" izz="0.0005023443"/>
712
+ </inertial>
713
+ <visual>
714
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
715
+ <geometry>
716
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/neck-pitch_mesh.stl" scale="0.001 0.001 0.001"/>
717
+ </geometry>
718
+ </visual>
719
+ <collision>
720
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0 0.0"/>
721
+ <geometry>
722
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/simple/neck-pitch_mesh.stl" scale="0.001 0.001 0.001"/>
723
+ </geometry>
724
+ </collision>
725
+ </link>
726
+ <!--JOINTS -->
727
+ <joint name="neck_base" type="fixed">
728
+ <parent link="torso_2"/>
729
+ <origin rpy="0 0 0" xyz="0.0030637045 0.0061607221 0.158"/>
730
+ <child link="neck_1"/>
731
+ <axis xyz="0 0 1"/>
732
+ </joint>
733
+ <joint name="velodyne_joint" type="revolute">
734
+ <parent link="neck_1"/>
735
+ <origin rpy="0 0 0" xyz="0.0822724 0 0.242"/>
736
+ <child link="velodyne_motor"/>
737
+ <axis xyz="0 -1 0"/>
738
+ <limit effort="35" lower="-1.1" upper="1.1" velocity="5.7"/>
739
+ <dynamics friction="10.0"/>
740
+ </joint>
741
+ <joint name="d435_head_joint" type="revolute">
742
+ <parent link="neck_1"/>
743
+ <origin rpy="0 0 0" xyz="0.06 0 0.1375"/>
744
+ <child link="d435_head_motor"/>
745
+ <axis xyz="0 -1 0"/>
746
+ <limit effort="35" lower="-1.1" upper="1.1" velocity="5.7"/>
747
+ <dynamics friction="10.0"/>
748
+ </joint>
749
+ <!-- camera body, with origin at bottom screw mount -->
750
+ <joint name="D435_head_camera_joint" type="fixed">
751
+ <origin rpy="0 0 0" xyz="0.03015 0 -0.0125"/>
752
+ <parent link="d435_head_motor"/>
753
+ <child link="D435_head_camera_bottom_screw_frame"/>
754
+ </joint>
755
+ <link name="D435_head_camera_bottom_screw_frame"/>
756
+ <joint name="D435_head_camera_link_joint" type="fixed">
757
+ <origin rpy="0 0 0" xyz="0 0.0175 0.0125"/>
758
+ <parent link="D435_head_camera_bottom_screw_frame"/>
759
+ <child link="D435_head_camera_link"/>
760
+ </joint>
761
+ <link name="D435_head_camera_link">
762
+ <visual>
763
+ <origin rpy="1.570796326795 0 1.570796326795" xyz="0.0149 -0.0175 0"/>
764
+ <geometry>
765
+ <box size="0.09 0.025 0.02505"/>
766
+ <!-- <mesh filename="${MESH_PATH}/realsense/d435.dae"/> -->
767
+ </geometry>
768
+ <material name="D435_head_camera_aluminum"/>
769
+ </visual>
770
+ <collision>
771
+ <origin rpy="0 0 0" xyz="0 -0.0175 0"/>
772
+ <geometry>
773
+ <box size="0.02505 0.09 0.025"/>
774
+ </geometry>
775
+ </collision>
776
+ <inertial>
777
+ <!-- The following are not reliable values, and should not be used for modeling -->
778
+ <mass value="0.564"/>
779
+ <origin xyz="0 0 0"/>
780
+ <inertia ixx="0.003881243" ixy="0.0" ixz="0.0" iyy="0.000498940" iyz="0.0" izz="0.003879257"/>
781
+ </inertial>
782
+ </link>
783
+ <!-- camera body, with origin at bottom screw mount -->
784
+ <joint name="D435i_camera_joint" type="fixed">
785
+ <origin rpy="0 0.54 0" xyz="0.272 0 0.084"/>
786
+ <parent link="pelvis"/>
787
+ <child link="D435i_camera_bottom_screw_frame"/>
788
+ </joint>
789
+ <link name="D435i_camera_bottom_screw_frame"/>
790
+ <joint name="D435i_camera_link_joint" type="fixed">
791
+ <origin rpy="0 0.0050 -0.008" xyz="0.053 0.0 -0.035"/>
792
+ <parent link="D435i_camera_bottom_screw_frame"/>
793
+ <child link="D435i_camera_link"/>
794
+ </joint>
795
+ <link name="D435i_camera_link">
796
+ <visual>
797
+ <origin rpy="1.570796326795 0 1.570796326795" xyz="0.053 -0.0 0"/>
798
+ <geometry>
799
+ <!-- <box size="${d435_cam_width} ${d435_cam_height} ${d435_cam_depth}"/> -->
800
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/realsense/d435.dae"/>
801
+ </geometry>
802
+ <material name="D435i_camera_aluminum"/>
803
+ </visual>
804
+ <collision>
805
+ <origin rpy="0 0 0" xyz="0 -0.0 0"/>
806
+ <geometry>
807
+ <box size="0.02505 0.09 0.025"/>
808
+ </geometry>
809
+ </collision>
810
+ <inertial>
811
+ <!-- The following are not reliable values, and should not be used for modeling -->
812
+ <mass value="0.564"/>
813
+ <origin xyz="0 0 0"/>
814
+ <inertia ixx="0.003881243" ixy="0.0" ixz="0.0" iyy="0.000498940" iyz="0.0" izz="0.003879257"/>
815
+ </inertial>
816
+ </link>
817
+ <!-- /macro centauro_wheel -->
818
+ <!-- /macro arms -->
819
+ <!-- /macro legs -->
820
+ <link name="hip1_1">
821
+ <inertial>
822
+ <origin xyz="0.0003595855 0.033338818000000006 -0.059190309"/>
823
+ <mass value="2.3349153"/>
824
+ <!-- org 2 -->
825
+ <inertia ixx="0.0068405374" ixy="1.8572633e-05" ixz="-4.8325972e-05" iyy="0.0026261815" iyz="0.0001564273" izz="0.0066933354"/>
826
+ </inertial>
827
+ <visual>
828
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
829
+ <geometry>
830
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-yaw.stl" scale="0.001 -0.001 0.001"/>
831
+ </geometry>
832
+ <material name="dark_grey"/>
833
+ </visual>
834
+ <collision>
835
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
836
+ <geometry>
837
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-yaw.stl" scale="0.001 -0.001 0.001"/>
838
+ </geometry>
839
+ </collision>
840
+ </link>
841
+ <link name="hip2_1">
842
+ <inertial>
843
+ <origin xyz="-4.6285952e-06 -0.2214019 0.03316685"/>
844
+ <mass value="3.4572118"/>
845
+ <!-- org 4.5 -->
846
+ <inertia ixx="0.050469853" ixy="-7.784938699999999e-06" ixz="-9.220580999999999e-06" iyy="0.008181596999999999" iyz="0.0059796848" izz="0.048213808999999996"/>
847
+ </inertial>
848
+ <visual>
849
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
850
+ <geometry>
851
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-pitch.stl" scale="0.001 0.001 -0.001"/>
852
+ </geometry>
853
+ </visual>
854
+ <collision>
855
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
856
+ <geometry>
857
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-pitch.stl" scale="0.001 0.001 -0.001"/>
858
+ </geometry>
859
+ </collision>
860
+ </link>
861
+ <link name="knee_1">
862
+ <inertial>
863
+ <origin xyz="0.00012063381 0.14620674 0.029740711000000003"/>
864
+ <mass value="2.1199315"/>
865
+ <!-- org 3.5, foot un modeled-->
866
+ <inertia ixx="0.018426402999999997" ixy="2.8226635999999997e-05" ixz="-2.9204348e-05" iyy="0.0047685615" iyz="-0.002640771" izz="0.017793722"/>
867
+ </inertial>
868
+ <visual>
869
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
870
+ <geometry>
871
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/knee.stl" scale="0.001 0.001 -0.001"/>
872
+ </geometry>
873
+ <material name="dark_grey"/>
874
+ </visual>
875
+ <collision>
876
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
877
+ <geometry>
878
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/knee.stl" scale="0.001 0.001 -0.001"/>
879
+ </geometry>
880
+ </collision>
881
+ </link>
882
+ <link name="ankle1_1">
883
+ <inertial>
884
+ <origin xyz="0.00012360094 -0.06274712 -0.0049068453"/>
885
+ <mass value="1.4023368"/>
886
+ <!-- org 3.5, foot un modeled-->
887
+ <inertia ixx="0.0049928829" ixy="5.762355699999999e-06" ixz="1.9606394e-05" iyy="0.0017621464" iyz="0.00045315091" izz="0.0048706328"/>
888
+ </inertial>
889
+ <visual>
890
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
891
+ <geometry>
892
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/ankle-pitch.stl" scale="0.001 0.001 -0.001"/>
893
+ </geometry>
894
+ </visual>
895
+ <collision>
896
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
897
+ <geometry>
898
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/ankle-pitch.stl" scale="0.001 0.001 -0.001"/>
899
+ </geometry>
900
+ </collision>
901
+ </link>
902
+ <link name="ankle2_1">
903
+ <inertial>
904
+ <origin xyz="0.00018512273 0.0017369405000000001 -0.091340683"/>
905
+ <mass value="2.440929"/>
906
+ <!-- org 3.5, foot un modeled-->
907
+ <inertia ixx="0.017711499" ixy="1.0713141e-05" ixz="-2.2094697e-05" iyy="0.014263485" iyz="0.00033426702" izz="0.0080747557"/>
908
+ </inertial>
909
+ <visual>
910
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
911
+ <geometry>
912
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_ankle-yaw_wheel23.stl" scale="0.001 -0.001 0.001"/>
913
+ </geometry>
914
+ <material name="dark_grey"/>
915
+ </visual>
916
+ <!-- <collision>
917
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
918
+ <geometry>
919
+ <mesh
920
+ filename="${MESH_PATH}/v2/collision/mesh_ankle-yaw_wheel23.stl"
921
+ scale="0.001 ${mirror*0.001} 0.001" />
922
+ </geometry>
923
+ </collision> -->
924
+ </link>
925
+ <joint name="j_wheel_1" type="continuous">
926
+ <parent link="ankle2_1"/>
927
+ <child link="wheel_1"/>
928
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 -0.0 -0.14435"/>
929
+ <axis xyz="0 0 -1"/>
930
+ <limit effort="35" velocity="20.0"/>
931
+ <!-- 23 Nm, 1
932
+ rad/s-->
933
+ </joint>
934
+ <link name="wheel_1">
935
+ <inertial>
936
+ <origin xyz="0.0 0.0 0.00015403767"/>
937
+ <mass value="1.8569388"/>
938
+ <inertia ixx="0.008637359099999999" ixy="0.0" ixz="-0.0" iyy="0.0086373644" iyz="-0.0" izz="0.016376551"/>
939
+ </inertial>
940
+ <visual>
941
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
942
+ <geometry>
943
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_wheel23.stl" scale="0.001 0.001 -0.001"/>
944
+ </geometry>
945
+ <material name="black"/>
946
+ </visual>
947
+ <collision>
948
+ <origin rpy="0 0 0" xyz="0 0 0"/>
949
+ <geometry>
950
+ <cylinder length="0.03" radius="0.124"/>
951
+ </geometry>
952
+ <!-- <geometry>
953
+ <mesh filename="${MESH_PATH}/v2/collision/wheel23_donut.stl"
954
+ scale="0.001 0.001 ${mirror*0.001}" />
955
+ </geometry> -->
956
+ <material name="black"/>
957
+ </collision>
958
+ </link>
959
+ <!-- *********** CONTACT FRAME ******************-->
960
+ <link name="contact_1"/>
961
+ <joint name="contact_joint_1" type="fixed">
962
+ <parent link="ankle2_1"/>
963
+ <child link="contact_1"/>
964
+ <origin rpy="0 0 0" xyz="0 0 -0.26835"/>
965
+ </joint>
966
+ <!-- ******************************** -->
967
+ <joint name="hip_yaw_1" type="revolute">
968
+ <parent link="pelvis"/>
969
+ <child link="hip1_1"/>
970
+ <origin rpy="0.0 0.0 0.0" xyz="0.25 0.125 -0.056600000000000004"/>
971
+ <axis xyz="0 0 -1"/>
972
+ <limit effort="304" lower="-2.03" upper="2.51" velocity="8.8"/>
973
+ <!-- 200 Nm, 1 rad/s-->
974
+ </joint>
975
+ <joint name="hip_pitch_1" type="revolute">
976
+ <parent link="hip1_1"/>
977
+ <child link="hip2_1"/>
978
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 0.1146 -0.0625"/>
979
+ <axis xyz="0 0 -1"/>
980
+ <limit effort="304" lower="-2.0226" upper="2.02748" velocity="8.8"/>
981
+ <!-- 200 Nm, 1 rad/s-->
982
+ </joint>
983
+ <joint name="knee_pitch_1" type="revolute">
984
+ <parent link="hip2_1"/>
985
+ <child link="knee_1"/>
986
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 -0.3 0.1182"/>
987
+ <axis xyz="0 0 -1"/>
988
+ <limit effort="304" lower="-2.4056" upper="2.3994" velocity="8.8"/>
989
+ <!-- 200 Nm, 1 rad/s-->
990
+ </joint>
991
+ <joint name="ankle_pitch_1" type="revolute">
992
+ <parent link="knee_1"/>
993
+ <child link="ankle1_1"/>
994
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 0.2 0.10015"/>
995
+ <axis xyz="0 0 -1"/>
996
+ <limit effort="147" lower="-2.4056" upper="2.3184" velocity="8.1"/>
997
+ <!-- 98 Nm, 1 rad/s-->
998
+ </joint>
999
+ <joint name="ankle_yaw_1" type="revolute">
1000
+ <parent link="ankle1_1"/>
1001
+ <child link="ankle2_1"/>
1002
+ <origin rpy="-1.57079632679 0.0 0.0" xyz="0.0 -0.139 -0.00085"/>
1003
+ <axis xyz="0 0 -1"/>
1004
+ <limit effort="35" lower="-2.5626" upper="2.5384" velocity="20.0"/>
1005
+ <!-- 23 Nm, 1 rad/s-->
1006
+ </joint>
1007
+ <link name="hip1_2">
1008
+ <inertial>
1009
+ <origin xyz="0.0003595855 -0.033338818000000006 -0.059190309"/>
1010
+ <mass value="2.3349153"/>
1011
+ <!-- org 2 -->
1012
+ <inertia ixx="0.0068405374" ixy="-1.8572633e-05" ixz="-4.8325972e-05" iyy="0.0026261815" iyz="-0.0001564273" izz="0.0066933354"/>
1013
+ </inertial>
1014
+ <visual>
1015
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1016
+ <geometry>
1017
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-yaw.stl" scale="0.001 0.001 0.001"/>
1018
+ </geometry>
1019
+ <material name="dark_grey"/>
1020
+ </visual>
1021
+ <collision>
1022
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1023
+ <geometry>
1024
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-yaw.stl" scale="0.001 0.001 0.001"/>
1025
+ </geometry>
1026
+ </collision>
1027
+ </link>
1028
+ <link name="hip2_2">
1029
+ <inertial>
1030
+ <origin xyz="-4.6285952e-06 -0.2214019 -0.03316685"/>
1031
+ <mass value="3.4572118"/>
1032
+ <!-- org 4.5 -->
1033
+ <inertia ixx="0.050469853" ixy="-7.784938699999999e-06" ixz="9.220580999999999e-06" iyy="0.008181596999999999" iyz="-0.0059796848" izz="0.048213808999999996"/>
1034
+ </inertial>
1035
+ <visual>
1036
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1037
+ <geometry>
1038
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-pitch.stl" scale="0.001 0.001 0.001"/>
1039
+ </geometry>
1040
+ </visual>
1041
+ <collision>
1042
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1043
+ <geometry>
1044
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-pitch.stl" scale="0.001 0.001 0.001"/>
1045
+ </geometry>
1046
+ </collision>
1047
+ </link>
1048
+ <link name="knee_2">
1049
+ <inertial>
1050
+ <origin xyz="0.00012063381 0.14620674 -0.029740711000000003"/>
1051
+ <mass value="2.1199315"/>
1052
+ <!-- org 3.5, foot un modeled-->
1053
+ <inertia ixx="0.018426402999999997" ixy="2.8226635999999997e-05" ixz="2.9204348e-05" iyy="0.0047685615" iyz="0.002640771" izz="0.017793722"/>
1054
+ </inertial>
1055
+ <visual>
1056
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1057
+ <geometry>
1058
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/knee.stl" scale="0.001 0.001 0.001"/>
1059
+ </geometry>
1060
+ <material name="dark_grey"/>
1061
+ </visual>
1062
+ <collision>
1063
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1064
+ <geometry>
1065
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/knee.stl" scale="0.001 0.001 0.001"/>
1066
+ </geometry>
1067
+ </collision>
1068
+ </link>
1069
+ <link name="ankle1_2">
1070
+ <inertial>
1071
+ <origin xyz="0.00012360094 -0.06274712 0.0049068453"/>
1072
+ <mass value="1.4023368"/>
1073
+ <!-- org 3.5, foot un modeled-->
1074
+ <inertia ixx="0.0049928829" ixy="5.762355699999999e-06" ixz="-1.9606394e-05" iyy="0.0017621464" iyz="-0.00045315091" izz="0.0048706328"/>
1075
+ </inertial>
1076
+ <visual>
1077
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1078
+ <geometry>
1079
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/ankle-pitch.stl" scale="0.001 0.001 0.001"/>
1080
+ </geometry>
1081
+ </visual>
1082
+ <collision>
1083
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1084
+ <geometry>
1085
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/ankle-pitch.stl" scale="0.001 0.001 0.001"/>
1086
+ </geometry>
1087
+ </collision>
1088
+ </link>
1089
+ <link name="ankle2_2">
1090
+ <inertial>
1091
+ <origin xyz="0.00018512273 -0.0017369405000000001 -0.091340683"/>
1092
+ <mass value="2.440929"/>
1093
+ <!-- org 3.5, foot un modeled-->
1094
+ <inertia ixx="0.017711499" ixy="-1.0713141e-05" ixz="-2.2094697e-05" iyy="0.014263485" iyz="-0.00033426702" izz="0.0080747557"/>
1095
+ </inertial>
1096
+ <visual>
1097
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1098
+ <geometry>
1099
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_ankle-yaw_wheel23.stl" scale="0.001 0.001 0.001"/>
1100
+ </geometry>
1101
+ <material name="dark_grey"/>
1102
+ </visual>
1103
+ <!-- <collision>
1104
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
1105
+ <geometry>
1106
+ <mesh
1107
+ filename="${MESH_PATH}/v2/collision/mesh_ankle-yaw_wheel23.stl"
1108
+ scale="0.001 ${mirror*0.001} 0.001" />
1109
+ </geometry>
1110
+ </collision> -->
1111
+ </link>
1112
+ <joint name="j_wheel_2" type="continuous">
1113
+ <parent link="ankle2_2"/>
1114
+ <child link="wheel_2"/>
1115
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 0.0 -0.14435"/>
1116
+ <axis xyz="0 0 1"/>
1117
+ <limit effort="35" velocity="20.0"/>
1118
+ <!-- 23 Nm, 1
1119
+ rad/s-->
1120
+ </joint>
1121
+ <link name="wheel_2">
1122
+ <inertial>
1123
+ <origin xyz="0.0 0.0 -0.00015403767"/>
1124
+ <mass value="1.8569388"/>
1125
+ <inertia ixx="0.008637359099999999" ixy="0.0" ixz="0.0" iyy="0.0086373644" iyz="0.0" izz="0.016376551"/>
1126
+ </inertial>
1127
+ <visual>
1128
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1129
+ <geometry>
1130
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_wheel23.stl" scale="0.001 0.001 0.001"/>
1131
+ </geometry>
1132
+ <material name="black"/>
1133
+ </visual>
1134
+ <collision>
1135
+ <origin rpy="0 0 0" xyz="0 0 0"/>
1136
+ <geometry>
1137
+ <cylinder length="0.03" radius="0.124"/>
1138
+ </geometry>
1139
+ <!-- <geometry>
1140
+ <mesh filename="${MESH_PATH}/v2/collision/wheel23_donut.stl"
1141
+ scale="0.001 0.001 ${mirror*0.001}" />
1142
+ </geometry> -->
1143
+ <material name="black"/>
1144
+ </collision>
1145
+ </link>
1146
+ <!-- *********** CONTACT FRAME ******************-->
1147
+ <link name="contact_2"/>
1148
+ <joint name="contact_joint_2" type="fixed">
1149
+ <parent link="ankle2_2"/>
1150
+ <child link="contact_2"/>
1151
+ <origin rpy="0 0 0" xyz="0 0 -0.26835"/>
1152
+ </joint>
1153
+ <!-- ******************************** -->
1154
+ <joint name="hip_yaw_2" type="revolute">
1155
+ <parent link="pelvis"/>
1156
+ <child link="hip1_2"/>
1157
+ <origin rpy="0.0 0.0 0.0" xyz="0.25 -0.125 -0.056600000000000004"/>
1158
+ <axis xyz="0 0 -1"/>
1159
+ <limit effort="304" lower="-2.51" upper="2.03" velocity="8.8"/>
1160
+ <!-- 200 Nm, 1 rad/s-->
1161
+ </joint>
1162
+ <joint name="hip_pitch_2" type="revolute">
1163
+ <parent link="hip1_2"/>
1164
+ <child link="hip2_2"/>
1165
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 -0.1146 -0.0625"/>
1166
+ <axis xyz="0 0 1"/>
1167
+ <limit effort="304" lower="-2.0156" upper="2.0354" velocity="8.8"/>
1168
+ <!-- 200 Nm, 1 rad/s-->
1169
+ </joint>
1170
+ <joint name="knee_pitch_2" type="revolute">
1171
+ <parent link="hip2_2"/>
1172
+ <child link="knee_2"/>
1173
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 -0.3 -0.1182"/>
1174
+ <axis xyz="0 0 1"/>
1175
+ <limit effort="304" lower="-2.4006" upper="2.4034" velocity="8.8"/>
1176
+ <!-- 200 Nm, 1 rad/s-->
1177
+ </joint>
1178
+ <joint name="ankle_pitch_2" type="revolute">
1179
+ <parent link="knee_2"/>
1180
+ <child link="ankle1_2"/>
1181
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 0.2 -0.10015"/>
1182
+ <axis xyz="0 0 1"/>
1183
+ <limit effort="147" lower="-2.3266" upper="2.3914" velocity="8.1"/>
1184
+ <!-- 98 Nm, 1 rad/s-->
1185
+ </joint>
1186
+ <joint name="ankle_yaw_2" type="revolute">
1187
+ <parent link="ankle1_2"/>
1188
+ <child link="ankle2_2"/>
1189
+ <origin rpy="-1.57079632679 0.0 0.0" xyz="0.0 -0.139 0.00085"/>
1190
+ <axis xyz="0 0 -1"/>
1191
+ <limit effort="35" lower="-2.5546" upper="2.5484" velocity="20.0"/>
1192
+ <!-- 23 Nm, 1 rad/s-->
1193
+ </joint>
1194
+ <link name="hip1_3">
1195
+ <inertial>
1196
+ <origin xyz="0.0003595855 0.033338818000000006 -0.059190309"/>
1197
+ <mass value="2.3349153"/>
1198
+ <!-- org 2 -->
1199
+ <inertia ixx="0.0068405374" ixy="1.8572633e-05" ixz="-4.8325972e-05" iyy="0.0026261815" iyz="0.0001564273" izz="0.0066933354"/>
1200
+ </inertial>
1201
+ <visual>
1202
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1203
+ <geometry>
1204
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-yaw.stl" scale="0.001 -0.001 0.001"/>
1205
+ </geometry>
1206
+ <material name="dark_grey"/>
1207
+ </visual>
1208
+ <collision>
1209
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1210
+ <geometry>
1211
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-yaw.stl" scale="0.001 -0.001 0.001"/>
1212
+ </geometry>
1213
+ </collision>
1214
+ </link>
1215
+ <link name="hip2_3">
1216
+ <inertial>
1217
+ <origin xyz="-4.6285952e-06 -0.2214019 0.03316685"/>
1218
+ <mass value="3.4572118"/>
1219
+ <!-- org 4.5 -->
1220
+ <inertia ixx="0.050469853" ixy="-7.784938699999999e-06" ixz="-9.220580999999999e-06" iyy="0.008181596999999999" iyz="0.0059796848" izz="0.048213808999999996"/>
1221
+ </inertial>
1222
+ <visual>
1223
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1224
+ <geometry>
1225
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-pitch.stl" scale="0.001 0.001 -0.001"/>
1226
+ </geometry>
1227
+ </visual>
1228
+ <collision>
1229
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1230
+ <geometry>
1231
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-pitch.stl" scale="0.001 0.001 -0.001"/>
1232
+ </geometry>
1233
+ </collision>
1234
+ </link>
1235
+ <link name="knee_3">
1236
+ <inertial>
1237
+ <origin xyz="0.00012063381 0.14620674 0.029740711000000003"/>
1238
+ <mass value="2.1199315"/>
1239
+ <!-- org 3.5, foot un modeled-->
1240
+ <inertia ixx="0.018426402999999997" ixy="2.8226635999999997e-05" ixz="-2.9204348e-05" iyy="0.0047685615" iyz="-0.002640771" izz="0.017793722"/>
1241
+ </inertial>
1242
+ <visual>
1243
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1244
+ <geometry>
1245
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/knee.stl" scale="0.001 0.001 -0.001"/>
1246
+ </geometry>
1247
+ <material name="dark_grey"/>
1248
+ </visual>
1249
+ <collision>
1250
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1251
+ <geometry>
1252
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/knee.stl" scale="0.001 0.001 -0.001"/>
1253
+ </geometry>
1254
+ </collision>
1255
+ </link>
1256
+ <link name="ankle1_3">
1257
+ <inertial>
1258
+ <origin xyz="0.00012360094 -0.06274712 -0.0049068453"/>
1259
+ <mass value="1.4023368"/>
1260
+ <!-- org 3.5, foot un modeled-->
1261
+ <inertia ixx="0.0049928829" ixy="5.762355699999999e-06" ixz="1.9606394e-05" iyy="0.0017621464" iyz="0.00045315091" izz="0.0048706328"/>
1262
+ </inertial>
1263
+ <visual>
1264
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1265
+ <geometry>
1266
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/ankle-pitch.stl" scale="0.001 0.001 -0.001"/>
1267
+ </geometry>
1268
+ </visual>
1269
+ <collision>
1270
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1271
+ <geometry>
1272
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/ankle-pitch.stl" scale="0.001 0.001 -0.001"/>
1273
+ </geometry>
1274
+ </collision>
1275
+ </link>
1276
+ <link name="ankle2_3">
1277
+ <inertial>
1278
+ <origin xyz="0.00018512273 0.0017369405000000001 -0.091340683"/>
1279
+ <mass value="2.440929"/>
1280
+ <!-- org 3.5, foot un modeled-->
1281
+ <inertia ixx="0.017711499" ixy="1.0713141e-05" ixz="-2.2094697e-05" iyy="0.014263485" iyz="0.00033426702" izz="0.0080747557"/>
1282
+ </inertial>
1283
+ <visual>
1284
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1285
+ <geometry>
1286
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_ankle-yaw_wheel23.stl" scale="0.001 -0.001 0.001"/>
1287
+ </geometry>
1288
+ <material name="dark_grey"/>
1289
+ </visual>
1290
+ <!-- <collision>
1291
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
1292
+ <geometry>
1293
+ <mesh
1294
+ filename="${MESH_PATH}/v2/collision/mesh_ankle-yaw_wheel23.stl"
1295
+ scale="0.001 ${mirror*0.001} 0.001" />
1296
+ </geometry>
1297
+ </collision> -->
1298
+ </link>
1299
+ <joint name="j_wheel_3" type="continuous">
1300
+ <parent link="ankle2_3"/>
1301
+ <child link="wheel_3"/>
1302
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 -0.0 -0.14435"/>
1303
+ <axis xyz="0 0 -1"/>
1304
+ <limit effort="35" velocity="20.0"/>
1305
+ <!-- 23 Nm, 1
1306
+ rad/s-->
1307
+ </joint>
1308
+ <link name="wheel_3">
1309
+ <inertial>
1310
+ <origin xyz="0.0 0.0 0.00015403767"/>
1311
+ <mass value="1.8569388"/>
1312
+ <inertia ixx="0.008637359099999999" ixy="0.0" ixz="-0.0" iyy="0.0086373644" iyz="-0.0" izz="0.016376551"/>
1313
+ </inertial>
1314
+ <visual>
1315
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1316
+ <geometry>
1317
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_wheel23.stl" scale="0.001 0.001 -0.001"/>
1318
+ </geometry>
1319
+ <material name="black"/>
1320
+ </visual>
1321
+ <collision>
1322
+ <origin rpy="0 0 0" xyz="0 0 0"/>
1323
+ <geometry>
1324
+ <cylinder length="0.03" radius="0.124"/>
1325
+ </geometry>
1326
+ <!-- <geometry>
1327
+ <mesh filename="${MESH_PATH}/v2/collision/wheel23_donut.stl"
1328
+ scale="0.001 0.001 ${mirror*0.001}" />
1329
+ </geometry> -->
1330
+ <material name="black"/>
1331
+ </collision>
1332
+ </link>
1333
+ <!-- *********** CONTACT FRAME ******************-->
1334
+ <link name="contact_3"/>
1335
+ <joint name="contact_joint_3" type="fixed">
1336
+ <parent link="ankle2_3"/>
1337
+ <child link="contact_3"/>
1338
+ <origin rpy="0 0 0" xyz="0 0 -0.26835"/>
1339
+ </joint>
1340
+ <!-- ******************************** -->
1341
+ <joint name="hip_yaw_3" type="revolute">
1342
+ <parent link="pelvis"/>
1343
+ <child link="hip1_3"/>
1344
+ <origin rpy="0.0 0.0 0.0" xyz="-0.25 0.125 -0.056600000000000004"/>
1345
+ <axis xyz="0 0 -1"/>
1346
+ <limit effort="304" lower="-2.51" upper="2.03" velocity="8.8"/>
1347
+ <!-- 200 Nm, 1 rad/s-->
1348
+ </joint>
1349
+ <joint name="hip_pitch_3" type="revolute">
1350
+ <parent link="hip1_3"/>
1351
+ <child link="hip2_3"/>
1352
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 0.1146 -0.0625"/>
1353
+ <axis xyz="0 0 -1"/>
1354
+ <limit effort="304" lower="-2.0126" upper="2.0384" velocity="8.8"/>
1355
+ <!-- 200 Nm, 1 rad/s-->
1356
+ </joint>
1357
+ <joint name="knee_pitch_3" type="revolute">
1358
+ <parent link="hip2_3"/>
1359
+ <child link="knee_3"/>
1360
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 -0.3 0.1182"/>
1361
+ <axis xyz="0 0 -1"/>
1362
+ <limit effort="304" lower="-2.4056" upper="2.4064" velocity="8.8"/>
1363
+ <!-- 200 Nm, 1 rad/s-->
1364
+ </joint>
1365
+ <joint name="ankle_pitch_3" type="revolute">
1366
+ <parent link="knee_3"/>
1367
+ <child link="ankle1_3"/>
1368
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 0.2 0.10015"/>
1369
+ <axis xyz="0 0 -1"/>
1370
+ <limit effort="147" lower="-2.4696" upper="2.4464" velocity="8.1"/>
1371
+ <!-- 98 Nm, 1 rad/s-->
1372
+ </joint>
1373
+ <joint name="ankle_yaw_3" type="revolute">
1374
+ <parent link="ankle1_3"/>
1375
+ <child link="ankle2_3"/>
1376
+ <origin rpy="-1.57079632679 0.0 0.0" xyz="0.0 -0.139 -0.00085"/>
1377
+ <axis xyz="0 0 -1"/>
1378
+ <limit effort="35" lower="-2.5606" upper="2.5454" velocity="20.0"/>
1379
+ <!-- 23 Nm, 1 rad/s-->
1380
+ </joint>
1381
+ <link name="hip1_4">
1382
+ <inertial>
1383
+ <origin xyz="0.0003595855 -0.033338818000000006 -0.059190309"/>
1384
+ <mass value="2.3349153"/>
1385
+ <!-- org 2 -->
1386
+ <inertia ixx="0.0068405374" ixy="-1.8572633e-05" ixz="-4.8325972e-05" iyy="0.0026261815" iyz="-0.0001564273" izz="0.0066933354"/>
1387
+ </inertial>
1388
+ <visual>
1389
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1390
+ <geometry>
1391
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-yaw.stl" scale="0.001 0.001 0.001"/>
1392
+ </geometry>
1393
+ <material name="dark_grey"/>
1394
+ </visual>
1395
+ <collision>
1396
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1397
+ <geometry>
1398
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-yaw.stl" scale="0.001 0.001 0.001"/>
1399
+ </geometry>
1400
+ </collision>
1401
+ </link>
1402
+ <link name="hip2_4">
1403
+ <inertial>
1404
+ <origin xyz="-4.6285952e-06 -0.2214019 -0.03316685"/>
1405
+ <mass value="3.4572118"/>
1406
+ <!-- org 4.5 -->
1407
+ <inertia ixx="0.050469853" ixy="-7.784938699999999e-06" ixz="9.220580999999999e-06" iyy="0.008181596999999999" iyz="-0.0059796848" izz="0.048213808999999996"/>
1408
+ </inertial>
1409
+ <visual>
1410
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1411
+ <geometry>
1412
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/hip-pitch.stl" scale="0.001 0.001 0.001"/>
1413
+ </geometry>
1414
+ </visual>
1415
+ <collision>
1416
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1417
+ <geometry>
1418
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/hip-pitch.stl" scale="0.001 0.001 0.001"/>
1419
+ </geometry>
1420
+ </collision>
1421
+ </link>
1422
+ <link name="knee_4">
1423
+ <inertial>
1424
+ <origin xyz="0.00012063381 0.14620674 -0.029740711000000003"/>
1425
+ <mass value="2.1199315"/>
1426
+ <!-- org 3.5, foot un modeled-->
1427
+ <inertia ixx="0.018426402999999997" ixy="2.8226635999999997e-05" ixz="2.9204348e-05" iyy="0.0047685615" iyz="0.002640771" izz="0.017793722"/>
1428
+ </inertial>
1429
+ <visual>
1430
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1431
+ <geometry>
1432
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/knee.stl" scale="0.001 0.001 0.001"/>
1433
+ </geometry>
1434
+ <material name="dark_grey"/>
1435
+ </visual>
1436
+ <collision>
1437
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1438
+ <geometry>
1439
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/knee.stl" scale="0.001 0.001 0.001"/>
1440
+ </geometry>
1441
+ </collision>
1442
+ </link>
1443
+ <link name="ankle1_4">
1444
+ <inertial>
1445
+ <origin xyz="0.00012360094 -0.06274712 0.0049068453"/>
1446
+ <mass value="1.4023368"/>
1447
+ <!-- org 3.5, foot un modeled-->
1448
+ <inertia ixx="0.0049928829" ixy="5.762355699999999e-06" ixz="-1.9606394e-05" iyy="0.0017621464" iyz="-0.00045315091" izz="0.0048706328"/>
1449
+ </inertial>
1450
+ <visual>
1451
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1452
+ <geometry>
1453
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/ankle-pitch.stl" scale="0.001 0.001 0.001"/>
1454
+ </geometry>
1455
+ </visual>
1456
+ <collision>
1457
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1458
+ <geometry>
1459
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/collision/ankle-pitch.stl" scale="0.001 0.001 0.001"/>
1460
+ </geometry>
1461
+ </collision>
1462
+ </link>
1463
+ <link name="ankle2_4">
1464
+ <inertial>
1465
+ <origin xyz="0.00018512273 -0.0017369405000000001 -0.091340683"/>
1466
+ <mass value="2.440929"/>
1467
+ <!-- org 3.5, foot un modeled-->
1468
+ <inertia ixx="0.017711499" ixy="-1.0713141e-05" ixz="-2.2094697e-05" iyy="0.014263485" iyz="-0.00033426702" izz="0.0080747557"/>
1469
+ </inertial>
1470
+ <visual>
1471
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1472
+ <geometry>
1473
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_ankle-yaw_wheel23.stl" scale="0.001 0.001 0.001"/>
1474
+ </geometry>
1475
+ <material name="dark_grey"/>
1476
+ </visual>
1477
+ <!-- <collision>
1478
+ <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
1479
+ <geometry>
1480
+ <mesh
1481
+ filename="${MESH_PATH}/v2/collision/mesh_ankle-yaw_wheel23.stl"
1482
+ scale="0.001 ${mirror*0.001} 0.001" />
1483
+ </geometry>
1484
+ </collision> -->
1485
+ </link>
1486
+ <joint name="j_wheel_4" type="continuous">
1487
+ <parent link="ankle2_4"/>
1488
+ <child link="wheel_4"/>
1489
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 0.0 -0.14435"/>
1490
+ <axis xyz="0 0 1"/>
1491
+ <limit effort="35" velocity="20.0"/>
1492
+ <!-- 23 Nm, 1
1493
+ rad/s-->
1494
+ </joint>
1495
+ <link name="wheel_4">
1496
+ <inertial>
1497
+ <origin xyz="0.0 0.0 -0.00015403767"/>
1498
+ <mass value="1.8569388"/>
1499
+ <inertia ixx="0.008637359099999999" ixy="0.0" ixz="0.0" iyy="0.0086373644" iyz="0.0" izz="0.016376551"/>
1500
+ </inertial>
1501
+ <visual>
1502
+ <origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
1503
+ <geometry>
1504
+ <mesh filename="/root/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/meshes/v2/mesh_wheel23.stl" scale="0.001 0.001 0.001"/>
1505
+ </geometry>
1506
+ <material name="black"/>
1507
+ </visual>
1508
+ <collision>
1509
+ <origin rpy="0 0 0" xyz="0 0 0"/>
1510
+ <geometry>
1511
+ <cylinder length="0.03" radius="0.124"/>
1512
+ </geometry>
1513
+ <!-- <geometry>
1514
+ <mesh filename="${MESH_PATH}/v2/collision/wheel23_donut.stl"
1515
+ scale="0.001 0.001 ${mirror*0.001}" />
1516
+ </geometry> -->
1517
+ <material name="black"/>
1518
+ </collision>
1519
+ </link>
1520
+ <!-- *********** CONTACT FRAME ******************-->
1521
+ <link name="contact_4"/>
1522
+ <joint name="contact_joint_4" type="fixed">
1523
+ <parent link="ankle2_4"/>
1524
+ <child link="contact_4"/>
1525
+ <origin rpy="0 0 0" xyz="0 0 -0.26835"/>
1526
+ </joint>
1527
+ <!-- ******************************** -->
1528
+ <joint name="hip_yaw_4" type="revolute">
1529
+ <parent link="pelvis"/>
1530
+ <child link="hip1_4"/>
1531
+ <origin rpy="0.0 0.0 0.0" xyz="-0.25 -0.125 -0.056600000000000004"/>
1532
+ <axis xyz="0 0 -1"/>
1533
+ <limit effort="304" lower="-2.03" upper="2.51" velocity="8.8"/>
1534
+ <!-- 200 Nm, 1 rad/s-->
1535
+ </joint>
1536
+ <joint name="hip_pitch_4" type="revolute">
1537
+ <parent link="hip1_4"/>
1538
+ <child link="hip2_4"/>
1539
+ <origin rpy="1.57079632679 0.0 0.0" xyz="0.0 -0.1146 -0.0625"/>
1540
+ <axis xyz="0 0 1"/>
1541
+ <limit effort="304" lower="-2.0326" upper="2.0524" velocity="8.8"/>
1542
+ <!-- 200 Nm, 1 rad/s-->
1543
+ </joint>
1544
+ <joint name="knee_pitch_4" type="revolute">
1545
+ <parent link="hip2_4"/>
1546
+ <child link="knee_4"/>
1547
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 -0.3 -0.1182"/>
1548
+ <axis xyz="0 0 1"/>
1549
+ <limit effort="304" lower="-2.4176" upper="2.3884" velocity="8.8"/>
1550
+ <!-- 200 Nm, 1 rad/s-->
1551
+ </joint>
1552
+ <joint name="ankle_pitch_4" type="revolute">
1553
+ <parent link="knee_4"/>
1554
+ <child link="ankle1_4"/>
1555
+ <origin rpy="3.14159265359 0.0 0.0" xyz="0.0 0.2 -0.10015"/>
1556
+ <axis xyz="0 0 1"/>
1557
+ <limit effort="147" lower="-2.3426" upper="2.3934" velocity="8.1"/>
1558
+ <!-- 98 Nm, 1 rad/s-->
1559
+ </joint>
1560
+ <joint name="ankle_yaw_4" type="revolute">
1561
+ <parent link="ankle1_4"/>
1562
+ <child link="ankle2_4"/>
1563
+ <origin rpy="-1.57079632679 0.0 0.0" xyz="0.0 -0.139 0.00085"/>
1564
+ <axis xyz="0 0 -1"/>
1565
+ <limit effort="35" lower="-2.6046" upper="2.5514" velocity="20.0"/>
1566
+ <!-- 23 Nm, 1 rad/s-->
1567
+ </joint>
1568
+ <!-- ************ Control frames ************ -->
1569
+ </robot>
bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/centauro_rhc.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from aug_mpc.controllers.rhc.horizon_based.hybrid_quad_rhc import HybridQuadRhc
2
+
3
+ import numpy as np
4
+
5
+ from typing import Dict
6
+
7
+ from centaurohybridmpc.utils.sysutils import PathsGetter
8
+
9
+ class CentauroRhc(HybridQuadRhc):
10
+
11
+ def __init__(self,
12
+ srdf_path: str,
13
+ urdf_path: str,
14
+ robot_name: str, # used for shared memory namespaces
15
+ codegen_dir: str,
16
+ n_nodes: float = 31,
17
+ dt: float = 0.03,
18
+ injection_node: int = 10,
19
+ max_solver_iter = 1, # defaults to rt-iteration
20
+ open_loop: bool = True,
21
+ close_loop_all: bool = False,
22
+ dtype = np.float32,
23
+ verbose = False,
24
+ debug = False,
25
+ refs_in_hor_frame = True,
26
+ timeout_ms: int = 60000,
27
+ custom_opts: Dict = {}
28
+ ):
29
+
30
+ paths = PathsGetter()
31
+ self._files_suffix=""
32
+ if open_loop:
33
+ self._files_suffix="_open"
34
+
35
+ self._add_upper_body=False
36
+ if ("add_upper_body" in custom_opts) and \
37
+ (custom_opts["add_upper_body"]):
38
+ self._add_upper_body=True
39
+ self._files_suffix+="_ub"
40
+
41
+ config_path=paths.RHCCONFIGPATH_NO_WHEELS+self._files_suffix+".yaml"
42
+
43
+ super().__init__(srdf_path=srdf_path,
44
+ urdf_path=urdf_path,
45
+ config_path=config_path,
46
+ robot_name=robot_name, # used for shared memory namespaces
47
+ codegen_dir=codegen_dir,
48
+ n_nodes=n_nodes,
49
+ dt=dt,
50
+ injection_node=injection_node,
51
+ max_solver_iter=max_solver_iter, # defaults to rt-iteration
52
+ open_loop=open_loop,
53
+ close_loop_all=close_loop_all,
54
+ dtype=dtype,
55
+ verbose=verbose,
56
+ debug=debug,
57
+ refs_in_hor_frame=refs_in_hor_frame,
58
+ timeout_ms=timeout_ms,
59
+ custom_opts=custom_opts)
60
+
61
+ self._fail_idx_scale=1e-9
62
+ self._fail_idx_thresh_open_loop=1e0
63
+ self._fail_idx_thresh_close_loop=10
64
+
65
+ if open_loop:
66
+ self._fail_idx_thresh=self._fail_idx_thresh_open_loop
67
+ else:
68
+ self._fail_idx_thresh=self._fail_idx_thresh_close_loop
69
+
70
+ # adding some additional config files for jnt imp control
71
+ self._rhc_fpaths.append(paths.JNT_IMP_CONFIG_XBOT+".yaml")
72
+ self._rhc_fpaths.append(paths.JNT_IMP_CONFIG+".yaml")
73
+
74
+ def _set_rhc_pred_idx(self):
75
+ self._pred_node_idx=round((self._n_nodes-1)*2/3)
76
+
77
+ def _set_rhc_cmds_idx(self):
78
+ self._rhc_cmds_node_idx=2
79
+
80
+ def _config_override(self):
81
+ paths = PathsGetter()
82
+ if ("control_wheels" in self._custom_opts):
83
+ if self._custom_opts["control_wheels"]:
84
+ self.config_path = paths.RHCCONFIGPATH_WHEELS+self._files_suffix+".yaml"
85
+ if ("fix_yaw" in self._custom_opts) and \
86
+ (self._custom_opts["fix_yaw"]):
87
+ self.config_path = paths.RHCCONFIGPATH_WHEELS_NO_YAW+self._files_suffix+".yaml"
88
+ if ("replace_continuous_joints" in self._custom_opts) and \
89
+ (not self._custom_opts["replace_continuous_joints"]):
90
+ # use continuous joints -> different config
91
+ self.config_path = paths.RHCCONFIGPATH_WHEELS_CONTINUOUS+self._files_suffix+".yaml"
92
+ if ("fix_yaw" in self._custom_opts) and \
93
+ (self._custom_opts["fix_yaw"]):
94
+ self.config_path = paths.RHCCONFIGPATH_WHEELS_CONTINUOUS_NO_YAW+self._files_suffix+".yaml"
95
+
96
+ else:
97
+ self._custom_opts["control_wheels"]=False
98
+
99
+ if not self._custom_opts["control_wheels"]:
100
+ self._fixed_jnt_patterns=self._fixed_jnt_patterns+\
101
+ ["j_wheel",
102
+ "ankle_yaw"]
103
+ self._custom_opts["replace_continuous_joints"]=True
104
+
105
+ def _init_problem(self):
106
+
107
+ if not self._custom_opts["control_wheels"]:
108
+ self._yaw_vertical_weight=120.0
109
+ else:
110
+ self._yaw_vertical_weight=50.0
111
+
112
+ fixed_jnts_patterns=[
113
+ "d435_head",
114
+ "velodyne_joint",
115
+ "dagana"]
116
+
117
+ if not self._add_upper_body:
118
+ fixed_jnts_patterns.append("j_arm")
119
+ fixed_jnts_patterns.append("torso")
120
+
121
+ if ("fix_yaw" in self._custom_opts) and \
122
+ (self._custom_opts["fix_yaw"]):
123
+ fixed_jnts_patterns.append("ankle_yaw")
124
+
125
+ flight_duration_sec=0.5 # [s]
126
+ flight_duration=int(flight_duration_sec/self._dt)
127
+ post_flight_duration_sec=0.2 # [s]
128
+ post_flight_duration=int(post_flight_duration_sec/self._dt)
129
+
130
+ step_height=0.1
131
+ if ("step_height" in self._custom_opts):
132
+ step_height=self._custom_opts["step_height"]
133
+
134
+ super()._init_problem(fixed_jnt_patterns=fixed_jnts_patterns,
135
+ wheels_patterns=["wheel_"],
136
+ foot_linkname="wheel_1",
137
+ flight_duration=flight_duration,
138
+ post_flight_stance=post_flight_duration,
139
+ step_height=step_height,
140
+ keep_yaw_vert=True,
141
+ yaw_vertical_weight=self._yaw_vertical_weight,
142
+ vertical_landing=True,
143
+ vertical_land_weight=10.0,
144
+ phase_force_reg=5e-2,
145
+ vel_bounds_weight=1.0)
bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/centauro_rhc_config_no_wheels_ub.yaml ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ solver:
2
+ type: ilqr
3
+ ipopt.linear_solver: ma57
4
+ ipopt.tol: 0.1
5
+ # ilqr.merit_der_threshold: 1e-3
6
+ # ilqr.defect_norm_threshold: 1e-3
7
+ ipopt.constr_viol_tol: 0.01
8
+ ilqr.constraint_violation_threshold: 1e-2
9
+ # ipopt.hessian_approximation: exact
10
+ ipopt.print_level: 5
11
+ ipopt.suppress_all_output: 'yes'
12
+ ipopt.sb: 'yes'
13
+ ilqr.suppress_all_output: 'yes'
14
+ ilqr.codegen_enabled: true
15
+ ilqr.codegen_workdir: /tmp/tyhio
16
+ ilqr.enable_gn: true
17
+ ilqr.hxx_reg_base: 0.0
18
+ ilqr.n_threads: 0
19
+ print_time: 0
20
+
21
+ constraints:
22
+ - contact_1
23
+ - contact_2
24
+ - contact_3
25
+ - contact_4
26
+
27
+ costs:
28
+ - z_contact_1
29
+ - z_contact_2
30
+ - z_contact_3
31
+ - z_contact_4
32
+ # - vz_contact_1
33
+ # - vz_contact_2
34
+ # - vz_contact_3
35
+ # - vz_contact_4
36
+ - base_lin_velxy
37
+ - base_lin_velz
38
+ - base_omega
39
+ - base_capture
40
+ - joint_posture_capture
41
+ - v_regularization
42
+ - a_regularization
43
+ # - force_regularization
44
+
45
+ #open loop
46
+ .define:
47
+ - &w_base_omega 15.
48
+ - &w_base_vxy 18.
49
+ - &w_base_vz 15.
50
+ - &w_base_z 15.
51
+ - &w_contact_z 200.0
52
+ - &w_contact_vz 250.0
53
+ - &w_base_capture 200.
54
+ - &wheel_radius 0.124
55
+
56
+ base_lin_velxy:
57
+ type: Cartesian
58
+ distal_link: base_link
59
+ indices: [0, 1]
60
+ nodes: ${range(1, N-5)}
61
+ cartesian_type: velocity
62
+ weight: *w_base_vxy
63
+
64
+ base_lin_velz:
65
+ type: Cartesian
66
+ distal_link: base_link
67
+ indices: [2]
68
+ nodes: ${range(1, N-5)}
69
+ cartesian_type: velocity
70
+ weight: *w_base_vz
71
+
72
+ base_omega:
73
+ type: Cartesian
74
+ distal_link: base_link
75
+ indices: [3, 4, 5]
76
+ nodes: ${range(1, N-5)}
77
+ cartesian_type: velocity
78
+ weight: *w_base_omega
79
+
80
+ base_capture:
81
+ type: Cartesian
82
+ distal_link: base_link
83
+ indices: [0, 1, 2, 3, 4, 5]
84
+ nodes: ${range(N-5, N+1)}
85
+ cartesian_type: velocity
86
+ weight: *w_base_capture
87
+
88
+ # ===============================
89
+
90
+ zero_velocity_contact_1:
91
+ type: Cartesian
92
+ base_link: "world"
93
+ distal_link: wheel_1
94
+ indices: [0, 1, 2]
95
+ cartesian_type: velocity
96
+ # offset: *wheel_radius
97
+
98
+ zero_velocity_contact_2:
99
+ type: Cartesian
100
+ base_link: "world"
101
+ distal_link: wheel_2
102
+ indices: [0, 1, 2]
103
+ cartesian_type: velocity
104
+ # offset: *wheel_radius
105
+
106
+ zero_velocity_contact_3:
107
+ type: Cartesian
108
+ base_link: "world"
109
+ distal_link: wheel_3
110
+ indices: [0, 1, 2]
111
+ cartesian_type: velocity
112
+ # offset: *wheel_radius
113
+
114
+ zero_velocity_contact_4:
115
+ type: Cartesian
116
+ base_link: "world"
117
+ distal_link: wheel_4
118
+ indices: [0, 1, 2]
119
+ cartesian_type: velocity
120
+ # offset: *wheel_radius
121
+
122
+ # ==================================
123
+
124
+ interaction_contact_1:
125
+ type: VertexForce
126
+ frame: contact_1
127
+ fn_min: 100.0
128
+ enable_fc: true
129
+ friction_coeff: 0.2
130
+ vertex_frames:
131
+ - wheel_1
132
+
133
+ interaction_contact_2:
134
+ type: VertexForce
135
+ frame: contact_2
136
+ fn_min: 100.0
137
+ enable_fc: true
138
+ friction_coeff: 0.2
139
+ vertex_frames:
140
+ - wheel_2
141
+
142
+ interaction_contact_3:
143
+ type: VertexForce
144
+ frame: contact_3
145
+ fn_min: 100.0
146
+ enable_fc: true
147
+ friction_coeff: 0.2
148
+ vertex_frames:
149
+ - wheel_3
150
+
151
+ interaction_contact_4:
152
+ type: VertexForce
153
+ frame: contact_4
154
+ fn_min: 100.0
155
+ enable_fc: true
156
+ friction_coeff: 0.2
157
+ vertex_frames:
158
+ - wheel_4
159
+
160
+ contact_1:
161
+ type: Contact
162
+ subtask: [interaction_contact_1, zero_velocity_contact_1]
163
+
164
+ contact_2:
165
+ type: Contact
166
+ subtask: [interaction_contact_2, zero_velocity_contact_2]
167
+
168
+ contact_3:
169
+ type: Contact
170
+ subtask: [interaction_contact_3, zero_velocity_contact_3]
171
+
172
+ contact_4:
173
+ type: Contact
174
+ subtask: [interaction_contact_4, zero_velocity_contact_4]
175
+
176
+ joint_posture_capture:
177
+ type: Postural
178
+ weight: [25.0, 10.0, 10.0, 10.0,
179
+ 25.0, 10.0, 10.0, 10.0,
180
+ 25.0, 10.0, 10.0, 10.0,
181
+ 25.0, 10.0, 10.0, 10.0,
182
+ 55.0,
183
+ 25.0, 25.0, 25.0, 25.0, 25.0, 25.0,
184
+ 25.0, 25.0, 25.0, 25.0, 25.0, 25.0]
185
+ indices: [0, 1, 2, 3,
186
+ 4, 5, 6, 7,
187
+ 8, 9, 10, 11,
188
+ 12, 13, 14, 15,
189
+ 16,
190
+ 17, 18, 19, 20, 21, 22,
191
+ 23, 24, 25, 26, 27, 28]
192
+ nodes: ${range(N-5, N+1)}
193
+
194
+ v_regularization:
195
+ type: Regularization
196
+ variable_name: v
197
+ nodes: ${range(0, N+1)}
198
+ indices: [0, 1, 2, 3, 4, 5,
199
+ 6, 7, 8, 9,
200
+ 10, 11, 12, 13,
201
+ 14, 15, 16, 17,
202
+ 18, 19, 20, 21,
203
+ 22,
204
+ 23, 24, 25, 26, 27, 28,
205
+ 29, 30, 31, 32, 33, 34]
206
+ weight: [2e-1, 2e-1, 2e-1, 2e-1, 2e-1, 2e-1,
207
+ 8e-1, 8e-1, 8e-1, 8e-1,
208
+ 8e-1, 8e-1, 8e-1, 8e-1,
209
+ 8e-1, 8e-1, 8e-1, 8e-1,
210
+ 8e-1, 8e-1, 8e-1, 8e-1,
211
+ 8e-1,
212
+ 1.2, 1.2, 1.2, 1.2, 1.2, 1.2,
213
+ 1.2, 1.2, 1.2, 1.2, 1.2, 1.2]
214
+
215
+ a_regularization:
216
+ type: Regularization
217
+ variable_name: a
218
+ nodes: ${range(0, N+1)}
219
+ indices: [0, 1, 2, 3, 4, 5,
220
+ 6, 7, 8, 9,
221
+ 10, 11, 12, 13,
222
+ 14, 15, 16, 17,
223
+ 18, 19, 20, 21,
224
+ 22,
225
+ 23, 24, 25, 26, 27, 28,
226
+ 29, 30, 31, 32, 33, 34]
227
+ weight: [1e-1, 1e-1, 1e-1, 1e-1, 1e-1, 1e-1,
228
+ 2e-1, 2e-1, 2e-1, 2e-1,
229
+ 2e-1, 2e-1, 2e-1, 2e-1,
230
+ 2e-1, 2e-1, 2e-1, 2e-1,
231
+ 2e-1, 2e-1, 2e-1, 2e-1,
232
+ 2e-1,
233
+ 2e-1, 2e-1, 2e-1, 2e-1, 2e-1, 2e-1,
234
+ 2e-1, 2e-1, 2e-1, 2e-1, 2e-1, 2e-1]
235
+
236
+ # flight phase traj tracking
237
+ z_contact_1:
238
+ type: Cartesian
239
+ distal_link: contact_1
240
+ indices: [2]
241
+ cartesian_type: position
242
+ weight: *w_contact_z
243
+
244
+ z_contact_2:
245
+ type: Cartesian
246
+ distal_link: contact_2
247
+ indices: [2]
248
+ cartesian_type: position
249
+ weight: *w_contact_z
250
+
251
+ z_contact_3:
252
+ type: Cartesian
253
+ distal_link: contact_3
254
+ indices: [2]
255
+ cartesian_type: position
256
+ weight: *w_contact_z
257
+
258
+ z_contact_4:
259
+ type: Cartesian
260
+ distal_link: contact_4
261
+ indices: [2]
262
+ cartesian_type: position
263
+ weight: *w_contact_z
264
+
265
+ vz_contact_1:
266
+ type: Cartesian
267
+ base_link: base_link
268
+ distal_link: contact_1
269
+ indices: [2]
270
+ cartesian_type: velocity
271
+ weight: *w_contact_vz
272
+
273
+ vz_contact_2:
274
+ type: Cartesian
275
+ base_link: base_link
276
+ distal_link: contact_2
277
+ indices: [2]
278
+ cartesian_type: velocity
279
+ weight: *w_contact_vz
280
+
281
+ vz_contact_3:
282
+ type: Cartesian
283
+ base_link: base_link
284
+ distal_link: contact_3
285
+ indices: [2]
286
+ cartesian_type: velocity
287
+ weight: *w_contact_vz
288
+
289
+ vz_contact_4:
290
+ type: Cartesian
291
+ base_link: base_link
292
+ distal_link: contact_4
293
+ indices: [2]
294
+ cartesian_type: velocity
295
+ weight: *w_contact_vz
bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv_model ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3303cf6df03c15e74cc116dd689f2d1593719b4b24c67f22a3a6289b1cd2158a
3
+ size 4452447
bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/fake_pos_tracking_env.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ from typing import Dict
4
+
5
+ import torch
6
+
7
+ from EigenIPC.PyEigenIPC import VLevel
8
+
9
+ from mpc_hive.utilities.math_utils_torch import world2base_frame
10
+
11
+ from aug_mpc_envs.training_envs.twist_tracking_env import TwistTrackingEnv
12
+
13
+ class FakePosTrackingEnv(TwistTrackingEnv):
14
+ """Converts random planar position goals into twist references so the agent learns to drive the robot toward targets while managing contact scheduling."""
15
+
16
+ def __init__(self,
17
+ namespace: str,
18
+ actions_dim: int = 10,
19
+ verbose: bool = False,
20
+ vlevel: VLevel = VLevel.V1,
21
+ use_gpu: bool = True,
22
+ dtype: torch.dtype = torch.float32,
23
+ debug: bool = True,
24
+ override_agent_refs: bool = False,
25
+ timeout_ms: int = 60000,
26
+ env_opts: Dict = {}):
27
+
28
+ self._add_env_opt(env_opts, "max_distance", default=5.0) # [m]
29
+ self._add_env_opt(env_opts, "min_distance", default=0.0) # [m]
30
+ self._add_env_opt(env_opts, "max_vref", default=1.0) # [m/s]
31
+ self._add_env_opt(env_opts, "max_dp", default=5.0) # [m] after this, v ref saturates
32
+ self._add_env_opt(env_opts, "max_dt", default=env_opts["max_dp"]/ env_opts["max_vref"])
33
+
34
+ TwistTrackingEnv.__init__(self,
35
+ namespace=namespace,
36
+ actions_dim=actions_dim, # twist + contact flags
37
+ verbose=verbose,
38
+ vlevel=vlevel,
39
+ use_gpu=use_gpu,
40
+ dtype=dtype,
41
+ debug=debug,
42
+ override_agent_refs=override_agent_refs,
43
+ timeout_ms=timeout_ms,
44
+ env_opts=env_opts)
45
+
46
+ def get_file_paths(self):
47
+ paths=TwistTrackingEnv.get_file_paths(self)
48
+ paths.append(os.path.abspath(__file__))
49
+ return paths
50
+
51
+ def _custom_post_init(self):
52
+ TwistTrackingEnv._custom_post_init(self)
53
+
54
+ # position targets to be reached (wrt robot's pos at ep start)
55
+ self._p_trgt_w=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)[:, 0:2].detach().clone()
56
+ self._p_delta_w=self._p_trgt_w.detach().clone()
57
+ self._dp_norm=torch.zeros((self._n_envs, 1),dtype=self._dtype,device=self._device)
58
+ self._dp_versor=self._p_trgt_w.detach().clone()
59
+
60
+ self._trgt_d=torch.zeros((self._n_envs, 1),dtype=self._dtype,device=self._device)
61
+ self._trgt_theta=torch.zeros((self._n_envs, 1),dtype=self._dtype,device=self._device)
62
+
63
+ def _update_loc_twist_refs(self):
64
+ # this is called at each env substep
65
+
66
+ self._compute_twist_ref_w()
67
+
68
+ if not self._override_agent_refs:
69
+ agent_p_ref_current=self._agent_refs.rob_refs.root_state.get(data_type="p",
70
+ gpu=self._use_gpu)
71
+ agent_p_ref_current[:, 0:2]=self._p_trgt_w
72
+
73
+ # then convert it to base ref local for the agent
74
+ robot_q = self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
75
+ # rotate agent ref from world to robot base
76
+ world2base_frame(t_w=self._agent_twist_ref_current_w, q_b=robot_q,
77
+ t_out=self._agent_twist_ref_current_base_loc)
78
+ # write it to agent refs tensors
79
+ self._agent_refs.rob_refs.root_state.set(data_type="twist", data=self._agent_twist_ref_current_base_loc,
80
+ gpu=self._use_gpu)
81
+
82
+ def _compute_twist_ref_w(self, env_indxs: torch.Tensor = None):
83
+
84
+ # angular refs are not altered
85
+ if env_indxs is None:
86
+ # we update the position error using the current base position
87
+ self._p_delta_w[:, :]=self._p_trgt_w-\
88
+ self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)[:, 0:2]
89
+
90
+ self._dp_norm[:, :]=self._p_delta_w.norm(dim=1,keepdim=True)+1e-6
91
+ self._dp_versor[:, :]=self._p_delta_w/self._dp_norm
92
+
93
+ # apply for vref saturation
94
+ to_be_saturated=self._dp_norm[:, :]>self._env_opts["max_dp"]
95
+ self._dp_norm[to_be_saturated.flatten(), :]=self._env_opts["max_dp"]
96
+
97
+ # we compute the twist refs for the agent depending of the position error
98
+ self._agent_twist_ref_current_w[:, 0:2]=self._dp_norm*self._dp_versor/self._env_opts["max_dt"]
99
+ self._agent_twist_ref_current_w[:, 2:3]=0 # no vertical vel
100
+
101
+ # apply pof0 using last value of bernoully coeffs
102
+ self._agent_twist_ref_current_w[:, 0:3] = self._agent_twist_ref_current_w[:, 0:3]*self._bernoulli_coeffs_linvel # linvel
103
+ self._agent_twist_ref_current_w[:, 3:6] = self._agent_twist_ref_current_w[:, 3:6]*self._bernoulli_coeffs_omega # omega
104
+ else:
105
+ self._p_delta_w[env_indxs, :]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)[env_indxs, 0:2] -\
106
+ self._p_trgt_w[env_indxs, :]
107
+
108
+ # apply for vref saturation
109
+ to_be_saturated=torch.logical_and((self._dp_norm[:, :]>self._env_opts["max_dp"]).flatten(),env_indxs)
110
+ self._dp_norm[to_be_saturated.flatten(), :]=self._env_opts["max_dp"]
111
+
112
+ self._dp_norm[env_indxs, :]=self._p_delta_w[env_indxs, :].norm(dim=1,keepdim=True)+1e-6
113
+ self._dp_versor[env_indxs, :]=self._p_delta_w[env_indxs, :]/self._dp_norm[env_indxs, :]
114
+
115
+ self._agent_twist_ref_current_w[env_indxs, 0:2]=self._dp_norm[env_indxs, :]*self._dp_versor[env_indxs, :]/self._env_opts["max_dt"]
116
+ self._agent_twist_ref_current_w[env_indxs, 2:3]=0 # no vertical vel
117
+
118
+ # apply pof0 using last value of bernoully coeffs
119
+ self._agent_twist_ref_current_w[env_indxs, 0:3] = self._agent_twist_ref_current_w[env_indxs, 0:3]*self._bernoulli_coeffs_linvel[env_indxs, :]
120
+ self._agent_twist_ref_current_w[env_indxs, 3:6] = self._agent_twist_ref_current_w[env_indxs, 3:6]*self._bernoulli_coeffs_omega[env_indxs, :] # omega
121
+
122
+ def _override_refs(self,
123
+ env_indxs: torch.Tensor = None):
124
+
125
+ # runs at every post_step
126
+ self._agent_refs.rob_refs.root_state.synch_all(read=True,retry=True) # first read from mem
127
+ if self._use_gpu:
128
+ # copies latest refs to GPU
129
+ self._agent_refs.rob_refs.root_state.synch_mirror(from_gpu=False,non_blocking=False)
130
+
131
+ agent_p_ref_current=self._agent_refs.rob_refs.root_state.get(data_type="p",
132
+ gpu=self._use_gpu)
133
+
134
+ agent_yaw_omega_ref_current=self._agent_refs.rob_refs.root_state.get(data_type="omega",
135
+ gpu=self._use_gpu)
136
+
137
+ # self._p_trgt_w[:, :]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)[:, 0:2] + \
138
+ # agent_p_ref_current[:, 0:2]
139
+ self._p_trgt_w[:, :]=agent_p_ref_current[:, 0:2] # set p target target from shared mem
140
+
141
+ self._agent_twist_ref_current_w[:, 5:6]=agent_yaw_omega_ref_current[:, 2:3] # set yaw ang. vel target from shared mem
142
+
143
+ def _debug_agent_refs(self):
144
+ if self._use_gpu:
145
+ self._agent_refs.rob_refs.root_state.synch_mirror(from_gpu=True,non_blocking=False)
146
+ self._agent_refs.rob_refs.root_state.synch_all(read=False, retry = True)
147
+
148
+ def _randomize_task_refs(self,
149
+ env_indxs: torch.Tensor = None):
150
+
151
+ # we randomize the target position/omega in world frame
152
+ if env_indxs is None:
153
+ self._trgt_d.uniform_(self._env_opts["min_distance"], self._env_opts["max_distance"])
154
+ self._trgt_theta.uniform_(0.0, 2*torch.pi)
155
+
156
+ self._p_trgt_w[:, :]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)[:, 0:2] +\
157
+ torch.cat((self._trgt_d*torch.cos(self._trgt_theta)
158
+ ,self._trgt_d*torch.sin(self._trgt_theta)), dim=1)
159
+
160
+ # randomize just omega
161
+ random_uniform=torch.full_like(self._agent_twist_ref_current_w[:, 3:6], fill_value=0.0)
162
+ torch.nn.init.uniform_(random_uniform, a=-1, b=1)
163
+ self._agent_twist_ref_current_w[:, 3:6] = random_uniform*self._twist_ref_scale[:, 3:6] + self._twist_ref_offset[:, 3:6]
164
+
165
+ # sample for all envs pof0
166
+ if self._env_opts["use_pof0"]: # sample from bernoulli distribution and update coefficients
167
+ torch.bernoulli(input=self._pof1_b_linvel,out=self._bernoulli_coeffs_linvel) # by default bernoulli_coeffs are 1 if not self._env_opts["use_pof0"]
168
+ torch.bernoulli(input=self._pof1_b_omega,out=self._bernoulli_coeffs_omega)
169
+
170
+ else:
171
+
172
+ if env_indxs.any():
173
+ integer_idxs=torch.nonzero(env_indxs).flatten()
174
+
175
+ trgt_d_selected=self._trgt_d[integer_idxs, :]
176
+ trgt_d_selected.uniform_(self._env_opts["min_distance"], self._env_opts["max_distance"])
177
+ self._trgt_d[integer_idxs, :]=trgt_d_selected
178
+
179
+ trgt_theta_selected=self._trgt_theta[integer_idxs, :]
180
+ trgt_theta_selected.uniform_(0.0, 2*torch.pi)
181
+ self._trgt_theta[integer_idxs, :]=trgt_theta_selected
182
+
183
+ self._p_trgt_w[integer_idxs, 0:1]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)[integer_idxs, 0:1] +\
184
+ self._trgt_d[integer_idxs, :]*torch.cos(self._trgt_theta[integer_idxs, :])
185
+ self._p_trgt_w[integer_idxs, 1:2]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)[integer_idxs, 1:2] +\
186
+ self._trgt_d[integer_idxs, :]*torch.sin(self._trgt_theta[integer_idxs, :])
187
+
188
+ # randomize just omega
189
+ random_uniform=torch.full_like(self._agent_twist_ref_current_w[env_indxs, 3:6], fill_value=0.0)
190
+ torch.nn.init.uniform_(random_uniform, a=-1, b=1)
191
+ self._agent_twist_ref_current_w[env_indxs, 3:6] = random_uniform*self._twist_ref_scale[:, 3:6] + self._twist_ref_offset[:, 3:6]
192
+
193
+ # sample for all envs pof0, then reset to 1 for envs which are not to be randomized
194
+ if self._env_opts["use_pof0"]: # sample from bernoulli distribution and update coefficients
195
+ torch.bernoulli(input=self._pof1_b_linvel,out=self._bernoulli_coeffs_linvel) # by default bernoulli_coeffs are 1 if not self._env_opts["use_pof0"]
196
+ torch.bernoulli(input=self._pof1_b_omega,out=self._bernoulli_coeffs_omega)
197
+ self._bernoulli_coeffs_linvel[~env_indxs, :]=1
198
+ self._bernoulli_coeffs_omega[~env_indxs, :]=1
199
+
200
+ self._compute_twist_ref_w(env_indxs=env_indxs) # update linear vel twist refs based on pos error
201
+
202
+
bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/hybrid_quad_rhc.py ADDED
@@ -0,0 +1,1324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mpc_hive.controllers.rhc import RHController
2
+
3
+ from aug_mpc.controllers.rhc.horizon_based.horizon_imports import *
4
+
5
+ from aug_mpc.controllers.rhc.horizon_based.hybrid_quad_rhc_refs import HybridQuadRhcRefs
6
+ from aug_mpc.controllers.rhc.horizon_based.gait_manager import GaitManager
7
+
8
+ from EigenIPC.PyEigenIPC import VLevel
9
+ from EigenIPC.PyEigenIPC import Journal, LogType
10
+
11
+ import numpy as np
12
+
13
+ import os
14
+
15
+ import time
16
+
17
+ from typing import Dict, List
18
+
19
+ class HybridQuadRhc(RHController):
20
+
21
+ def __init__(self,
22
+ srdf_path: str,
23
+ urdf_path: str,
24
+ config_path: str,
25
+ robot_name: str, # used for shared memory namespaces
26
+ codegen_dir: str,
27
+ n_nodes:float = 25,
28
+ injection_node:int = 10,
29
+ dt: float = 0.02,
30
+ max_solver_iter = 1, # defaults to rt-iteration
31
+ open_loop: bool = True,
32
+ close_loop_all: bool = False,
33
+ dtype = np.float32,
34
+ verbose = False,
35
+ debug = False,
36
+ refs_in_hor_frame = True,
37
+ timeout_ms: int = 60000,
38
+ custom_opts: Dict = {}):
39
+
40
+ self._refs_in_hor_frame = refs_in_hor_frame
41
+
42
+ self._injection_node = injection_node
43
+
44
+ self._open_loop = open_loop
45
+ self._close_loop_all = close_loop_all
46
+
47
+ self._codegen_dir = codegen_dir
48
+ if not os.path.exists(self._codegen_dir):
49
+ os.makedirs(self._codegen_dir)
50
+ # else:
51
+ # # Directory already exists, delete it and recreate
52
+ # shutil.rmtree(self._codegen_dir)
53
+ # os.makedirs(self._codegen_dir)
54
+
55
+ self.step_counter = 0
56
+ self.sol_counter = 0
57
+
58
+ self.max_solver_iter = max_solver_iter
59
+
60
+ self._timer_start = time.perf_counter()
61
+ self._prb_update_time = time.perf_counter()
62
+ self._phase_shift_time = time.perf_counter()
63
+ self._task_ref_update_time = time.perf_counter()
64
+ self._rti_time = time.perf_counter()
65
+
66
+ self.robot_name = robot_name
67
+
68
+ self.config_path = config_path
69
+
70
+ self.urdf_path = urdf_path
71
+ # read urdf and srdf files
72
+ with open(self.urdf_path, 'r') as file:
73
+ self.urdf = file.read()
74
+ self._base_init = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0])
75
+
76
+ self._c_timelines = dict()
77
+ self._f_reg_timelines = dict()
78
+
79
+ self._custom_opts={"replace_continuous_joints": False,
80
+ "use_force_feedback": False,
81
+ "lin_a_feedback": False,
82
+ "is_open_loop": self._open_loop, # fully open (just for db)
83
+ "fully_closed": False, # closed loop with full feedback (just for db)
84
+ "closed_partial": False, # closed loop with partial feedback
85
+ "adaptive_is": True, # closed loop with adaptation
86
+ "estimate_v_root": False, # when adaptive_is or closed_partial, estimate vbase
87
+ "alpha_from_outside": False, # alpha set ext. from shared memory
88
+ "alpha_half": 1.0,
89
+ "only_vel_wheels": True, # whether wheels (if present) are just vel controlled
90
+ "use_jnt_v_feedback": False
91
+ }
92
+
93
+ self._custom_opts.update(custom_opts)
94
+
95
+ self._alpha_half=self._custom_opts["alpha_half"]
96
+
97
+ if self._open_loop:
98
+ self._custom_opts["fully_closed"]=False
99
+ self._custom_opts["adaptive_is"]=False
100
+ self._custom_opts["closed_partial"]=False
101
+ else:
102
+ self._custom_opts["is_open_loop"]=False
103
+ if self._custom_opts["fully_closed"]:
104
+ self._custom_opts["adaptive_is"]=False
105
+ self._custom_opts["closed_partial"]=False
106
+ self._custom_opts["lin_a_feedback"]=False
107
+ if self._custom_opts["closed_partial"]:
108
+ self._custom_opts["adaptive_is"]=False
109
+ self._custom_opts["fully_closed"]=False
110
+ self._custom_opts["lin_a_feedback"]=False
111
+ if self._custom_opts["adaptive_is"]:
112
+ self._custom_opts["closed_partial"]=False
113
+ self._custom_opts["fully_closed"]=False
114
+
115
+ super().__init__(srdf_path=srdf_path,
116
+ n_nodes=n_nodes,
117
+ dt=dt,
118
+ namespace=self.robot_name,
119
+ dtype=dtype,
120
+ verbose=verbose,
121
+ debug=debug,
122
+ timeout_ms=timeout_ms)
123
+
124
+ self._rhc_fpaths.append(self.config_path)
125
+
126
+ def _config_override(self):
127
+ pass
128
+
129
+ def _post_problem_init(self):
130
+
131
+ self.rhc_costs={}
132
+ self.rhc_constr={}
133
+
134
+ self._fail_idx_scale=0.0
135
+ self._expl_idx_window_size=int(1*self._n_nodes)
136
+ self._explosion_idx_buffer=np.zeros((1,self._expl_idx_window_size))
137
+ self._expl_idx_counter=0
138
+ self._expl_idx_buffer_counter=0
139
+
140
+ self._pred_node_idx=self._n_nodes-1
141
+
142
+ self._nq=self.nq()
143
+ self._nq_jnts=self._nq-7# assuming floating base
144
+ self._nv=self.nv()
145
+ self._nv_jnts=self._nv-6
146
+
147
+ self._alphas_q_root=np.zeros((7, 1), dtype=self._dtype)
148
+ self._alphas_q_jnts=np.zeros((self._nq_jnts, 1), dtype=self._dtype)
149
+ self._alphas_twist_root=np.zeros((6, 1), dtype=self._dtype)
150
+ self._alphas_v_jnts=np.zeros((self._nv_jnts, 1), dtype=self._dtype)
151
+ self._alphas_a=np.zeros((self._nv, 1), dtype=self._dtype)
152
+ self._alphas_q_root[:, :]=1.0 # default to all open
153
+ self._alphas_q_jnts[:, :]=1.0
154
+ self._alphas_twist_root[:, :]=1.0
155
+ self._alphas_v_jnts[:, :]=1.0
156
+ self._alphas_a[:, :]=1.0
157
+
158
+ def _init_problem(self,
159
+ fixed_jnt_patterns: List[str] = None,
160
+ wheels_patterns: List[str] = None,
161
+ foot_linkname: str = None,
162
+ flight_duration: int = 10,
163
+ post_flight_stance: int = 3,
164
+ step_height: float = 0.12,
165
+ keep_yaw_vert: bool = False,
166
+ yaw_vertical_weight: float = 2.0,
167
+ vertical_landing: bool = False,
168
+ vertical_land_weight: float = 1.0,
169
+ phase_force_reg: float = 1e-2,
170
+ vel_bounds_weight: float = 1.0):
171
+
172
+ self._fixed_jnt_patterns=fixed_jnt_patterns
173
+
174
+ self._config_override()
175
+
176
+ Journal.log(self.__class__.__name__,
177
+ "_init_problem",
178
+ f" Will use horizon config file at {self.config_path}",
179
+ LogType.INFO,
180
+ throw_when_excep=True)
181
+
182
+ self._vel_bounds_weight=vel_bounds_weight
183
+ self._phase_force_reg=phase_force_reg
184
+ self._yaw_vertical_weight=yaw_vertical_weight
185
+ self._vertical_land_weight=vertical_land_weight
186
+ # overrides parent
187
+ self._prb = Problem(self._n_intervals,
188
+ receding=True,
189
+ casadi_type=cs.SX)
190
+ self._prb.setDt(self._dt)
191
+
192
+ if "replace_continuous_joints" in self._custom_opts:
193
+ # continous joints are parametrized in So2
194
+ if self._custom_opts["replace_continuous_joints"]:
195
+ self.urdf = self.urdf.replace('continuous', 'revolute')
196
+ else:
197
+ self.urdf = self.urdf.replace('continuous', 'revolute')
198
+
199
+ self._kin_dyn = casadi_kin_dyn.CasadiKinDyn(self.urdf) # used for getting joint names
200
+ self._assign_controller_side_jnt_names(jnt_names=self._get_robot_jnt_names())
201
+
202
+ self._init_robot_homer()
203
+
204
+ # handle fixed joints
205
+ fixed_joint_map={}
206
+ if self._fixed_jnt_patterns is not None:
207
+ for jnt_name in self._get_robot_jnt_names():
208
+ for fixed_jnt_pattern in self._fixed_jnt_patterns:
209
+ if fixed_jnt_pattern in jnt_name:
210
+ fixed_joint_map.update({f"{jnt_name}":
211
+ self._homer.get_homing_val(jnt_name=jnt_name)})
212
+ break # do not search for other pattern matches
213
+
214
+ if not len(fixed_joint_map)==0: # we need to recreate kin dyn and homers
215
+ Journal.log(self.__class__.__name__,
216
+ "_init_problem",
217
+ f"Will fix following joints: \n{str(fixed_joint_map)}",
218
+ LogType.INFO,
219
+ throw_when_excep=True)
220
+ # with the fixed joint map
221
+ self._kin_dyn = casadi_kin_dyn.CasadiKinDyn(self.urdf,fixed_joints=fixed_joint_map)
222
+ # assign again controlled joints names
223
+ self._assign_controller_side_jnt_names(jnt_names=self._get_robot_jnt_names())
224
+ # updated robot homer for controlled joints
225
+ self._init_robot_homer()
226
+
227
+ # handle continuous joints (need to change homing and retrieve
228
+ # cont jnts indexes) and homing
229
+ self._continuous_joints=self._get_continuous_jnt_names()
230
+ # reduced
231
+ self._continuous_joints_idxs=[]
232
+ self._continuous_joints_idxs_cos=[]
233
+ self._continuous_joints_idxs_sin=[]
234
+ self._continuous_joints_idxs_red=[]
235
+ self._rev_joints_idxs=[]
236
+ self._rev_joints_idxs_red=[]
237
+ # qfull
238
+ self._continuous_joints_idxs_qfull=[]
239
+ self._continuous_joints_idxs_cos_qfull=[]
240
+ self._continuous_joints_idxs_sin_qfull=[]
241
+ self._continuous_joints_idxs_red_qfull=[]
242
+ self._rev_joints_idxs_qfull=[]
243
+ self._rev_joints_idxs_red_qfull=[]
244
+ jnt_homing=[""]*(len(self._homer.get_homing())+len(self._continuous_joints))
245
+ jnt_names=self._get_robot_jnt_names()
246
+ for i in range(len(jnt_names)):
247
+ jnt=jnt_names[i]
248
+ index=self._get_jnt_id(jnt)# accounting for floating joint
249
+ homing_idx=index-7 # homing is only for actuated joints
250
+ homing_value=self._homer.get_homing_val(jnt)
251
+ if jnt in self._continuous_joints:
252
+ jnt_homing[homing_idx]=np.cos(homing_value).item()
253
+ jnt_homing[homing_idx+1]=np.sin(homing_value).item()
254
+ # just actuated joints
255
+ self._continuous_joints_idxs.append(homing_idx) # cos
256
+ self._continuous_joints_idxs.append(homing_idx+1) # sin
257
+ self._continuous_joints_idxs_cos.append(homing_idx)
258
+ self._continuous_joints_idxs_sin.append(homing_idx+1)
259
+ self._continuous_joints_idxs_red.append(i)
260
+ # q full
261
+ self._continuous_joints_idxs_qfull.append(index) # cos
262
+ self._continuous_joints_idxs_qfull.append(index+1) # sin
263
+ self._continuous_joints_idxs_cos_qfull.append(index)
264
+ self._continuous_joints_idxs_sin_qfull.append(index+1)
265
+ self._continuous_joints_idxs_red_qfull.append(i+7)
266
+ else:
267
+ jnt_homing[homing_idx]=homing_value
268
+ # just actuated joints
269
+ self._rev_joints_idxs.append(homing_idx)
270
+ self._rev_joints_idxs_red.append(i)
271
+ # q full
272
+ self._rev_joints_idxs_qfull.append(index)
273
+ self._rev_joints_idxs_red_qfull.append(i+7)
274
+
275
+ self._jnts_q_reduced=None
276
+ if not len(self._continuous_joints)==0:
277
+ cont_joints=", ".join(self._continuous_joints)
278
+
279
+ Journal.log(self.__class__.__name__,
280
+ "_init_problem",
281
+ f"The following continuous joints were found: \n{cont_joints}",
282
+ LogType.INFO,
283
+ throw_when_excep=True)
284
+ # preallocating data
285
+ self._jnts_q_reduced=np.zeros((1,self.nv()-6),dtype=self._dtype)
286
+ self._jnts_q_expanded=np.zeros((self.nq()-7,1),dtype=self._dtype)
287
+ self._full_q_reduced=np.zeros((7+len(jnt_names), self._n_nodes),dtype=self._dtype)
288
+ self._jnts_q_delta_expanded=np.zeros((self.nq()-7,1),dtype=self._dtype)
289
+ else:
290
+ self._custom_opts["replace_continuous_joints"]=True
291
+ Journal.log(self.__class__.__name__,
292
+ "_init_problem",
293
+ f"No continuous joints were found.",
294
+ LogType.INFO,
295
+ throw_when_excep=True)
296
+
297
+ # retrieve wheels indexes (not considering continuous joints,
298
+ # ok just for v, eff, etc..)
299
+ self._wheel_patterns=wheels_patterns
300
+ self._wheels_idxs_v=self._get_wheels_jnt_v_idxs(wheel_patterns=self._wheel_patterns)
301
+ self._f0 = [0, 0, self._kin_dyn.mass()/4*9.81]
302
+
303
+ # we can create an init for the base
304
+ init = self._base_init.tolist() + jnt_homing
305
+
306
+ if foot_linkname is not None:
307
+ FK = self._kin_dyn.fk(foot_linkname) # just to get robot reference height
308
+ ground_level = FK(q=init)['ee_pos']
309
+ self._base_init[2] = -ground_level[2] # override init
310
+
311
+ self._model = FullModelInverseDynamics(problem=self._prb,
312
+ kd=self._kin_dyn,
313
+ q_init=self._homer.get_homing_map(),
314
+ base_init=self._base_init)
315
+
316
+ self._ti = TaskInterface(prb=self._prb,
317
+ model=self._model,
318
+ max_solver_iter=self.max_solver_iter,
319
+ debug = self._debug,
320
+ verbose = self._verbose,
321
+ codegen_workdir = self._codegen_dir)
322
+ self._ti.setTaskFromYaml(self.config_path)
323
+
324
+ # setting initial base pos ref if exists
325
+ base_pos = self._ti.getTask('base_height')
326
+ if base_pos is not None:
327
+ base_pos.setRef(np.atleast_2d(self._base_init).T)
328
+
329
+ self._pm = pymanager.PhaseManager(self._n_nodes, debug=False) # intervals or nodes?????
330
+
331
+ self._gm = GaitManager(self._ti,
332
+ self._pm,
333
+ self._injection_node,
334
+ keep_yaw_vert=keep_yaw_vert,
335
+ yaw_vertical_weight=self._yaw_vertical_weight,
336
+ vertical_landing=vertical_landing,
337
+ landing_vert_weight=self._vertical_land_weight,
338
+ phase_force_reg=self._phase_force_reg,
339
+ custom_opts=self._custom_opts,
340
+ flight_duration=flight_duration,
341
+ post_flight_stance=post_flight_stance,
342
+ step_height=step_height,
343
+ dh=0.0)
344
+
345
+ self._ti.model.q.setBounds(self._ti.model.q0, self._ti.model.q0, nodes=0)
346
+ self._ti.model.v.setBounds(self._ti.model.v0, self._ti.model.v0, nodes=0)
347
+ self._ti.model.q.setInitialGuess(self._ti.model.q0)
348
+ self._ti.model.v.setInitialGuess(self._ti.model.v0)
349
+ for _, cforces in self._ti.model.cmap.items():
350
+ n_contact_f=len(cforces)
351
+ for c in cforces:
352
+ c.setInitialGuess(np.array(self._f0)/n_contact_f)
353
+
354
+ vel_lims = self._model.kd.velocityLimits()
355
+ import horizon.utils as utils
356
+ self._prb.createResidual('vel_lb_barrier', self._vel_bounds_weight*utils.utils.barrier(vel_lims[7:] - self._model.v[7:]))
357
+ self._prb.createResidual('vel_ub_barrier', self._vel_bounds_weight*utils.utils.barrier1(-1 * vel_lims[7:] - self._model.v[7:]))
358
+
359
+ self._meas_lin_a_par=None
360
+ # if self._custom_opts["lin_a_feedback"]:
361
+ # # acceleration feedback on first node
362
+ # self._meas_lin_a_par=self._prb.createParameter(name="lin_a_feedback",
363
+ # dim=3, nodes=0)
364
+ # base_lin_a_prb=self._prb.getInput().getVars()[0:3]
365
+ # self._prb.createConstraint('lin_acceleration_feedback', base_lin_a_prb - self._meas_lin_a_par,
366
+ # nodes=[0])
367
+
368
+ # if not self._open_loop:
369
+ # # we create a residual cost to be used as an attractor to the measured state on the first node
370
+ # # hard constraints injecting meas. states are pure EVIL!
371
+ # prb_state=self._prb.getState()
372
+ # full_state=prb_state.getVars()
373
+ # state_dim=prb_state.getBounds()[0].shape[0]
374
+ # meas_state=self._prb.createParameter(name="measured_state",
375
+ # dim=state_dim, nodes=0)
376
+ # self._prb.createResidual('meas_state_attractor', meas_state_attractor_weight * (full_state - meas_state),
377
+ # nodes=[0])
378
+
379
+ self._ti.finalize()
380
+ self._ti.bootstrap()
381
+
382
+ self._ti.init_inv_dyn_for_res() # we initialize some objects for sol. postprocessing purposes
383
+ self._ti.load_initial_guess()
384
+
385
+ self.n_dofs = self._get_ndofs() # after loading the URDF and creating the controller we
386
+ # know n_dofs -> we assign it (by default = None)
387
+
388
+ self.n_contacts = len(self._model.cmap.keys())
389
+
390
+ # remove variables bounds (before they were necessary to have a good
391
+ # quality bootstrap solution)
392
+ self._q_inf=np.zeros((self.nq(), 1))
393
+ self._q_inf[:, :]=np.inf
394
+ self._v_inf=np.zeros((self.nv(), 1))
395
+ self._v_inf[:, :]=np.inf
396
+ self._ti.model.q.setBounds(-self._q_inf, self._q_inf, nodes=0)
397
+ self._ti.model.v.setBounds(-self._v_inf, self._v_inf, nodes=0)
398
+
399
+ # self.horizon_anal = analyzer.ProblemAnalyzer(self._prb)
400
+
401
+ def get_file_paths(self):
402
+ # can be overriden by child
403
+ paths = super().get_file_paths()
404
+ return paths
405
+
406
+ def _get_quat_remap(self):
407
+ # overrides parent
408
+ return [1, 2, 3, 0] # mapping from robot quat. to Horizon's quaternion convention
409
+
410
+ def _zmp(self, model):
411
+
412
+ num = cs.SX([0, 0])
413
+ den = cs.SX([0])
414
+ pos_contact = dict()
415
+ force_val = dict()
416
+
417
+ q = cs.SX.sym('q', model.nq)
418
+ v = cs.SX.sym('v', model.nv)
419
+ a = cs.SX.sym('a', model.nv)
420
+
421
+ com = model.kd.centerOfMass()(q=q, v=v, a=a)['com']
422
+
423
+ n = cs.SX([0, 0, 1])
424
+ for c in model.fmap.keys():
425
+ pos_contact[c] = model.kd.fk(c)(q=q)['ee_pos']
426
+ force_val[c] = cs.SX.sym('force_val', 3)
427
+ num += (pos_contact[c][0:2] - com[0:2]) * cs.dot(force_val[c], n)
428
+ den += cs.dot(force_val[c], n)
429
+
430
+ zmp = com[0:2] + (num / den)
431
+ input_list = []
432
+ input_list.append(q)
433
+ input_list.append(v)
434
+ input_list.append(a)
435
+
436
+ for elem in force_val.values():
437
+ input_list.append(elem)
438
+
439
+ f = cs.Function('zmp', input_list, [zmp])
440
+ return f
441
+
442
+ def _add_zmp(self):
443
+
444
+ input_zmp = []
445
+
446
+ input_zmp.append(self._model.q)
447
+ input_zmp.append(self._model.v)
448
+ input_zmp.append(self._model.a)
449
+
450
+ for f_var in self._model.fmap.values():
451
+ input_zmp.append(f_var)
452
+
453
+ c_mean = cs.SX([0, 0, 0])
454
+ for c_name, f_var in self._model.fmap.items():
455
+ fk_c_pos = self._kin_dyn.fk(c_name)(q=self._model.q)['ee_pos']
456
+ c_mean += fk_c_pos
457
+
458
+ c_mean /= len(self._model.cmap.keys())
459
+
460
+ zmp_nominal_weight = 10.
461
+ zmp_fun = self._zmp(self._model)(*input_zmp)
462
+
463
+ if 'wheel_joint_1' in self._model.kd.joint_names():
464
+ zmp_residual = self._prb.createIntermediateResidual('zmp', zmp_nominal_weight * (zmp_fun[0:2] - c_mean[0:2]))
465
+
466
+ def _quaternion_multiply(self,
467
+ q1, q2):
468
+ x1, y1, z1, w1 = q1
469
+ x2, y2, z2, w2 = q2
470
+ w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2
471
+ x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
472
+ y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2
473
+ z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2
474
+ return np.array([x, y, z, w])
475
+
476
+ def _get_continuous_jnt_names(self):
477
+ import xml.etree.ElementTree as ET
478
+ root = ET.fromstring(self.urdf)
479
+ continuous_joints = []
480
+ for joint in root.findall('joint'):
481
+ joint_type = joint.get('type')
482
+ if joint_type == 'continuous':
483
+ joint_name = joint.get('name')
484
+ continuous_joints.append(joint_name)
485
+ return continuous_joints
486
+
487
+ def _get_wheels_jnt_v_idxs(self, wheel_patterns: List[str]):
488
+ jnt_names=self._get_robot_jnt_names()
489
+ wheels_idxs=[]
490
+ for i in range(len(jnt_names)):
491
+ jnt_name=jnt_names[i]
492
+ for wheel_pattern in wheel_patterns:
493
+ if wheel_pattern in jnt_name:
494
+ wheels_idxs.append(i)
495
+ break
496
+ return wheels_idxs
497
+
498
+ def _get_jnt_id(self, jnt_name):
499
+ return self._kin_dyn.joint_iq(jnt_name)
500
+
501
+ def _init_rhc_task_cmds(self):
502
+
503
+ rhc_refs = HybridQuadRhcRefs(gait_manager=self._gm,
504
+ robot_index_shm=self.controller_index,
505
+ robot_index_view=0, # when using optimize_mem the view if always of shape 1x...
506
+ namespace=self.namespace,
507
+ safe=False,
508
+ verbose=self._verbose,
509
+ vlevel=VLevel.V2,
510
+ use_force_feedback=self._custom_opts["use_force_feedback"],
511
+ optimize_mem=True)
512
+
513
+ rhc_refs.run()
514
+
515
+ rhc_refs.rob_refs.set_jnts_remapping(jnts_remapping=self._to_controller)
516
+ rhc_refs.rob_refs.set_q_remapping(q_remapping=self._get_quat_remap())
517
+
518
+ rhc_refs.set_default_refs(p_ref=np.atleast_2d(self._base_init)[:, 0:3],
519
+ q_ref=np.atleast_2d(self._base_init)[:, 3:7])
520
+
521
+ return rhc_refs
522
+
523
+ def get_vertex_fnames_from_ti(self):
524
+ tasks=self._ti.task_list
525
+ contact_f_names=[]
526
+ for task in tasks:
527
+ if isinstance(task, ContactTask):
528
+ interaction_task=task.dynamics_tasks[0]
529
+ contact_f_names.append(interaction_task.vertex_frames[0])
530
+ return contact_f_names
531
+
532
+ def _get_contact_names(self):
533
+ # should get contact names from vertex frames
534
+ # list(self._ti.model.cmap.keys())
535
+ return self.get_vertex_fnames_from_ti()
536
+
537
+ def _get_robot_jnt_names(self):
538
+
539
+ joints_names = self._kin_dyn.joint_names()
540
+ to_be_removed = ["universe",
541
+ "reference",
542
+ "world",
543
+ "floating",
544
+ "floating_base"]
545
+ for name in to_be_removed:
546
+ if name in joints_names:
547
+ joints_names.remove(name)
548
+
549
+ return joints_names
550
+
551
+ def _get_ndofs(self):
552
+ return len(self._model.joint_names)
553
+
554
+ def nq(self):
555
+ return self._kin_dyn.nq()
556
+
557
+ def nv(self):
558
+ return self._kin_dyn.nv()
559
+
560
+ def _get_robot_mass(self):
561
+
562
+ return self._kin_dyn.mass()
563
+
564
+ def _get_root_full_q_from_sol(self, node_idx=1):
565
+
566
+ root_q_full=self._ti.solution['q'][0:7, node_idx].reshape(1, 7).astype(self._dtype)
567
+
568
+ np.nan_to_num(root_q_full, nan=1e3, posinf=1e3, neginf=-1e3, copy=False)
569
+ np.clip(a=root_q_full, a_min=-1e3, a_max=1e3, out=root_q_full)
570
+
571
+ return root_q_full
572
+
573
+ def _get_full_q_from_sol(self, node_idx=1):
574
+
575
+ return self._ti.solution['q'][:, node_idx].reshape(1, -1).astype(self._dtype)
576
+
577
+ def _get_root_twist_from_sol(self, node_idx=1):
578
+ # provided in world frame
579
+ twist_base_local=self._get_v_from_sol()[0:6, node_idx].reshape(1, 6)
580
+ # if world_aligned:
581
+ # q_root_rhc = self._get_root_full_q_from_sol(node_idx=node_idx)[:, 0:4]
582
+ # r_base_rhc=Rotation.from_quat(q_root_rhc.flatten()).as_matrix()
583
+ # twist_base_local[:, 0:3] = r_base_rhc @ twist_base_local[0, 0:3]
584
+ # twist_base_local[:, 3:6] = r_base_rhc @ twist_base_local[0, 3:6]
585
+ return twist_base_local
586
+
587
+ def _get_root_a_from_sol(self, node_idx=0):
588
+ # provided in world frame
589
+ a_base_local=self._get_a_from_sol()[0:6, node_idx].reshape(1, 6)
590
+ # if world_aligned:
591
+ # q_root_rhc = self._get_root_full_q_from_sol(node_idx=node_idx)[:, 0:4]
592
+ # r_base_rhc=Rotation.from_quat(q_root_rhc.flatten()).as_matrix()
593
+ # a_base_local[:, 0:3] = r_base_rhc @ a_base_local[0, 0:3]
594
+ # a_base_local[:, 3:6] = r_base_rhc @ v[0, 3:6]
595
+ return a_base_local
596
+
597
+ def _get_jnt_q_from_sol(self, node_idx=0,
598
+ reduce: bool = True,
599
+ clamp: bool = True):
600
+
601
+ full_jnts_q=self._ti.solution['q'][7:, node_idx:node_idx+1].reshape(1,-1).astype(self._dtype)
602
+
603
+ np.nan_to_num(full_jnts_q, nan=1e3, posinf=1e3, neginf=-1e3, copy=False) # in place
604
+ np.clip(a=full_jnts_q, a_max=1e3, a_min=-1e3, out=full_jnts_q) # in place
605
+
606
+ if self._custom_opts["replace_continuous_joints"] or (not reduce):
607
+ if clamp:
608
+ return np.fmod(full_jnts_q, 2*np.pi)
609
+ else:
610
+ return full_jnts_q
611
+ else:
612
+ cos_sin=full_jnts_q[:,self._continuous_joints_idxs].reshape(-1,2)
613
+ # copy rev joint vals
614
+ self._jnts_q_reduced[:, self._rev_joints_idxs_red]=np.fmod(full_jnts_q[:, self._rev_joints_idxs], 2*np.pi).reshape(1, -1)
615
+ # and continuous
616
+ self._jnts_q_reduced[:, self._continuous_joints_idxs_red]=np.arctan2(cos_sin[:, 1], cos_sin[:, 0]).reshape(1,-1)
617
+ return self._jnts_q_reduced
618
+
619
+ def _get_jnt_v_from_sol(self, node_idx=1):
620
+
621
+ jnt_v_sol=self._get_v_from_sol()[6:, node_idx].reshape(1,
622
+ self._nv_jnts)
623
+ np.nan_to_num(jnt_v_sol, nan=1e5, posinf=1e5, neginf=-1e5, copy=False) # in place
624
+ # np.clip(a=jnt_v_sol, a_max=1e5, a_min=-1e5, out=jnt_v_sol) # in place
625
+
626
+ return jnt_v_sol
627
+
628
+ def _get_jnt_a_from_sol(self, node_idx=1):
629
+
630
+ return self._get_a_from_sol()[6:, node_idx].reshape(1,
631
+ self._nv_jnts)
632
+
633
+ def _get_jnt_eff_from_sol(self, node_idx=0):
634
+
635
+ efforts_on_node = self._ti.eval_efforts_on_node(node_idx=node_idx)
636
+
637
+ # if self._custom_opts["only_vel_wheels"]:
638
+
639
+ jnt_efforts=efforts_on_node[6:, 0]
640
+
641
+ if self._custom_opts["only_vel_wheels"] and self._wheels_idxs_v:
642
+ jnt_efforts[self._wheels_idxs_v]=0.0
643
+
644
+ return jnt_efforts.reshape(1,
645
+ self._nv_jnts).astype(self._dtype)
646
+
647
+ def _get_rhc_cost(self):
648
+
649
+ return self._ti.solution["opt_cost"]
650
+
651
+ def _get_rhc_constr_viol(self):
652
+
653
+ return self._ti.solution["residual_norm"]
654
+
655
+ def _get_rhc_nodes_cost(self):
656
+
657
+ cost = self._ti.solver_rti.getCostValOnNodes()
658
+ return cost.reshape((1, -1))
659
+
660
+ def _get_rhc_nodes_constr_viol(self):
661
+
662
+ constr_viol = self._ti.solver_rti.getConstrValOnNodes()
663
+ return constr_viol.reshape((1, -1))
664
+
665
+ def _get_rhc_niter_to_sol(self):
666
+
667
+ return self._ti.solution["n_iter2sol"]
668
+
669
+ def _set_ig_bootstrap(self,
670
+ q_state: np.ndarray = None,
671
+ v_state: np.ndarray = None):
672
+
673
+ xig = self._ti.solution['x_opt'].copy()
674
+ uig = self._ti.solution['u_opt'].copy()
675
+
676
+ # Normalize and keep quaternion in the same hemisphere as the previous
677
+ # solution to avoid artificial 180-deg jumps in the bootstrap warm start.
678
+ q_state_boot = q_state.copy()
679
+ q_prev = xig[3:7, 0]
680
+ q_now = q_state_boot[3:7, 0]
681
+
682
+ q_now_norm = np.linalg.norm(q_now)
683
+ if q_now_norm > 1e-9:
684
+ q_state_boot[3:7, :] /= q_now_norm
685
+ else:
686
+ q_state_boot[3:7, :] = np.array([[0.0], [0.0], [0.0], [1.0]], dtype=self._dtype)
687
+
688
+ q_prev_norm = np.linalg.norm(q_prev)
689
+ if q_prev_norm > 1e-9:
690
+ q_prev = q_prev / q_prev_norm
691
+
692
+ q_now = q_state_boot[3:7, 0]
693
+ if np.dot(q_prev, q_now) < 0.0:
694
+ q_state_boot[3:7, :] *= -1.0
695
+
696
+ xig[0:self._nq, :] = q_state_boot
697
+ xig[self._nq:self._nq + self._nv, :] = 0.0 # 0 velocity on first nodes
698
+ uig[0:self._nv, :]=0.0 # 0 acceleration
699
+
700
+ # assigning ig
701
+ self._prb.getState().setInitialGuess(xig)
702
+ self._prb.getInput().setInitialGuess(uig)
703
+ # self._prb.getVariables("a").setInitialGuess(np.zeros((self._nv, 1), dtype=self._dtype))
704
+ for _, cforces in self._ti.model.cmap.items():
705
+ n_contact_f = len(cforces)
706
+ if n_contact_f == 0:
707
+ continue
708
+ f_guess = np.array(self._f0, dtype=self._dtype) / n_contact_f
709
+ for c in cforces:
710
+ c.setInitialGuess(f_guess)
711
+
712
+ # print("initial guesses")
713
+ # print(self._nq)
714
+ # print(self._nv)
715
+ # print("q")
716
+ # qig=self._ti.model.q.getInitialGuess()
717
+ # print(qig.shape)
718
+ # print(qig)
719
+ # print("v")
720
+ # print(self._ti.model.v.getInitialGuess())
721
+ # print("a")
722
+ # print(self._ti.model.a.getInitialGuess())
723
+ # for _, cforces in self._ti.model.cmap.items():
724
+ # for c in cforces:
725
+ # print("force")
726
+ # print(c.getInitialGuess())
727
+
728
+ return xig, uig
729
+
730
+ def _set_ig(self):
731
+
732
+ shift_num = -1 # shift data by one node
733
+
734
+ x_opt = self._ti.solution['x_opt']
735
+ u_opt = self._ti.solution['u_opt']
736
+
737
+ # building ig for state
738
+ xig = np.roll(x_opt,
739
+ shift_num, axis=1) # rolling state sol.
740
+ for i in range(abs(shift_num)):
741
+ # state on last node is copied to the elements
742
+ # which are "lost" during the shift operation
743
+ xig[:, -1 - i] = x_opt[:, -1]
744
+ # building ig for inputs
745
+ uig = np.roll(u_opt,
746
+ shift_num, axis=1) # rolling state sol.
747
+ for i in range(abs(shift_num)):
748
+ # state on last node is copied to the elements
749
+ # which are "lost" during the shift operation
750
+ uig[:, -1 - i] = u_opt[:, -1]
751
+
752
+ # assigning ig
753
+ self._prb.getState().setInitialGuess(xig)
754
+ self._prb.getInput().setInitialGuess(uig)
755
+
756
+ return xig, uig
757
+
758
+ def _update_open_loop(self,
759
+ bootstrap: bool = False):
760
+
761
+ q_state, v_state, a_state=self._set_is_open()
762
+
763
+ if not bootstrap:
764
+ self._set_ig()
765
+ else:
766
+ self._set_ig_bootstrap(q_state=q_state, v_state=v_state)
767
+
768
+ # robot_state=xig[:, 0]
769
+ # # open loop update:
770
+ # self._prb.setInitialState(x0=robot_state) # (xig has been shifted, so node 0
771
+ # # is node 1 in the last opt solution)
772
+
773
+ return q_state, v_state, a_state
774
+
775
+ def _update_closed_loop(self,
776
+ bootstrap: bool = False):
777
+
778
+ # set initial state
779
+ q_state=None
780
+ v_state=None
781
+ a_state=None
782
+ if self._custom_opts["adaptive_is"]:
783
+ # adaptive closed loop
784
+ q_state, v_state, a_state=self._set_is_adaptive()
785
+ elif self._custom_opts["fully_closed"]:
786
+ q_state, v_state, a_state=self._set_is_full()
787
+ elif self._custom_opts["closed_partial"]:
788
+ q_state, v_state, a_state=self._set_is_partial()
789
+ else:
790
+ Journal.log(self.__class__.__name__,
791
+ "_update_closed_loop",
792
+ "Neither adaptive_is, fully_closed, or closed_partial.",
793
+ LogType.EXCEP,
794
+ throw_when_excep = False)
795
+ q_state, v_state, a_state=self._set_is()
796
+
797
+ # set initial guess for controller
798
+ if not bootstrap:
799
+ self._set_ig()
800
+ else:
801
+ self._set_ig_bootstrap(q_state=q_state, v_state=v_state)
802
+
803
+ return q_state, v_state, a_state
804
+
805
+ def _set_is_open(self):
806
+
807
+ # overriding states with rhc data
808
+ q_full_root=self._get_root_full_q_from_sol(node_idx=1).reshape(-1, 1)
809
+ q_jnts=self._get_jnt_q_from_sol(node_idx=1, reduce=False).reshape(-1, 1)
810
+
811
+ twist_root=self._get_root_twist_from_sol(node_idx=1).reshape(-1, 1)
812
+ v_jnts=self._get_jnt_v_from_sol(node_idx=1).reshape(-1, 1)
813
+
814
+ # rhc variables to be set
815
+ q=self._prb.getVariables("q") # .setBounds()
816
+ root_q_full_rhc=q[0:7] # root full q
817
+ jnts_q_rhc=q[7:] # jnts q
818
+ vel=self._prb.getVariables("v")
819
+ root_twist_rhc=vel[0:6] # lin v.
820
+ jnts_v_rhc=vel[6:] # jnts v
821
+
822
+ self.rhc_refs.set_alpha(alpha=1.0) # fully open
823
+
824
+ # close state on known quantities
825
+ root_q_full_rhc.setBounds(lb=q_full_root,
826
+ ub=q_full_root, nodes=0)
827
+ jnts_q_rhc.setBounds(lb=q_jnts,
828
+ ub=q_jnts, nodes=0)
829
+ root_twist_rhc.setBounds(lb=twist_root,
830
+ ub=twist_root, nodes=0)
831
+ jnts_v_rhc.setBounds(lb=v_jnts,
832
+ ub=v_jnts, nodes=0)
833
+
834
+ # return state used for feedback
835
+ q_state=np.concatenate((q_full_root, q_jnts),
836
+ axis=0)
837
+ v_state=np.concatenate((twist_root, v_jnts),
838
+ axis=0)
839
+
840
+ return (q_state, v_state, None)
841
+
842
+ def _set_is_full(self):
843
+
844
+ # measurements
845
+ q_full_root = self.robot_state.root_state.get(data_type="q_full", robot_idxs=self.controller_index_np).reshape(-1, 1)
846
+ v_root = self.robot_state.root_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
847
+ omega = self.robot_state.root_state.get(data_type="omega", robot_idxs=self.controller_index_np).reshape(-1, 1)
848
+ a_root = self.robot_state.root_state.get(data_type="a_full", robot_idxs=self.controller_index_np).reshape(-1, 1)
849
+
850
+ q_jnts = self.robot_state.jnts_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
851
+ v_jnts = self.robot_state.jnts_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
852
+ a_jnts = self.robot_state.jnts_state.get(data_type="a", robot_idxs=self.controller_index_np).reshape(-1, 1)
853
+
854
+ if (not len(self._continuous_joints)==0): # we need do expand some meas. rev jnts to So2
855
+ self._jnts_q_expanded[self._rev_joints_idxs, :]=q_jnts[self._rev_joints_idxs_red ,:]
856
+ self._jnts_q_expanded[self._continuous_joints_idxs_cos, :]=np.cos(q_jnts[self._continuous_joints_idxs_red, :]) # cos
857
+ self._jnts_q_expanded[self._continuous_joints_idxs_sin, :]=np.sin(q_jnts[self._continuous_joints_idxs_red, :]) # sin
858
+ q_jnts=self._jnts_q_expanded.reshape(-1,1)
859
+
860
+ # rhc variables to be set
861
+ q=self._prb.getVariables("q") # .setBounds()
862
+ root_full_q_rhc=q[0:7] # root p
863
+ jnts_q_rhc=q[7:] # jnts q
864
+ vel=self._prb.getVariables("v")
865
+ root_v_rhc=vel[0:3] # lin v.
866
+ root_omega_rhc=vel[3:6] # omega
867
+ jnts_v_rhc=vel[6:] # jnts v
868
+ acc=self._prb.getVariables("a")
869
+ lin_a_prb=acc[0:3] # lin acc
870
+
871
+ self.rhc_refs.set_alpha(alpha=0.0) # non-adaptive
872
+
873
+ root_full_q_rhc.setBounds(lb=q_full_root,
874
+ ub=q_full_root, nodes=0)
875
+ jnts_q_rhc.setBounds(lb=q_jnts,
876
+ ub=q_jnts, nodes=0)
877
+ root_v_rhc.setBounds(lb=v_root,
878
+ ub=v_root, nodes=0) # leaving lin v of the base free (estimated from constraints)
879
+ root_omega_rhc.setBounds(lb=omega,
880
+ ub=omega, nodes=0)
881
+ jnts_v_rhc.setBounds(lb=v_jnts,
882
+ ub=v_jnts, nodes=0)
883
+ if self._custom_opts["lin_a_feedback"]:
884
+ # write base lin 13793197 from meas
885
+ lin_a_prb.setBounds(lb=a_root[0:3, :],
886
+ ub=a_root[0:3, :],
887
+ nodes=0)
888
+
889
+ # return state used for feedback
890
+ q_state=np.concatenate((q_full_root, q_jnts),
891
+ axis=0)
892
+ v_state=np.concatenate((v_root, omega, v_jnts),
893
+ axis=0)
894
+ a_state=np.concatenate((a_root, a_jnts),
895
+ axis=0)
896
+
897
+ return (q_state, v_state, a_state)
898
+
899
+ def _set_is_partial(self):
900
+
901
+ # measurements
902
+ p_root = self.robot_state.root_state.get(data_type="p", robot_idxs=self.controller_index_np).reshape(-1, 1)
903
+ q_root = self.robot_state.root_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
904
+ v_root = self.robot_state.root_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
905
+ omega = self.robot_state.root_state.get(data_type="omega", robot_idxs=self.controller_index_np).reshape(-1, 1)
906
+ a_root = self.robot_state.root_state.get(data_type="a_full", robot_idxs=self.controller_index_np).reshape(-1, 1)
907
+
908
+ q_jnts = self.robot_state.jnts_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
909
+ v_jnts = self.robot_state.jnts_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
910
+ a_jnts = self.robot_state.jnts_state.get(data_type="a", robot_idxs=self.controller_index_np).reshape(-1, 1)
911
+
912
+ if (not len(self._continuous_joints)==0): # we need do expand some meas. rev jnts to So2
913
+ self._jnts_q_expanded[self._rev_joints_idxs, :]=q_jnts[self._rev_joints_idxs_red ,:]
914
+ self._jnts_q_expanded[self._continuous_joints_idxs_cos, :]=np.cos(q_jnts[self._continuous_joints_idxs_red, :]) # cos
915
+ self._jnts_q_expanded[self._continuous_joints_idxs_sin, :]=np.sin(q_jnts[self._continuous_joints_idxs_red, :]) # sin
916
+ q_jnts=self._jnts_q_expanded.reshape(-1,1)
917
+
918
+ # overriding states with rhc data (-> all overridden state are open looop)
919
+ root_q_full_from_rhc=self._get_root_full_q_from_sol(node_idx=1).reshape(-1, 1)
920
+ root_p_from_rhc=root_q_full_from_rhc[0:3, :]
921
+ p_root[:, :]=root_p_from_rhc # position is always open loop
922
+
923
+ if not self._custom_opts["estimate_v_root"]:
924
+ v_root[:, :]=self._get_root_twist_from_sol(node_idx=1)[:, 0:3].reshape(-1, 1)
925
+ # override v jnts with the ones from controller
926
+ if not self._custom_opts["use_jnt_v_feedback"]:
927
+ v_jnts[:, :]=self._get_jnt_v_from_sol(node_idx=1).reshape(-1, 1)
928
+ # v_jnts[:, :]=self._get_jnt_v_from_sol(node_idx=1).reshape(-1, 1)
929
+ # root_twist_from_rhc=self._get_root_twist_from_sol(node_idx=1)
930
+ # root_v_from_rhc=root_twist_from_rhc[:, 0:3].reshape(-1, 1)
931
+ # root_omega_from_rhc=root_twist_from_rhc[:, 3:6].reshape(-1, 1)
932
+ # jnt_q_from_rhc=self._get_jnt_q_from_sol(node_idx=1,reduce=False,clamp=False).reshape(-1, 1)
933
+ # jnt_v_from_rhc=self._get_jnt_v_from_sol(node_idx=1).reshape(-1, 1)
934
+
935
+ # rhc variables to be set
936
+ q=self._prb.getVariables("q") # .setBounds()
937
+ root_p_rhc=q[0:3] # root p
938
+ root_q_rhc=q[3:7] # root orientation
939
+ jnts_q_rhc=q[7:] # jnts q
940
+ vel=self._prb.getVariables("v")
941
+ root_v_rhc=vel[0:3] # lin v.
942
+ root_omega_rhc=vel[3:6] # omega
943
+ jnts_v_rhc=vel[6:] # jnts v
944
+ acc=self._prb.getVariables("a")
945
+ lin_a_prb=acc[0:3] # lin acc
946
+
947
+ self.rhc_refs.set_alpha(alpha=0.0) # non-adaptive
948
+
949
+ root_p_rhc.setBounds(lb=p_root,
950
+ ub=p_root, nodes=0)
951
+ root_q_rhc.setBounds(lb=q_root,
952
+ ub=q_root, nodes=0)
953
+ jnts_q_rhc.setBounds(lb=q_jnts,
954
+ ub=q_jnts, nodes=0)
955
+
956
+ if self._custom_opts["estimate_v_root"]:
957
+ root_v_rhc.setBounds(lb=-self._v_inf[0:3],
958
+ ub=self._v_inf[0:3], nodes=0) # leaving lin v of the base free (estimated from constraints)
959
+ else: # get it from controller
960
+ root_v_rhc.setBounds(lb=v_root,
961
+ ub=v_root, nodes=0)
962
+ root_omega_rhc.setBounds(lb=omega,
963
+ ub=omega, nodes=0)
964
+ jnts_v_rhc.setBounds(lb=v_jnts,
965
+ ub=v_jnts, nodes=0)
966
+ if self._custom_opts["lin_a_feedback"]:
967
+ # write base lin 13793197 from meas
968
+ lin_a_prb.setBounds(lb=a_root[0:3, :],
969
+ ub=a_root[0:3, :],
970
+ nodes=0)
971
+
972
+ # return state used for feedback
973
+ q_state=np.concatenate((p_root, q_root, q_jnts),
974
+ axis=0)
975
+ v_state=np.concatenate((v_root, omega, v_jnts),
976
+ axis=0)
977
+ a_state=np.concatenate((a_root, a_jnts),
978
+ axis=0)
979
+
980
+ return (q_state, v_state, a_state)
981
+
982
+ def _set_is_adaptive(self):
983
+
984
+ # measurements
985
+ p_root = self.robot_state.root_state.get(data_type="p", robot_idxs=self.controller_index_np).reshape(-1, 1)
986
+ q_root = self.robot_state.root_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
987
+ v_root = self.robot_state.root_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
988
+ omega = self.robot_state.root_state.get(data_type="omega", robot_idxs=self.controller_index_np).reshape(-1, 1)
989
+ a_root = self.robot_state.root_state.get(data_type="a_full", robot_idxs=self.controller_index_np).reshape(-1, 1)
990
+
991
+ q_jnts = self.robot_state.jnts_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
992
+ v_jnts = self.robot_state.jnts_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
993
+ a_jnts = self.robot_state.jnts_state.get(data_type="a", robot_idxs=self.controller_index_np).reshape(-1, 1)
994
+
995
+ # rhc variables to be set
996
+ q=self._prb.getVariables("q") # .setBounds()
997
+ root_p_rhc=q[0:3] # root p
998
+ root_q_rhc=q[3:7] # root orientation
999
+ jnts_q_rhc=q[7:] # jnts q
1000
+ vel=self._prb.getVariables("v")
1001
+ root_v_rhc=vel[0:3] # lin v.
1002
+ root_omega_rhc=vel[3:6] # omega
1003
+ jnts_v_rhc=vel[6:] # jnts v
1004
+ acc=self._prb.getVariables("a")
1005
+ lin_a_prb=acc[0:3] # lin acc
1006
+
1007
+ # getting prediction defects
1008
+ root_q_delta=self.rhc_pred_delta.root_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
1009
+ jnt_q_delta=self.rhc_pred_delta.jnts_state.get(data_type="q", robot_idxs=self.controller_index_np).reshape(-1, 1)
1010
+ jnt_v_delta=self.rhc_pred_delta.jnts_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
1011
+ v_root_delta = self.rhc_pred_delta.root_state.get(data_type="v", robot_idxs=self.controller_index_np).reshape(-1, 1)
1012
+ omega_root_delta = self.rhc_pred_delta.root_state.get(data_type="omega", robot_idxs=self.controller_index_np).reshape(-1, 1)
1013
+ a_root_delta = self.rhc_pred_delta.root_state.get(data_type="a_full", robot_idxs=self.controller_index_np).reshape(-1, 1)
1014
+
1015
+ # close state on known quantities, estimate some (e.g. lin vel) and
1016
+ # open loop if thing start to explode
1017
+ alpha_now=1.0
1018
+ delta=0.0
1019
+ if self._custom_opts["alpha_from_outside"]:
1020
+ alpha_now=self.rhc_refs.get_alpha()
1021
+ else: # "autotuned" alpha
1022
+ if self._custom_opts["estimate_v_root"]: # we copmute delta based on jnt v (since we use meas.)
1023
+ delta=np.max(np.abs(jnt_v_delta))
1024
+ else:
1025
+ delta=np.max(np.abs(omega_root_delta))
1026
+ # fail_idx=self._get_failure_index()
1027
+ # fail_idx=self._get_explosion_idx()/self._fail_idx_thresh
1028
+ alpha_now=(np.tanh(2*self._alpha_half*(delta-self._alpha_half))+1)/2.0
1029
+
1030
+ bound_relaxation=-np.log(1-alpha_now+1e-16) # [~0, ~inf] if alpha_now [0, 1]
1031
+ self.rhc_refs.set_alpha(alpha=alpha_now) # also writes on shared mem for db
1032
+ self.rhc_refs.set_bound_relax(bound_relax=bound_relaxation) # also writes on shared mem for db
1033
+
1034
+ self._alphas_q_root[:]=alpha_now # for now single alpha for everything
1035
+ self._alphas_q_jnts[:]=alpha_now
1036
+ self._alphas_twist_root[:]=alpha_now
1037
+ self._alphas_v_jnts[:]=alpha_now
1038
+ self._alphas_a[:]=alpha_now
1039
+ if not self._custom_opts["estimate_v_root"]:
1040
+ self._alphas_twist_root[0:3]=1.0 # open
1041
+ self._alphas_v_jnts[:]=1.0 # open
1042
+
1043
+ # position is always open loop
1044
+ root_q_full_from_rhc=self._get_root_full_q_from_sol(node_idx=1).reshape(-1, 1)
1045
+ root_p_from_rhc=root_q_full_from_rhc[0:3, :]
1046
+ p_root[:, :]=root_p_from_rhc
1047
+
1048
+ # expaning meas q if continuous joints
1049
+ if (not len(self._continuous_joints)==0): # we need do expand some meas. rev jnts to So2
1050
+ self._jnts_q_expanded[self._rev_joints_idxs, :]=q_jnts[self._rev_joints_idxs_red ,:]
1051
+ self._jnts_q_expanded[self._continuous_joints_idxs_cos, :]=np.cos(q_jnts[self._continuous_joints_idxs_red, :]) # cos
1052
+ self._jnts_q_expanded[self._continuous_joints_idxs_sin, :]=np.sin(q_jnts[self._continuous_joints_idxs_red, :]) # sin
1053
+
1054
+ # continous joints position is always open loop, but we need a delta vector of matching dimension
1055
+ q_jnts_from_rhc=self._get_jnt_q_from_sol(node_idx=1).reshape(-1, 1)
1056
+
1057
+ self._jnts_q_delta_expanded[self._rev_joints_idxs, :]=jnt_q_delta[self._rev_joints_idxs_red ,:]
1058
+
1059
+ self._jnts_q_delta_expanded[self._continuous_joints_idxs_cos, :]=\
1060
+ np.cos(q_jnts_from_rhc[self._continuous_joints_idxs_red, :]) - \
1061
+ np.cos(q_jnts[self._continuous_joints_idxs_red, :])
1062
+ self._jnts_q_delta_expanded[self._continuous_joints_idxs_sin, :]=\
1063
+ np.sin(q_jnts_from_rhc[self._continuous_joints_idxs_red, :]) - \
1064
+ np.sin(q_jnts[self._continuous_joints_idxs_red, :])
1065
+
1066
+ q_jnts=self._jnts_q_expanded.reshape(-1,1) # overriting with expanded jnts
1067
+ jnt_q_delta=self._jnts_q_delta_expanded.reshape(-1, 1) # overriting with expanded jnts
1068
+
1069
+ self._alphas_q_jnts[self._continuous_joints_idxs_cos, :]=1.0 # open loop
1070
+ self._alphas_q_jnts[self._continuous_joints_idxs_sin, :]=1.0 # open loop
1071
+
1072
+ # self._alphas_v_jnts[self._continuous_joints_idxs_red, :]=0.0 # open loop
1073
+
1074
+ root_p_rhc.setBounds(lb=p_root,
1075
+ ub=p_root, nodes=0)
1076
+ root_q_rhc.setBounds(lb=q_root+self._alphas_q_root[3:7]*root_q_delta,
1077
+ ub=q_root+self._alphas_q_root[3:7]*root_q_delta, nodes=0)
1078
+ jnts_q_rhc.setBounds(lb=q_jnts+self._alphas_q_jnts*jnt_q_delta,
1079
+ ub=q_jnts+self._alphas_q_jnts*jnt_q_delta, nodes=0)
1080
+ if self._custom_opts["estimate_v_root"]:
1081
+ root_v_rhc.setBounds(lb=-self._v_inf[0:3],
1082
+ ub=self._v_inf[0:3], nodes=0)
1083
+ else:
1084
+ root_v_rhc.setBounds(lb=v_root+self._alphas_twist_root[0:3, :]*v_root_delta,
1085
+ ub=v_root+self._alphas_twist_root[0:3, :]*v_root_delta, nodes=0)
1086
+ root_omega_rhc.setBounds(lb=omega+self._alphas_twist_root[3:6, :]*omega_root_delta,
1087
+ ub=omega+self._alphas_twist_root[3:6, :]*omega_root_delta, nodes=0)
1088
+ jnts_v_rhc.setBounds(lb=v_jnts+self._alphas_v_jnts*jnt_v_delta,
1089
+ ub=v_jnts+self._alphas_v_jnts*jnt_v_delta, nodes=0)
1090
+ if self._custom_opts["lin_a_feedback"]:
1091
+ lin_a_prb.setBounds(lb=a_root[0:3, :]+self._alphas_a[0:3]*a_root_delta[0:3, :],
1092
+ ub=a_root[0:3, :]+self._alphas_a[0:3]*a_root_delta[0:3, :],
1093
+ nodes=0)
1094
+
1095
+ # return state used for feedback
1096
+ q_state=np.concatenate((p_root, q_root, q_jnts),
1097
+ axis=0)
1098
+ v_state=np.concatenate((v_root, omega, v_jnts),
1099
+ axis=0)
1100
+ a_state=np.concatenate((a_root, a_jnts),
1101
+ axis=0)
1102
+
1103
+ return (q_state, v_state, a_state)
1104
+
1105
+ def _solve(self):
1106
+
1107
+ if self._debug:
1108
+ return self._db_solve(bootstrap=False)
1109
+ else:
1110
+ return self._min_solve(bootstrap=False)
1111
+
1112
+ def _bootstrap(self):
1113
+
1114
+ if self._debug:
1115
+ return self._db_solve(bootstrap=True)
1116
+ else:
1117
+ return self._min_solve(bootstrap=True)
1118
+
1119
+ def _min_solve(self, bootstrap: bool = False):
1120
+ # minimal solve version -> no debug
1121
+ robot_qstate=None
1122
+ robot_vstate=None
1123
+ robot_astate=None
1124
+ if self._open_loop:
1125
+ robot_qstate, robot_vstate, robot_astate = self._update_open_loop(bootstrap=bootstrap) # updates the TO ig and
1126
+ # initial conditions using data from the solution itself
1127
+ else:
1128
+ robot_qstate, robot_vstate, robot_astate = self._update_closed_loop(bootstrap=bootstrap) # updates the TO ig and
1129
+ # initial conditions using robot measurements
1130
+
1131
+ self._pm.shift() # shifts phases of one dt
1132
+ if self._refs_in_hor_frame:
1133
+ # q_base=self.robot_state.root_state.get(data_type="q",
1134
+ # robot_idxs=self.controller_index).reshape(-1, 1)
1135
+ # q_full=self._get_full_q_from_sol(node_idx=1).reshape(-1, 1)
1136
+ # using internal base pose from rhc. in case of closed loop, it will be the meas state
1137
+ force_norm=None
1138
+ if self._custom_opts["use_force_feedback"]:
1139
+ contact_forces=self.robot_state.contact_wrenches.get(data_type="f",
1140
+ robot_idxs=self.controller_index_np,
1141
+ contact_name=None).reshape(self.n_contacts,3)
1142
+ force_norm=np.linalg.norm(contact_forces, axis=1)
1143
+ self.rhc_refs.step(qstate=robot_qstate, vstate=robot_vstate,
1144
+ force_norm=force_norm)
1145
+ else:
1146
+ self.rhc_refs.step()
1147
+
1148
+ try:
1149
+ if not bootstrap:
1150
+ converged = self._ti.rti() # RTI step
1151
+ else:
1152
+ converged = self._ti.bootstrap() # full solve (to convergence)
1153
+ self.sol_counter = self.sol_counter + 1
1154
+ return not self._check_rhc_failure()
1155
+ except Exception as e: # fail in case of exceptions
1156
+ return False
1157
+
1158
+ def _db_solve(self, bootstrap: bool = False):
1159
+
1160
+ self._timer_start = time.perf_counter()
1161
+
1162
+ robot_qstate=None
1163
+ robot_vstate=None
1164
+ robot_astate=None
1165
+ if self._open_loop:
1166
+ robot_qstate, robot_vstate, robot_astate = self._update_open_loop(bootstrap=bootstrap) # updates the TO ig and
1167
+ # initial conditions using data from the solution itself
1168
+ else:
1169
+ robot_qstate, robot_vstate, robot_astate = self._update_closed_loop(bootstrap=bootstrap) # updates the TO ig and
1170
+ # initial conditions using robot measurements
1171
+
1172
+ self._prb_update_time = time.perf_counter()
1173
+ self._pm.shift() # shifts phases of one dt
1174
+ self._phase_shift_time = time.perf_counter()
1175
+
1176
+ if self._refs_in_hor_frame:
1177
+ # q_base=self.robot_state.root_state.get(data_type="q",
1178
+ # robot_idxs=self.controller_index).reshape(-1, 1)
1179
+ # q_full=self._get_full_q_from_sol(node_idx=1).reshape(-1, 1)
1180
+ # using internal base pose from rhc. in case of closed loop, it will be the meas state
1181
+ force_norm=None
1182
+ if self._custom_opts["use_force_feedback"]:
1183
+ contact_forces=self.robot_state.contact_wrenches.get(data_type="f",
1184
+ robot_idxs=self.controller_index_np,
1185
+ contact_name=None).reshape(self.n_contacts,3)
1186
+ force_norm=np.linalg.norm(contact_forces, axis=1)
1187
+ self.rhc_refs.step(qstate=robot_qstate, vstate=robot_vstate,
1188
+ force_norm=force_norm)
1189
+ else:
1190
+ self.rhc_refs.step()
1191
+
1192
+ self._task_ref_update_time = time.perf_counter()
1193
+
1194
+ try:
1195
+ if not bootstrap:
1196
+ converged = self._ti.rti() # RTI step
1197
+ else:
1198
+ converged = self._ti.bootstrap() # full solve bootstrap
1199
+ self._rti_time = time.perf_counter()
1200
+ self.sol_counter = self.sol_counter + 1
1201
+ self._update_db_data()
1202
+ return not self._check_rhc_failure()
1203
+ except Exception as e: # fail in case of exceptions
1204
+ if self._verbose:
1205
+ solve_mode = "RTI" if not bootstrap else "Bootstrap"
1206
+ exception = f"{solve_mode}() for controller {self.controller_index} failed" + \
1207
+ f" with exception {type(e).__name__}"
1208
+ Journal.log(self.__class__.__name__,
1209
+ "solve",
1210
+ exception,
1211
+ LogType.EXCEP,
1212
+ throw_when_excep = False)
1213
+ self._update_db_data()
1214
+ return False
1215
+
1216
+ def _get_fail_idx(self):
1217
+
1218
+ self._explosion_idx_buffer[:, self._expl_idx_buffer_counter]=self._get_explosion_idx()
1219
+ self._expl_idx_buffer_counter+=1
1220
+ self._expl_idx_counter+=1
1221
+ if self._expl_idx_counter%self._expl_idx_window_size==0:
1222
+ self._expl_idx_buffer_counter=0 # restart from 0
1223
+
1224
+ running_avrg=np.mean(self._explosion_idx_buffer).item()
1225
+
1226
+ return running_avrg
1227
+
1228
+ def _get_explosion_idx(self):
1229
+ explosion_index = self._get_rhc_constr_viol() + self._get_rhc_cost()*self._fail_idx_scale
1230
+ return explosion_index
1231
+
1232
+ def _update_db_data(self):
1233
+
1234
+ self._profiling_data_dict["problem_update_dt"] = self._prb_update_time - self._timer_start
1235
+ self._profiling_data_dict["phases_shift_dt"] = self._phase_shift_time - self._prb_update_time
1236
+ self._profiling_data_dict["task_ref_update"] = self._task_ref_update_time - self._phase_shift_time
1237
+ self._profiling_data_dict["rti_solve_dt"] = self._rti_time - self._task_ref_update_time
1238
+ self.rhc_costs.update(self._ti.solver_rti.getCostsValues())
1239
+ self.rhc_constr.update(self._ti.solver_rti.getConstraintsValues())
1240
+
1241
+ def _reset(self):
1242
+
1243
+ # reset task interface (ig, solvers, etc..) +
1244
+ # phase manager and sets bootstap as solution
1245
+ self._gm.reset()
1246
+ self._explosion_idx_buffer[:, :]=self._get_explosion_idx() # reset with data from reset solution
1247
+ self._expl_idx_counter=0.0
1248
+ self._expl_idx_buffer_counter=0
1249
+
1250
+ def _get_cost_info(self):
1251
+
1252
+ cost_dict = self._ti.solver_rti.getCostsValues()
1253
+ cost_names = list(cost_dict.keys())
1254
+ cost_dims = [1] * len(cost_names) # costs are always scalar
1255
+ return cost_names, cost_dims
1256
+
1257
+ def _get_constr_info(self):
1258
+
1259
+ constr_dict = self._ti.solver_rti.getConstraintsValues()
1260
+ constr_names = list(constr_dict.keys())
1261
+ constr_dims = [-1] * len(constr_names)
1262
+ i = 0
1263
+ for constr in constr_dict:
1264
+ constr_val = constr_dict[constr]
1265
+ constr_shape = constr_val.shape
1266
+ constr_dims[i] = constr_shape[0]
1267
+ i+=1
1268
+ return constr_names, constr_dims
1269
+
1270
+ def _get_q_from_sol(self):
1271
+ full_q=self._ti.solution['q'].astype(self._dtype)
1272
+ if self._custom_opts["replace_continuous_joints"]:
1273
+ return full_q
1274
+ else:
1275
+ cont_jnts=full_q[self._continuous_joints_idxs_qfull, :]
1276
+ cos=cont_jnts[::2, :]
1277
+ sin=cont_jnts[1::2, :]
1278
+ # copy root
1279
+ self._full_q_reduced[0:7, :]=full_q[0:7, :]
1280
+ # copy rev joint vals
1281
+ self._full_q_reduced[self._rev_joints_idxs_red_qfull, :]=full_q[self._rev_joints_idxs_qfull, :]
1282
+ # and continuous
1283
+ angle=np.arctan2(sin, cos)
1284
+ self._full_q_reduced[self._continuous_joints_idxs_red_qfull, :]=angle
1285
+ return self._full_q_reduced
1286
+
1287
+ def _get_v_from_sol(self):
1288
+ return self._ti.solution['v'].astype(self._dtype)
1289
+
1290
+ def _get_a_from_sol(self):
1291
+ return self._ti.solution['a'].astype(self._dtype)
1292
+
1293
+ def _get_a_dot_from_sol(self):
1294
+ return None
1295
+
1296
+ def _get_f_from_sol(self):
1297
+ # to be overridden by child class
1298
+ contact_names =self._get_contacts() # we use controller-side names
1299
+ try:
1300
+ data=[]
1301
+ for key in contact_names:
1302
+ contact_f=self._ti.solution["f_" + key].astype(self._dtype)
1303
+ np.nan_to_num(contact_f, nan=1e6, posinf=1e6, neginf=-1e6, copy=False)
1304
+ np.clip(a=contact_f, a_max=1e6, a_min=-1e6, out=contact_f)
1305
+ data.append(contact_f)
1306
+ return np.concatenate(data, axis=0)
1307
+ except:
1308
+ return None
1309
+
1310
+ def _get_f_dot_from_sol(self):
1311
+ # to be overridden by child class
1312
+ return None
1313
+
1314
+ def _get_eff_from_sol(self):
1315
+ # to be overridden by child class
1316
+ return None
1317
+
1318
+ def _get_cost_from_sol(self,
1319
+ cost_name: str):
1320
+ return self.rhc_costs[cost_name]
1321
+
1322
+ def _get_constr_from_sol(self,
1323
+ constr_name: str):
1324
+ return self.rhc_constr[constr_name]
bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/isaac_world_interface.py ADDED
The diff for this file is too large to render. See raw diff
 
bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/jnt_imp_config.yaml ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ XBotInterface: # just used for retrieving homing in sim with xbot_mujoco
2
+ urdf_path: $PWD/xmj_env_files/centauro.urdf
3
+ srdf_path: $PWD/xmj_env_files/centauro.srdf
4
+
5
+ # defaults
6
+ motor_pd:
7
+ "j_arm*_1": [500, 10]
8
+ "j_arm*_2": [500, 10]
9
+ "j_arm*_3": [500, 10]
10
+ "j_arm*_4": [500, 10]
11
+ "j_arm*_5": [100, 5]
12
+ "j_arm*_6": [100, 5]
13
+ "j_arm*_7": [100, 5]
14
+ "hip_yaw_*": [3000, 30]
15
+ "hip_pitch_*": [3000, 30]
16
+ "knee_pitch_*": [3000, 30]
17
+ "ankle_pitch_*": [1000, 10]
18
+ "ankle_yaw_*": [300, 10]
19
+ "neck_pitch": [10, 1]
20
+ "neck_yaw": [10, 1]
21
+ "torso_yaw": [1000, 30]
22
+ "j_wheel_*": [0, 30]
23
+ "velodyne_*": [10, 1]
24
+ "d435_*": [10, 1]
25
+ "dagana_*": [50, 1]
26
+
27
+ startup_motor_pd: # iannis combo
28
+ "j_arm*_1": [500, 30]
29
+ "j_arm*_2": [500, 30]
30
+ "j_arm*_3": [500, 30]
31
+ "j_arm*_4": [500, 30]
32
+ "j_arm*_5": [100, 5]
33
+ "j_arm*_6": [100, 5]
34
+ "j_arm*_7": [100, 5]
35
+ "hip_yaw_*": [200, 60]
36
+ "hip_pitch_*": [200, 60]
37
+ "knee_pitch_*": [200, 60]
38
+ "ankle_pitch_*": [200, 60]
39
+ "ankle_yaw_*": [120, 30]
40
+ "neck_pitch": [10, 1]
41
+ "neck_yaw": [10, 1]
42
+ "torso_yaw": [1000, 30]
43
+ "j_wheel_*": [0, 30]
44
+ "velodyne_*": [10, 1]
45
+ "d435_*": [10, 1]
46
+ "dagana_*": [50, 1]
bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/jnt_imp_config_with_ub.yaml ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ XBotInterface: # just used for retrieving homing in sim with xbot_mujoco
2
+ urdf_path: $PWD/xmj_env_files/centauro.urdf
3
+ srdf_path: $PWD/xmj_env_files/centauro.srdf
4
+
5
+ # defaults
6
+ motor_pd:
7
+ "j_arm*_1": [500, 10]
8
+ "j_arm*_2": [500, 10]
9
+ "j_arm*_3": [500, 10]
10
+ "j_arm*_4": [500, 10]
11
+ "j_arm*_5": [100, 5]
12
+ "j_arm*_6": [100, 5]
13
+ "j_arm*_7": [100, 5]
14
+ "hip_yaw_*": [3000, 30]
15
+ "hip_pitch_*": [3000, 30]
16
+ "knee_pitch_*": [3000, 30]
17
+ "ankle_pitch_*": [1000, 10]
18
+ "ankle_yaw_*": [300, 10]
19
+ "neck_pitch": [10, 1]
20
+ "neck_yaw": [10, 1]
21
+ "torso_yaw": [1000, 30]
22
+ "j_wheel_*": [0, 30]
23
+ "velodyne_*": [10, 1]
24
+ "d435_*": [10, 1]
25
+ "dagana_*": [50, 1]
26
+
27
+ startup_motor_pd: # iannis combo
28
+ "j_arm*_1": [100, 25]
29
+ "j_arm*_2": [100, 25]
30
+ "j_arm*_3": [100, 25]
31
+ "j_arm*_4": [100, 25]
32
+ "j_arm*_5": [20, 8]
33
+ "j_arm*_6": [20, 8]
34
+ "j_arm*_7": [20, 8]
35
+ "hip_yaw_*": [200, 60]
36
+ "hip_pitch_*": [200, 60]
37
+ "knee_pitch_*": [200, 60]
38
+ "ankle_pitch_*": [200, 60]
39
+ "ankle_yaw_*": [120, 30]
40
+ "neck_pitch": [10, 1]
41
+ "neck_yaw": [10, 1]
42
+ "torso_yaw": [400, 60]
43
+ "j_wheel_*": [0, 30]
44
+ "velodyne_*": [10, 1]
45
+ "d435_*": [10, 1]
46
+ "dagana_*": [50, 1]
bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/launch_control_cluster.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import argparse
3
+ import multiprocessing as mp
4
+ import importlib.util
5
+ import inspect
6
+
7
+ from aug_mpc.utils.custom_arg_parsing import generate_custom_arg_dict
8
+
9
+ from EigenIPC.PyEigenIPC import Journal, LogType
10
+
11
+ this_script_name = os.path.splitext(os.path.basename(os.path.abspath(__file__)))[0]
12
+
13
+ # Function to dynamically import a module from a specific file path
14
+ def import_env_module(env_path):
15
+ spec = importlib.util.spec_from_file_location("env_module", env_path)
16
+ env_module = importlib.util.module_from_spec(spec)
17
+ spec.loader.exec_module(env_module)
18
+ return env_module
19
+
20
+ if __name__ == "__main__":
21
+
22
+ # Parse command line arguments for CPU affinity
23
+ parser = argparse.ArgumentParser(description="Set CPU affinity for the script.")
24
+ parser.add_argument('--ns', type=str, help='Namespace to be used for cluster shared memory')
25
+ parser.add_argument('--urdf_path', type=str, help='Robot description package path for URDF ')
26
+ parser.add_argument('--srdf_path', type=str, help='Robot description package path for SRDF ')
27
+ parser.add_argument('--size', type=int, help='cluster size', default=1)
28
+
29
+ # Replacing argparse.BooleanOptionalAction with 'store_true' and 'store_false' for compatibility with Python 3.8
30
+ parser.add_argument('--cloop',action='store_true', help='whether to use RHC controllers in closed loop mode')
31
+ parser.add_argument('--cluster_dt', type=float, default=0.05, help='dt used by MPC controllers for discretization')
32
+ parser.add_argument('--n_nodes', type=int, default=31, help='n nodes used by MPC controllers')
33
+
34
+ parser.add_argument('--verbose',action='store_true', help='run in verbose mode')
35
+
36
+ parser.add_argument('--enable_debug',action='store_true', help='enable debug mode for cluster client and all controllers')
37
+
38
+ parser.add_argument('--dmpdir', type=str, help='directory where data is dumped', default="/root/aux_data")
39
+
40
+ parser.add_argument('--no_mp_fork',action='store_true', help='whether to multiprocess with forkserver context')
41
+
42
+ parser.add_argument('--set_affinity',action='store_true', help='set affinity to a core for each controller')
43
+
44
+ parser.add_argument('--comment', type=str, help='Any useful comment associated with this run', default="")
45
+ parser.add_argument('--timeout_ms', type=int, help='connection timeout after which the script self-terminates', default=60000)
46
+ parser.add_argument('--codegen_override_dir', type=str, help='Path to base dir where codegen is to be loaded', default="")
47
+
48
+ parser.add_argument('--custom_args_names', nargs='+', default=None,
49
+ help='list of custom arguments names')
50
+ parser.add_argument('--custom_args_vals', nargs='+', default=None,
51
+ help='list of custom arguments values')
52
+ parser.add_argument('--custom_args_dtype', nargs='+', default=None,
53
+ help='list of custom arguments data types')
54
+
55
+ parser.add_argument('--cluster_client_fname', type=str,
56
+ default="aug_mpc.controllers.rhc.hybrid_quad_client",
57
+ help="cluster client file import pattern (without extension)")
58
+
59
+ args = parser.parse_args()
60
+
61
+ # Ensure custom_args_names and custom_args_vals have the same length
62
+ custom_opts = generate_custom_arg_dict(args=args)
63
+ custom_opts.update({"cloop": args.cloop,
64
+ "cluster_dt": args.cluster_dt,
65
+ "n_nodes": args.n_nodes,
66
+ "codegen_override_dir": args.codegen_override_dir})
67
+ if args.no_mp_fork: # this needs to be in the main
68
+ mp.set_start_method('spawn')
69
+ else:
70
+ # mp.set_start_method('forkserver')
71
+ mp.set_start_method('fork')
72
+
73
+ cluster_module=importlib.import_module(args.cluster_client_fname)
74
+ # Get all classes defined in the module
75
+ classes_in_module = [name for name, obj in inspect.getmembers(cluster_module, inspect.isclass)
76
+ if obj.__module__ == cluster_module.__name__]
77
+ if len(classes_in_module) == 1:
78
+ cluster_classname=classes_in_module[0]
79
+ ClusterClient = getattr(cluster_module, cluster_classname)
80
+ cluster_client = ClusterClient(namespace=args.ns,
81
+ cluster_size=args.size,
82
+ urdf_xacro_path=args.urdf_path,
83
+ srdf_xacro_path=args.srdf_path,
84
+ open_loop=not args.cloop,
85
+ use_mp_fork = not args.no_mp_fork,
86
+ verbose=args.verbose,
87
+ debug=args.enable_debug,
88
+ base_dump_dir=args.dmpdir,
89
+ timeout_ms=args.timeout_ms,
90
+ custom_opts=custom_opts,
91
+ codegen_override=args.codegen_override_dir,
92
+ set_affinity=args.set_affinity)
93
+ cluster_client.run()
94
+
95
+ else:
96
+ class_list_str = ", ".join(classes_in_module)
97
+ Journal.log("launch_control_cluster.py",
98
+ "",
99
+ f"Found more than one class in cluster client file {args.cluster_client_fname}. Found: {class_list_str}",
100
+ LogType.EXCEP,
101
+ throw_when_excep = False)
102
+ exit()
103
+
104
+ # control_cluster_client =
105
+ # control_cluster_client.run() # spawns the controllers on separate processes (blocking)
106
+
bundles/centauro/d2026_02_21_h14_m01_s10-CentauroCloopPartialUbNoWheels_FakePosTrackingEnv/launch_train_env.py ADDED
@@ -0,0 +1,358 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from aug_mpc.utils.determinism import deterministic_run
2
+
3
+ from mpc_hive.utilities.shared_data.sim_data import SharedEnvInfo
4
+ from mpc_hive.utilities.shared_data.cluster_data import SharedClusterInfo
5
+
6
+ from EigenIPC.PyEigenIPC import VLevel, Journal, LogType
7
+ from EigenIPC.PyEigenIPC import StringTensorServer
8
+
9
+ import os, argparse, sys, types, inspect
10
+
11
+ from perf_sleep.pyperfsleep import PerfSleep
12
+
13
+ import importlib.util
14
+ import torch
15
+ import signal
16
+
17
+ algo = None # global to make it accessible by signal handler
18
+ exit_request=False
19
+ dummy_step_exit_req=False
20
+
21
+ def handle_sigint(signum, frame):
22
+ global exit_request, dummy_step_exit_req
23
+ Journal.log("launch_train_env.py",
24
+ "",
25
+ f"Received sigint. Will stop training.",
26
+ LogType.WARN)
27
+ exit_request=True
28
+ dummy_step_exit_req=True # in case dummy_step_loop was used
29
+
30
+ # Function to dynamically import a module from a specific file path
31
+ # def import_env_module(env_path):
32
+ # spec = importlib.util.spec_from_file_location("env_module", env_path)
33
+ # env_module = importlib.util.module_from_spec(spec)
34
+ # spec.loader.exec_module(env_module)
35
+ # return env_module
36
+
37
+ def import_env_module(env_path, local_env_root: str = None):
38
+ """
39
+ env_path: full path to the child env .py file to exec
40
+ local_env_root: directory where local copies of aug_mpc_envs.training_envs modules live
41
+ """
42
+ if local_env_root is not None:
43
+ local_env_root = os.path.abspath(local_env_root)
44
+ # override aug_mpc_envs.training_envs package to point to the local_env_root
45
+ pkg_name = "aug_mpc_envs.training_envs"
46
+ if pkg_name not in sys.modules:
47
+ mod = types.ModuleType(pkg_name)
48
+ mod.__path__ = [local_env_root] # tell Python to look here first
49
+ sys.modules[pkg_name] = mod
50
+ else:
51
+ existing = getattr(sys.modules[pkg_name], "__path__", None)
52
+ if existing is None:
53
+ sys.modules[pkg_name].__path__ = [local_env_root]
54
+ elif local_env_root not in existing:
55
+ existing.insert(0, local_env_root)
56
+
57
+ # load the module as usual
58
+ spec = importlib.util.spec_from_file_location("env_module", env_path)
59
+ env_module = importlib.util.module_from_spec(spec)
60
+ spec.loader.exec_module(env_module)
61
+ return env_module
62
+
63
+ def log_env_hierarchy(env_class, env_path, env_type="training"):
64
+ """
65
+ Logs the env class, its file, and full inheritance hierarchy with file paths.
66
+ env_class: the child env class
67
+ env_path: file path where the child class was loaded from
68
+ env_type: string label, e.g., "training", "evaluation", "resumed_training"
69
+ """
70
+ def get_bases_recursive(cls):
71
+ """Recursively get all base classes with their file paths."""
72
+ info = []
73
+ for base in cls.__bases__:
74
+ try:
75
+ file = inspect.getfile(base)
76
+ except TypeError:
77
+ file = "built-in or unknown"
78
+ info.append(f"{base.__name__} (from {file})")
79
+ # Recurse unless it's object
80
+ if base is not object:
81
+ info.extend(get_bases_recursive(base))
82
+ return info
83
+
84
+ hierarchy_info = get_bases_recursive(env_class)
85
+ hierarchy_str = " -> ".join(hierarchy_info) if hierarchy_info else "No parents"
86
+
87
+ Journal.log(
88
+ "launch_train_env.py",
89
+ "",
90
+ f"loading {env_type} env {env_class.__name__} (from {env_path}) "
91
+ f"with hierarchy: {hierarchy_str}",
92
+ LogType.INFO,
93
+ throw_when_excep=True
94
+ )
95
+
96
+ def dummy_step_loop(env):
97
+ global dummy_step_exit_req
98
+ while True:
99
+ if dummy_step_exit_req:
100
+ return True
101
+ step_ok=env.step(action=env.safe_action) # not a busy loop because of MPC in the step
102
+ if not step_ok:
103
+ return False
104
+
105
+ if __name__ == "__main__":
106
+
107
+ signal.signal(signal.SIGINT, handle_sigint)
108
+
109
+ # Parse command line arguments for CPU affinity
110
+ parser = argparse.ArgumentParser(description="Set CPU affinity for the script.")
111
+
112
+ parser.add_argument('--run_name', type=str, default=None, help='Name of training run')
113
+ parser.add_argument('--ns', type=str, help='Namespace to be used for shared memory')
114
+ parser.add_argument('--timeout_ms', type=int, help='Connection timeout after which the script self-terminates', default=60000)
115
+ parser.add_argument('--drop_dir', type=str, help='Directory root where all run data will be dumped')
116
+ parser.add_argument('--comment', type=str, help='Any useful comment associated with this run', default="")
117
+ parser.add_argument('--seed', type=int, help='Seed', default=1)
118
+ parser.add_argument('--use_cpu',action='store_true', help='If set, all the training (data included) will be performed on CPU')
119
+
120
+ parser.add_argument('--db',action='store_true', help='Whether to enable local data logging for the algorithm (reward metrics, etc.)')
121
+ parser.add_argument('--env_db',action='store_true', help='Whether to enable env db data logging on shared mem (e.g. reward metrics are not available for reading anymore)')
122
+ parser.add_argument('--full_env_db',action='store_true', help='Whether to enable detailed episodic data storage (data over single transitions)')
123
+ parser.add_argument('--rmdb',action='store_true', help='Whether to enable remote debug (e.g. data logging on remote servers)')
124
+
125
+ parser.add_argument('--tot_tsteps', type=int, help='Total number of timesteps to be collected', default=int(30e6))
126
+ parser.add_argument('--action_repeat', type=int, help='Frame skipping (1-> no skip)', default=1)
127
+ parser.add_argument('--discount_factor', type=float, help='', default=0.99)
128
+ parser.add_argument('--obs_norm',action='store_true', help='Whether to enable the use of running normalizer in agent')
129
+ parser.add_argument('--obs_rescale',action='store_true', help='Whether to rescale observation depending on their expected range')
130
+ parser.add_argument('--add_weight_norm',action='store_true', help='Whether to add weight normalization to agent interal llayers')
131
+ parser.add_argument('--add_layer_norm',action='store_true', help='Whether to add layer normalization to agent internal llayers')
132
+ parser.add_argument('--add_batch_norm',action='store_true', help='Whether to add batch normalization to agent internal llayers')
133
+
134
+ parser.add_argument('--act_rescale_critic',action='store_true', help='Whether to rescale actions provided to critic (if SAC) to be in range [-1, 1]')
135
+ parser.add_argument('--use_period_resets',action='store_true', help='')
136
+
137
+ parser.add_argument('--sac',action='store_true', help='Use SAC, otherwise PPO, unless dummy is set')
138
+ parser.add_argument('--dummy',action='store_true', help='Use dummy agent (useful for testing and debugging environments)')
139
+
140
+ parser.add_argument('--dump_checkpoints',action='store_true', help='Whether to dump model checkpoints during training')
141
+
142
+ parser.add_argument('--demo_envs_perc', type=float, help='[0, 1]', default=0.0)
143
+ parser.add_argument('--demo_stop_thresh', type=float, default=None,
144
+ help='Performance hreshold above which demonstration envs should be deactivated.')
145
+
146
+ parser.add_argument('--expl_envs_perc', type=float, help='[0, 1]', default=0)
147
+
148
+ parser.add_argument('--use_rnd',action='store_true', help='Whether to use RND for exploration')
149
+
150
+ parser.add_argument('--eval',action='store_true', help='Whether to perform an evaluation run')
151
+ parser.add_argument('--n_eval_timesteps', type=int, help='Total number of timesteps to be evaluated', default=int(1e6))
152
+ parser.add_argument('--det_eval',action='store_true', help='Whether to perform a deterministic eval (only action mean is used). Only valid if --eval.')
153
+ parser.add_argument('--allow_expl_during_eval',action='store_true', help='Whether to allow expl envs during evaluation (useful to tune exploration)')
154
+
155
+ parser.add_argument('--resume',action='store_true', help='Resume a previous training using a checkpoint')
156
+ parser.add_argument('--mpath', type=str, help='Model path to be used for policy evaluation', default=None)
157
+ parser.add_argument('--mname', type=str, help='Model name', default=None)
158
+ parser.add_argument('--override_env',action='store_true', help='Whether to override env when running evaluation')
159
+
160
+ parser.add_argument('--anomaly_detect',action='store_true', help='Whether to enable anomaly detection (useful for debug)')
161
+
162
+ parser.add_argument('--compression_ratio', type=float,
163
+ help='If e.g. 0.8, the fist layer will be of dimension [input_features_size x (input_features_size*compression_ratio)]', default=-1.0)
164
+ parser.add_argument('--actor_lwidth', type=int, help='Actor network layer width', default=128)
165
+ parser.add_argument('--critic_lwidth', type=int, help='Critic network layer width', default=256)
166
+ parser.add_argument('--actor_n_hlayers', type=int, help='Actor network size', default=3)
167
+ parser.add_argument('--critic_n_hlayers', type=int, help='Critic network size', default=4)
168
+
169
+ parser.add_argument('--env_fname', type=str, default="twist_tracking_env", help='Training env file name (without extension)')
170
+ parser.add_argument('--env_classname', type=str, default="TwistTrackingEnv", help='Training env class name')
171
+ parser.add_argument('--override_agent_actions',action='store_true', help='Whether to override agent actions with custom ones from shared mem (useful for db)')
172
+ parser.add_argument('--override_agent_refs',action='store_true', help='Whether to override automatically generated agent refs (useful for debug)')
173
+
174
+ parser.add_argument('--step_while_setup',action='store_true', help='Continue stepping env with default actions while setting up agent, etc..')
175
+ parser.add_argument('--reset_on_init',action='store_true', help='Whether to reset the environment on initialization')
176
+
177
+ args = parser.parse_args()
178
+ args_dict = vars(args)
179
+
180
+ if args.eval and args.resume:
181
+ Journal.log("launch_train_env.py",
182
+ "",
183
+ f"Cannot set both --eval and --resume flags. Exiting.",
184
+ LogType.EXCEP,
185
+ throw_when_excep = True)
186
+
187
+ deterministic_run(seed=args.seed, torch_det_algos=False)
188
+
189
+ anomaly_detect=False
190
+ if args.anomaly_detect:
191
+ torch.autograd.set_detect_anomaly(True)
192
+
193
+ if (not args.mpath is None) and (not args.mname is None):
194
+ mpath_full = os.path.join(args.mpath, args.mname)
195
+ else:
196
+ mpath_full=None
197
+
198
+ env_fname=args.env_fname
199
+ env_classname = args.env_classname
200
+ env_path=""
201
+ env_module=None
202
+ if (not args.eval and not args.resume) or (args.override_env):
203
+ # if starting a fresh traning or overriding env, load from a fresh env from aug_mpc
204
+ env_path = f"aug_mpc_envs.training_envs.{env_fname}"
205
+ env_module = importlib.import_module(env_path)
206
+ else:
207
+ if args.mpath is None:
208
+ Journal.log("launch_train_env.py",
209
+ "",
210
+ f"no mpath provided! Cannot load env. Either provide a mpath or run with --override_env",
211
+ LogType.EXCEP,
212
+ throw_when_excep = True)
213
+
214
+ env_path = os.path.join(args.mpath, env_fname + ".py")
215
+ env_module = import_env_module(env_path, local_env_root=args.mpath)
216
+
217
+ EnvClass = getattr(env_module, env_classname)
218
+ env_type = "training" if not args.eval else "evaluation"
219
+ if args.resume:
220
+ env_type = "resumed_training"
221
+ log_env_hierarchy(EnvClass, env_path, env_type) # db print of env class
222
+
223
+ env = EnvClass(namespace=args.ns,
224
+ verbose=True,
225
+ vlevel=VLevel.V2,
226
+ use_gpu=not args.use_cpu,
227
+ debug=args.env_db,
228
+ override_agent_refs=args.override_agent_refs,
229
+ timeout_ms=args.timeout_ms,
230
+ env_opts=args_dict)
231
+ if not env.is_ready(): # something went wrong
232
+ exit()
233
+
234
+ dummy_step_thread = None
235
+ if args.step_while_setup:
236
+ import threading
237
+ # spawn step thread (we don't true parallelization, thread is fine)
238
+ # start the dummy stepping in a separate thread so setup can continue concurrently
239
+ dummy_step_thread = threading.Thread(target=dummy_step_loop, args=(env,), daemon=True)
240
+ dummy_step_thread.start()
241
+
242
+ # getting some sim info for debugging
243
+ sim_data = {}
244
+ sim_info_shared = SharedEnvInfo(namespace=args.ns,
245
+ is_server=False,
246
+ safe=False)
247
+ sim_info_shared.run()
248
+ sim_info_keys = sim_info_shared.param_keys
249
+ sim_info_data = sim_info_shared.get().flatten()
250
+ for i in range(len(sim_info_keys)):
251
+ sim_data[sim_info_keys[i]] = sim_info_data[i]
252
+
253
+ # getting come cluster info for debugging
254
+ cluster_data={}
255
+ cluste_info_shared = SharedClusterInfo(namespace=args.ns,
256
+ is_server=False,
257
+ safe=False)
258
+ cluste_info_shared.run()
259
+ cluster_info_keys = cluste_info_shared.param_keys
260
+ cluster_info_data = cluste_info_shared.get().flatten()
261
+ for i in range(len(cluster_info_keys)):
262
+ cluster_data[cluster_info_keys[i]] = cluster_info_data[i]
263
+
264
+ custom_args={}
265
+ custom_args["uname_host"]="user_host"
266
+ try:
267
+ username = os.getlogin() # add machine info to db data
268
+ hostname = os.uname().nodename
269
+ user_host = f"{username}@{hostname}"
270
+ custom_args["uname_host"]=user_host
271
+ except:
272
+ pass
273
+
274
+ algo=None
275
+ if not args.dummy:
276
+ if args.sac:
277
+ from aug_mpc.training_algs.sac.sac import SAC
278
+
279
+ algo = SAC(env=env,
280
+ debug=args.db,
281
+ remote_db=args.rmdb,
282
+ seed=args.seed)
283
+ else:
284
+ from aug_mpc.training_algs.ppo.ppo import PPO
285
+
286
+ algo = PPO(env=env,
287
+ debug=args.db,
288
+ remote_db=args.rmdb,
289
+ seed=args.seed)
290
+ else:
291
+ from aug_mpc.training_algs.dummy.dummy import Dummy
292
+
293
+ algo=Dummy(env=env,
294
+ debug=args.db,
295
+ remote_db=args.rmdb,
296
+ seed=args.seed)
297
+
298
+ custom_args.update(args_dict)
299
+ custom_args.update(cluster_data)
300
+ custom_args.update(sim_data)
301
+
302
+ run_name=env_classname if args.run_name is None else args.run_name
303
+ algo.setup(run_name=run_name,
304
+ ns=args.ns,
305
+ verbose=True,
306
+ drop_dir_name=args.drop_dir,
307
+ custom_args=custom_args,
308
+ comment=args.comment,
309
+ eval=args.eval,
310
+ resume=args.resume,
311
+ model_path=mpath_full,
312
+ n_eval_timesteps=args.n_eval_timesteps,
313
+ dump_checkpoints=args.dump_checkpoints,
314
+ norm_obs=args.obs_norm,
315
+ rescale_obs=args.obs_rescale)
316
+
317
+ full_drop_dir=algo.drop_dir()
318
+ shared_drop_dir = StringTensorServer(length=1,
319
+ basename="SharedTrainingDropDir",
320
+ name_space=args.ns,
321
+ verbose=True,
322
+ vlevel=VLevel.V2,
323
+ force_reconnection=True)
324
+ shared_drop_dir.run()
325
+
326
+ while True:
327
+ if not shared_drop_dir.write_vec([full_drop_dir], 0):
328
+ ns=1000000000
329
+ PerfSleep.thread_sleep(ns)
330
+ continue
331
+ else:
332
+ break
333
+
334
+ if args.step_while_setup:
335
+ # stop dummy step thread and give algo authority on step
336
+ dummy_step_exit_req=True
337
+ # wait for thread to join
338
+ if dummy_step_thread is not None:
339
+ dummy_step_thread.join()
340
+ Journal.log("launch_train_env.py",
341
+ "",
342
+ f"Dummy env step thread joined. Moving step authority to algo.",
343
+ LogType.INFO)
344
+
345
+ eval=args.eval
346
+ if args.override_agent_actions:
347
+ eval=True
348
+ if not eval:
349
+ while not exit_request:
350
+ if not algo.learn():
351
+ break
352
+ else: # eval phase
353
+ with torch.no_grad(): # no need for grad computation
354
+ while not exit_request:
355
+ if not algo.eval():
356
+ break
357
+
358
+ algo.done() # make sure to terminate training properly