AndrePatri commited on
Commit
86d7bf7
·
verified ·
1 Parent(s): 6f41411

Upload folder using huggingface_hub

Browse files
bundle.yaml ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ bundle_format: augmpc_model_bundle_v1
2
+ bundle_name: d2026_03_07_h19_m22_s30-CentauroCloopPartialNoYawUb_FakePosTrackingEnv
3
+ checkpoint_file: d2026_03_07_h19_m22_s30-CentauroCloopPartialNoYawUb_FakePosTrackingEnv_model
4
+ preserved_training_cfgs:
5
+ - ibrido_run__2026_03_07_19_20_05_ID/training_cfg_centauro_no_yaw_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
centauro_big_wheels_no_yaw_ub_2026_03_07_19_20_05_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>
centauro_big_wheels_no_yaw_ub_2026_03_07_19_20_05_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>
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)
centauro_rhc_wheels_continuous_no_yaw_ub.yaml ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ - v_regularization
50
+ - a_regularization
51
+ # - force_regularization
52
+
53
+ .define:
54
+ - &w_base_omega 15.
55
+ - &w_base_vxy 20.
56
+ - &w_base_vz 20.
57
+ - &w_base_z 15.
58
+ - &w_contact_z 225.0
59
+ - &w_contact_vz 250.0
60
+ - &w_contact_xy 60.0
61
+ - &w_contact_vxy 50.0
62
+ - &w_base_capture 200.
63
+ - &wheel_radius 0.124
64
+
65
+ base_lin_velxy:
66
+ type: Cartesian
67
+ distal_link: base_link
68
+ indices: [0, 1]
69
+ nodes: ${range(1, N-5)}
70
+ cartesian_type: velocity
71
+ weight: *w_base_vxy
72
+
73
+ base_lin_velz:
74
+ type: Cartesian
75
+ distal_link: base_link
76
+ indices: [2]
77
+ nodes: ${range(1, N-5)}
78
+ cartesian_type: velocity
79
+ weight: *w_base_vz
80
+
81
+ base_omega:
82
+ type: Cartesian
83
+ distal_link: base_link
84
+ indices: [3, 4, 5]
85
+ nodes: ${range(1, N-5)}
86
+ cartesian_type: velocity
87
+ weight: *w_base_omega
88
+
89
+ base_capture:
90
+ type: Cartesian
91
+ distal_link: base_link
92
+ indices: [0, 1, 2, 3, 4, 5]
93
+ nodes: ${range(N-5, N+1)}
94
+ cartesian_type: velocity
95
+ weight: *w_base_capture
96
+
97
+ # ===============================
98
+
99
+ rolling_contact_1:
100
+ type: Rolling
101
+ frame: wheel_1
102
+ radius: *wheel_radius
103
+
104
+ rolling_contact_2:
105
+ type: Rolling
106
+ frame: wheel_2
107
+ radius: *wheel_radius
108
+
109
+ rolling_contact_3:
110
+ type: Rolling
111
+ frame: wheel_3
112
+ radius: *wheel_radius
113
+
114
+ rolling_contact_4:
115
+ type: Rolling
116
+ frame: wheel_4
117
+ radius: *wheel_radius
118
+
119
+ # ==================================
120
+
121
+ interaction_contact_1:
122
+ type: VertexForce
123
+ frame: contact_1
124
+ fn_min: 100.0
125
+ enable_fc: true
126
+ friction_coeff: 0.5
127
+ vertex_frames:
128
+ - wheel_1
129
+
130
+ interaction_contact_2:
131
+ type: VertexForce
132
+ frame: contact_2
133
+ fn_min: 100.0
134
+ enable_fc: true
135
+ friction_coeff: 0.5
136
+ vertex_frames:
137
+ - wheel_2
138
+
139
+ interaction_contact_3:
140
+ type: VertexForce
141
+ frame: contact_3
142
+ fn_min: 100.0
143
+ enable_fc: true
144
+ friction_coeff: 0.5
145
+ vertex_frames:
146
+ - wheel_3
147
+
148
+ interaction_contact_4:
149
+ type: VertexForce
150
+ frame: contact_4
151
+ fn_min: 100.0
152
+ enable_fc: true
153
+ friction_coeff: 0.5
154
+ vertex_frames:
155
+ - wheel_4
156
+
157
+ contact_1:
158
+ type: Contact
159
+ subtask: [interaction_contact_1, rolling_contact_1]
160
+
161
+ contact_2:
162
+ type: Contact
163
+ subtask: [interaction_contact_2, rolling_contact_2]
164
+
165
+ contact_3:
166
+ type: Contact
167
+ subtask: [interaction_contact_3, rolling_contact_3]
168
+
169
+ contact_4:
170
+ type: Contact
171
+ subtask: [interaction_contact_4, rolling_contact_4]
172
+
173
+ joint_posture_capture:
174
+ type: Postural
175
+ weight: [45.0, 10.0, 10.0, 10.0,
176
+ 45.0, 10.0, 10.0, 10.0,
177
+ 45.0, 10.0, 10.0, 10.0,
178
+ 45.0, 10.0, 10.0, 10.0,
179
+ 55.0,
180
+ 25.0, 25.0, 25.0, 25.0, 25.0, 25.0,
181
+ 25.0, 25.0, 25.0, 25.0, 25.0, 25.0]
182
+ indices: [0, 1, 2, 3,
183
+ 6, 7, 8, 9,
184
+ 12, 13, 14, 15,
185
+ 18, 19, 20, 21,
186
+ 24,
187
+ 25, 26, 27, 28, 29, 30,
188
+ 31, 32, 33, 34, 35, 36]
189
+ nodes: ${range(N-5, N+1)}
190
+
191
+ v_regularization:
192
+ type: Regularization
193
+ variable_name: v
194
+ nodes: ${range(0, N+1)}
195
+ indices: [0, 1, 2, 3, 4, 5,
196
+ 6, 7, 8, 9, 10,
197
+ 11, 12, 13, 14, 15,
198
+ 16, 17, 18, 19, 20,
199
+ 21, 21, 23, 24, 25,
200
+ 26,
201
+ 27, 28, 29, 30, 31, 32,
202
+ 33, 34, 35, 36, 37, 38]
203
+ weight: [2e-1, 2e-1, 2e-1, 2e-1, 2e-1, 2e-1,
204
+ 1e0, 8e-1, 8e-1, 8e-1, 1e0,
205
+ 1e0, 8e-1, 8e-1, 8e-1, 1e0,
206
+ 1e0, 8e-1, 8e-1, 8e-1, 1e0,
207
+ 1e0, 8e-1, 8e-1, 8e-1, 1e0,
208
+ 2e0,
209
+ 2e0, 2e0, 2e0, 2e0, 2e0, 2e0,
210
+ 2e0, 2e0, 2e0, 2e0, 2e0, 2e0]
211
+
212
+ a_regularization:
213
+ type: Regularization
214
+ variable_name: a
215
+ nodes: ${range(0, N+1)}
216
+ indices: [0, 1, 2, 3, 4, 5,
217
+ 6, 7, 8, 9, 10,
218
+ 11, 12, 13, 14, 15,
219
+ 16, 17, 18, 19, 20,
220
+ 21, 21, 23, 24, 25,
221
+ 26,
222
+ 27, 28, 29, 30, 31, 32,
223
+ 33, 34, 35, 36, 37, 38]
224
+ weight: [4e-1, 4e-1, 4e-1, 4e-1, 4e-1, 4e-1,
225
+ 4e-1, 4e-1, 4e-1, 4e-1, 8e-1,
226
+ 4e-1, 4e-1, 4e-1, 4e-1, 8e-1,
227
+ 4e-1, 4e-1, 4e-1, 4e-1, 8e-1,
228
+ 4e-1, 4e-1, 4e-1, 4e-1, 8e-1,
229
+ 4e-1,
230
+ 4e-1, 4e-1, 4e-1, 4e-1, 4e-1, 4e-1,
231
+ 4e-1, 4e-1, 4e-1, 4e-1, 4e-1, 4e-1]
232
+
233
+ # flight phase traj tracking
234
+ z_contact_1:
235
+ type: Cartesian
236
+ distal_link: contact_1
237
+ indices: [2]
238
+ cartesian_type: position
239
+ weight: *w_contact_z
240
+
241
+ z_contact_2:
242
+ type: Cartesian
243
+ distal_link: contact_2
244
+ indices: [2]
245
+ cartesian_type: position
246
+ weight: *w_contact_z
247
+
248
+ z_contact_3:
249
+ type: Cartesian
250
+ distal_link: contact_3
251
+ indices: [2]
252
+ cartesian_type: position
253
+ weight: *w_contact_z
254
+
255
+ z_contact_4:
256
+ type: Cartesian
257
+ distal_link: contact_4
258
+ indices: [2]
259
+ cartesian_type: position
260
+ weight: *w_contact_z
261
+
262
+ xy_contact_1:
263
+ type: Cartesian
264
+ distal_link: contact_1
265
+ indices: [0, 1]
266
+ cartesian_type: position
267
+ weight: *w_contact_xy
268
+
269
+ xy_contact_2:
270
+ type: Cartesian
271
+ distal_link: contact_2
272
+ indices: [0, 1]
273
+ cartesian_type: position
274
+ weight: *w_contact_xy
275
+
276
+ xy_contact_3:
277
+ type: Cartesian
278
+ distal_link: contact_3
279
+ indices: [0, 1]
280
+ cartesian_type: position
281
+ weight: *w_contact_xy
282
+
283
+ xy_contact_4:
284
+ type: Cartesian
285
+ distal_link: contact_4
286
+ indices: [0, 1]
287
+ cartesian_type: position
288
+ weight: *w_contact_xy
289
+
290
+ vz_contact_1:
291
+ type: Cartesian
292
+ distal_link: contact_1
293
+ indices: [2]
294
+ cartesian_type: velocity
295
+ weight: *w_contact_vz
296
+
297
+ vz_contact_2:
298
+ type: Cartesian
299
+ distal_link: contact_2
300
+ indices: [2]
301
+ cartesian_type: velocity
302
+ weight: *w_contact_vz
303
+
304
+ vz_contact_3:
305
+ type: Cartesian
306
+ distal_link: contact_3
307
+ indices: [2]
308
+ cartesian_type: velocity
309
+ weight: *w_contact_vz
310
+
311
+ vz_contact_4:
312
+ type: Cartesian
313
+ distal_link: contact_4
314
+ indices: [2]
315
+ cartesian_type: velocity
316
+ weight: *w_contact_vz
317
+
318
+ vxy_contact_1:
319
+ type: Cartesian
320
+ distal_link: contact_1
321
+ indices: [0, 1]
322
+ cartesian_type: velocity
323
+ weight: *w_contact_vxy
324
+
325
+ vxy_contact_2:
326
+ type: Cartesian
327
+ distal_link: contact_2
328
+ indices: [0, 1]
329
+ cartesian_type: velocity
330
+ weight: *w_contact_vxy
331
+
332
+ vxy_contact_3:
333
+ type: Cartesian
334
+ distal_link: contact_3
335
+ indices: [0, 1]
336
+ cartesian_type: velocity
337
+ weight: *w_contact_vxy
338
+
339
+ vxy_contact_4:
340
+ type: Cartesian
341
+ distal_link: contact_4
342
+ indices: [0, 1]
343
+ cartesian_type: velocity
344
+ weight: *w_contact_vxy
d2026_03_07_h19_m22_s30-CentauroCloopPartialNoYawUb_FakePosTrackingEnv_model ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1b1bb0640bc5b6daf899d3b2fa5be08e9a243dd72b8eeeae32e7ad121ed3deb4
3
+ size 132
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
+
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]
ibrido_run__2026_03_07_19_20_05_ID/training_cfg_centauro_no_yaw_ub_cloop.sh ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ export EVAL=0
3
+ export DET_EVAL=1
4
+ export EVAL_ON_CPU=1
5
+ export OVERRIDE_ENV=1
6
+ export OVERRIDE_AGENT_REFS=1
7
+ export MPATH="/root/training_data/"
8
+ export MNAME=""
9
+
10
+ export WANDB_KEY="25f235316292344cea6dfa68e7c95409b3374d03"
11
+ export SHM_NS="centauro_big_wheels_no_yaw_ub" # shared mem namespace used for all shared data on CPU
12
+ export N_ENVS=800 # number of env to run in parallel
13
+ export RNAME="CentauroCloopPartialNoYawUb" # a descriptive base name for this run
14
+ export SEED=98 # random n generator seed to be used for this run
15
+ export REMOTE_STEPPING=1
16
+ export COMPRESSION_RATIO=0.6
17
+ export ACTOR_LWIDTH=128
18
+ export ACTOR_DEPTH=3
19
+ export CRITIC_LWIDTH=256
20
+ export CRITIC_DEPTH=4
21
+ export OBS_NORM=1
22
+ export OBS_RESCALING=0
23
+ export CRITIC_ACTION_RESCALE=1
24
+ export WEIGHT_NORM=1
25
+ export LAYER_NORM=0
26
+ export BATCH_NORM=0
27
+ export IS_CLOSED_LOOP=1
28
+ export DEBUG=1
29
+ export RMDEBUG=1
30
+ export DUMP_ENV_CHECKPOINTS=1
31
+ export DEMO_STOP_THRESH=10.0
32
+ export TOT_STEPS=20000000
33
+ export USE_RND=0
34
+ export DEMO_ENVS_PERC=0.0
35
+ export EXPL_ENVS_PERC=0.0
36
+ export ACTION_REPEAT=3
37
+ export USE_SAC=1
38
+ export USE_DUMMY=0
39
+ export DISCOUNT_FACTOR=0.99
40
+ export USE_PERIOD_RESETS=0
41
+ export COMMENT='centauro big wheels (fixed ankle yaw) with upper body CLOOP, a bit of pushes' # any training comment
42
+ export URDF_PATH="${HOME}/ibrido_ws/src/iit-centauro-ros-pkg/centauro_urdf/urdf/centauro.urdf.xacro" # name of the description package for the robot
43
+ export SRDF_PATH="${HOME}/ibrido_ws/src/iit-centauro-ros-pkg/centauro_srdf/srdf/centauro.srdf.xacro" # base path where the description package for the robot are located
44
+ export JNT_IMP_CF_PATH="${HOME}/ibrido_ws/src/CentauroHybridMPC/centaurohybridmpc/config/jnt_imp_config_no_yaw_ub_open.yaml" # path to yaml file for jnt imp configuration
45
+ if (( $IS_CLOSED_LOOP )); then
46
+ export JNT_IMP_CF_PATH="${HOME}/ibrido_ws/src/CentauroHybridMPC/centaurohybridmpc/config/jnt_imp_config_no_yaw_ub.yaml"
47
+ fi
48
+
49
+ export CLUSTER_CL_FNAME="centaurohybridmpc.controllers.horizon_based.centauro_rhc_cluster_client" # base path where the description package for the robot are located
50
+ export CLUSTER_DT=0.04
51
+ export N_NODES=25
52
+ export CLUSTER_DB=1
53
+ export PHYSICS_DT=0.0005
54
+ export USE_GPU_SIM=1
55
+ # export CODEGEN_OVERRIDE_BDIR="none"
56
+ export CODEGEN_OVERRIDE_BDIR="${HOME}/aux_data/CentauroRHCLusterClient_${SHM_NS}/CodeGen/${SHM_NS}Rhc"
57
+ # export TRAIN_ENV_FNAME="twist_tracking_env"
58
+ # export TRAIN_ENV_CNAME="TwistTrackingEnv"
59
+ export TRAIN_ENV_FNAME="fake_pos_tracking_env"
60
+ export TRAIN_ENV_CNAME="FakePosTrackingEnv"
61
+ # export TRAIN_ENV_FNAME="fake_pos_track_env_phase_control"
62
+ # export TRAIN_ENV_CNAME="FakePosTrackEnvPhaseControl"
63
+ # export TRAIN_ENV_FNAME="fake_pos_track_env_phase_control_with_demo"
64
+ # export TRAIN_ENV_CNAME="FakePosTrackEnvPhaseControlWithDemo"
65
+ # export TRAIN_ENV_FNAME="fake_pos_tracking_with_demo"
66
+ # export TRAIN_ENV_CNAME="FakePosTrackingEnvWithDemo"
67
+ # export TRAIN_ENV_FNAME="linvel_env_with_demo"
68
+ # export TRAIN_ENV_CNAME="TwistTrackingEnvWithDemo"
69
+ export PUB_HEIGHTMAP=0
70
+ export BAG_SDT=90.0
71
+ export BRIDGE_DT=0.1
72
+ export DUMP_DT=50.0
73
+ export ENV_IDX_BAG=4
74
+ export ENV_IDX_BAG_DEMO=-1
75
+ export ENV_IDX_BAG_EXPL=-1
76
+ export SRDF_PATH_ROSBAG="${HOME}/aux_data/CentauroRHClusterClient_${SHM_NS}/$SHM_NS.srdf" # base path where the description package for the robot are located
77
+ export CUSTOM_ARGS_NAMES="use_random_pertub use_jnt_v_feedback step_height control_wheels fixed_flights adaptive_is lin_a_feedback closed_partial fix_yaw use_flat_ground estimate_v_root self_collide add_upper_body"
78
+ export CUSTOM_ARGS_DTYPE="bool bool float bool bool bool bool bool bool bool bool bool bool "
79
+ export CUSTOM_ARGS_VALS="true false 0.1 true true true false true true true false false true"
80
+ # export CUSTOM_ARGS_NAMES+=" contact_prims"
81
+ # export CUSTOM_ARGS_DTYPE+=" strlist"
82
+ # export CUSTOM_ARGS_VALS+=" wheel_1,wheel_2,wheel_3,wheel_4"
83
+ export SET_ULIM=1
84
+ export ULIM_N=131072 # maximum number of open file descriptors for each process (shared memory)
85
+ export TIMEOUT_MS=120000 # timeout after which each script autokills ([ms])
isaac_world_interface.py ADDED
The diff for this file is too large to render. See raw diff
 
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]
jnt_imp_config_no_yaw_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_*": [600, 10]
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]
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
+
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
launch_world_interface.py ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_world_module(env_path):
19
+ spec = importlib.util.spec_from_file_location("world_module", env_path)
20
+ world_module = importlib.util.module_from_spec(spec)
21
+ spec.loader.exec_module(world_module)
22
+ return world_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('--world_iface_fname', type=str,
69
+ default="aug_mpc_envs.world_interfaces.isaac_world_interface",
70
+ help="world interface 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
+ world_module=importlib.import_module(args.world_iface_fname)
125
+ classes_in_module = [name for name, obj in inspect.getmembers(world_module, inspect.isclass)
126
+ if obj.__module__ == world_module.__name__]
127
+ if len(classes_in_module) == 1:
128
+ cluster_classname=classes_in_module[0]
129
+ WorldInterface = getattr(world_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 world file {args.world_iface_fname}. Found: {class_list_str}",
135
+ LogType.EXCEP,
136
+ throw_when_excep = False)
137
+ exit()
138
+
139
+ world_interface = WorldInterface(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
+ # reset_ok=world_interface.reset(reset_sim=True)
157
+ # if not reset_ok:
158
+ # world_interface.close()
159
+ # exit()
160
+
161
+ rt_factor = RtFactor(dt_nom=world_interface.physics_dt(),
162
+ window_size=100)
163
+
164
+ while True:
165
+
166
+ if rt_factor.reset_due():
167
+ rt_factor.reset()
168
+
169
+ step_ok=world_interface.step()
170
+
171
+ if not step_ok:
172
+ break
173
+
174
+ rt_factor.update()
175
+
176
+ for i in range(len(robot_names)):
177
+ robot_name=robot_names[i]
178
+ n_steps = world_interface.cluster_sim_step_counters[robot_name]
179
+ sol_counter = world_interface.cluster_servers[robot_name].solution_counter()
180
+ trigger_counter = world_interface.cluster_servers[robot_name].trigger_counter()
181
+ shared_sim_infos[i].write(dyn_info_name=["sim_rt_factor",
182
+ "total_rt_factor",
183
+ "env_stepping_dt",
184
+ "world_stepping_dt",
185
+ "time_to_get_states_from_env",
186
+ "cluster_state_update_dt",
187
+ "cluster_sol_time",
188
+ "n_sim_steps",
189
+ "n_cluster_trigger_steps",
190
+ "n_cluster_sol_steps",
191
+ "sim_time",
192
+ "cluster_time"],
193
+ val=[rt_factor.get(),
194
+ rt_factor.get() * num_envs,
195
+ rt_factor.get_avrg_step_time(),
196
+ world_interface.debug_data["time_to_step_world"],
197
+ world_interface.debug_data["time_to_get_states_from_env"],
198
+ world_interface.debug_data["cluster_state_update_dt"][robot_name],
199
+ world_interface.debug_data["cluster_sol_time"][robot_name],
200
+ n_steps,
201
+ trigger_counter,
202
+ sol_counter,
203
+ world_interface.debug_data["sim_time"][robot_name],
204
+ sol_counter*world_interface.cluster_servers[robot_name].cluster_dt()
205
+ ])
206
+
207
+ world_interface.close()
other/__init__.py ADDED
File without changes
other/augmpc_cluster_client.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ # call _xrdf_cmds_override in case some cmds need to be overridden
84
+ override_cmds=self._xrdf_cmds_override()
85
+ cmds=merge_xacro_cmds(prev_cmds=cmds,
86
+ new_cmds=override_cmds)
87
+
88
+ self._srdf_path=generate_srdf(robot_name=namespace,
89
+ xacro_path=self._srdf_xacro_path,
90
+ dump_path=self._temp_path,
91
+ xrdf_cmds=cmds)
92
+
93
+ def _generate_urdf(self,namespace:str):
94
+
95
+ custom_xacro_args=extract_custom_xacro_args(self._custom_opts)
96
+ cmds=merge_xacro_cmds(prev_cmds=self._xrdf_cmds(),
97
+ new_cmds=custom_xacro_args)
98
+
99
+ # call _xrdf_cmds_override in case some cmds need to be overridden
100
+ override_cmds=self._xrdf_cmds_override()
101
+ cmds=merge_xacro_cmds(prev_cmds=cmds,
102
+ new_cmds=override_cmds)
103
+
104
+ self._urdf_path=generate_urdf(robot_name=namespace,
105
+ xacro_path=self._urdf_xacro_path,
106
+ dump_path=self._temp_path,
107
+ xrdf_cmds=cmds)
108
+
109
+ @abstractmethod
110
+ def _xrdf_cmds(self):
111
+
112
+ # to be implemented by parent class (
113
+ # for an example have a look at utils/centauro_xrdf_gen.py)
114
+
115
+ pass
116
+
117
+ def _xrdf_cmds_override(self):
118
+
119
+ # to be overridden by parent class
120
+
121
+ to_be_overridden = ["dummy_cmd:=true"]
122
+
123
+ return to_be_overridden
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)
other/horizon_based/__init__.py ADDED
File without changes
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
other/horizon_based/gait_manager.py ADDED
@@ -0,0 +1,566 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ self._touchdown_phases[contact]=None
199
+
200
+ if self._zpos_task_found: # we use pos trajectory
201
+ self._ref_trjs[contact]=np.zeros(shape=[7, self.task_interface.prb.getNNodes()])
202
+ init_z_foot = self._fk_contacts[contact](q=self._q0)['ee_pos'].elements()[2]
203
+ if self._is_open_loop:
204
+ self._ref_trjs[contact][2, :] = np.atleast_2d(init_z_foot)
205
+ else:
206
+ self._ref_trjs[contact][2, :] = 0.0 # place foot at ground level initially ()
207
+
208
+ # z
209
+ self._flight_phases[contact].addItemReference(self.task_interface.getTask(f'z_{contact}'),
210
+ self._ref_trjs[contact][2, 0:1],
211
+ nodes=list(range(0, flight_phase_short_duration)))
212
+
213
+ if self._xypos_task_found: # xy, we add a landing phase of unit duration to enforce landing pos costs
214
+
215
+ self._touchdown_phases[contact]=self._contact_timelines[contact].createPhase(flight_phase_short_duration,
216
+ f'touchdown_{contact}_short')
217
+
218
+ self._touchdown_phases[contact].addItemReference(self.task_interface.getTask(f'xy_{contact}'),
219
+ self._ref_trjs[contact][0:2, 0:1],
220
+ nodes=list(range(0, short_stance_duration)))
221
+
222
+ else: # foot traj in velocity
223
+ # ref vel traj
224
+ self._ref_vtrjs[contact]=np.zeros(shape=[7, self.task_interface.prb.getNNodes()]) # allocate traj
225
+ # of max length eual to number of nodes
226
+ self._ref_vtrjs[contact][2, :] = np.atleast_2d(0)
227
+
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
+
233
+ if self._xyvel_task_found: # xy (when in vel the xy vel is set on the whole flight phase)
234
+ self._flight_phases[contact].addItemReference(self.task_interface.getTask(f'vxy_{contact}'),
235
+ self._ref_vtrjs[contact][0:2, 0:1],
236
+ nodes=list(range(0, flight_phase_short_duration)))
237
+
238
+ if self._vertical_landing: # add touchdown phase for vertical landing
239
+ self._touchdown_phases[contact]=self._contact_timelines[contact].createPhase(flight_phase_short_duration,
240
+ f'touchdown_{contact}_short')
241
+
242
+ if self._vertical_landing and self._touchdown_phases[contact] is not None:
243
+ v_xy=self._fkd_contacts[contact](q=self._model.q, qdot=self._model.v)['ee_vel_linear'][0:2]
244
+ vertical_landing=self.task_interface.prb.createResidual(f'{contact}_only_vert_v',
245
+ self._landing_vert_weight * v_xy,
246
+ nodes=[])
247
+ self._touchdown_phases[contact].addCost(vertical_landing, nodes=list(range(0, short_stance_duration)))
248
+
249
+ if self._keep_yaw_vert:
250
+ # keep ankle vertical over the whole horizon (can be useful with wheeled robots)
251
+ c_ori = self._model.kd.fk(contact)(q=self._model.q)['ee_rot'][2, :]
252
+ cost_ori = self.task_interface.prb.createResidual(f'{contact}_ori', self._yaw_vertical_weight * (c_ori.T - np.array([0, 0, 1])))
253
+ # flight_phase.addCost(cost_ori, nodes=list(range(0, flight_duration+post_landing_stance)))
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: # we use _ref_trjs to write xy pos references
437
+ self._ref_trjs[contact_name][0, -1]=starting_x_pos+land_dx_w
438
+ self._ref_trjs[contact_name][1, -1]=starting_y_pos+land_dy_w
439
+
440
+ for i in range(flight_duration_req):
441
+ res, phase_token_flight=timeline.addPhase(self._flight_phases[contact_name],
442
+ pos=self._injection_node+i,
443
+ absolute_position=True)
444
+ phase_token_flight.setItemReference(f'z_{contact_name}',
445
+ self._ref_trjs[contact_name][:, i])
446
+
447
+ if self._touchdown_phases[contact_name] is not None:
448
+ # add touchdown phase after flight
449
+ res, phase_token_touchdown=timeline.addPhase(self._touchdown_phases[contact_name],
450
+ pos=self._injection_node+flight_duration_req,
451
+ absolute_position=True)
452
+ if self._xypos_task_found:
453
+ phase_token_touchdown.setItemReference(f'xy_{contact_name}',
454
+ self._ref_trjs[contact_name][:, -1])
455
+
456
+ # inject vel traj if vel mode
457
+ if self._ref_vtrjs[contact_name] is not None:
458
+ self._ref_vtrjs[contact_name][2, 0:flight_duration_req]=np.atleast_2d(self._tg.derivative_of_trajectory(
459
+ flight_duration_req,
460
+ p_start=0.0,
461
+ p_goal=flight_enddh_req,
462
+ clearance=flight_apex_req,
463
+ derivatives=self._traj_der,
464
+ second_der=self._traj_second_der,
465
+ third_der=self._third_traj_der))
466
+ if self._xyvel_task_found: # compute vel reference using problem dt and flight length
467
+ flight_duration_sec=float(flight_duration_req)*self._dt
468
+ self._ref_vtrjs[contact_name][0, 0:flight_duration_req]=land_dx_w/flight_duration_sec
469
+ self._ref_vtrjs[contact_name][1, 0:flight_duration_req]=land_dy_w/flight_duration_sec
470
+
471
+ for i in range(flight_duration_req):
472
+ res, phase_token=timeline.addPhase(self._flight_phases[contact_name],
473
+ pos=self._injection_node+i,
474
+ absolute_position=True)
475
+ phase_token.setItemReference(f'vz_{contact_name}',
476
+ self._ref_vtrjs[contact_name][2:3, i:i+1])
477
+ if self._touchdown_phases[contact_name] is not None:
478
+ # add touchdown phase for forcing vertical landing
479
+ res, phase_token=timeline.addPhase(self._touchdown_phases[contact_name],
480
+ pos=self._injection_node+flight_duration_req,
481
+ absolute_position=True)
482
+
483
+ if timeline.getEmptyNodes() > 0: # fill empty nodes at the end of the horizon, if any, with stance
484
+ timeline.addPhase(timeline.getRegisteredPhase(f'stance_{contact_name}_short'))
485
+
486
+ def update(self):
487
+ self._phase_manager.update()
488
+
489
+ def update_flight_info(self, timeline_name):
490
+
491
+ # get current position and remaining duration of flight phases over the horizon for a single contact
492
+
493
+ # phase indexes over timeline
494
+ phase_idxs=self._contact_timelines[timeline_name].getPhaseIdx(self._flight_phases[timeline_name])
495
+
496
+ if not len(phase_idxs)==0: # at least one flight phase on horizon -> read info from timeline
497
+
498
+ # all active phases on timeline
499
+ active_phases=self._contact_timelines[timeline_name].getActivePhases()
500
+
501
+ phase_idx_start=phase_idxs[0]
502
+ # active_nodes_start=active_phases[phase_idx_start].getActiveNodes()
503
+ pos_start=active_phases[phase_idx_start].getPosition()
504
+ # n_nodes_start=active_phases[phase_idx_start].getNNodes()
505
+
506
+ phase_idx_last=phase_idxs[-1] # just get info for last phase on the horizon
507
+ # active_nodes_last=active_phases[phase_idx_last].getActiveNodes()
508
+ pos_last=active_phases[phase_idx_last].getPosition()
509
+ # n_nodes_last=active_phases[phase_idx_last].getNNodes()
510
+
511
+ # write to info
512
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+0*self.n_contacts]=pos_last
513
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+1*self.n_contacts]=pos_last - pos_start
514
+
515
+ return True
516
+
517
+ return False
518
+
519
+ def get_flight_info(self, timeline_name):
520
+ return (self._flight_info_now[self._name_to_idx_map[timeline_name]+0*self.n_contacts],
521
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+1*self.n_contacts],
522
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+2*self.n_contacts],
523
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+3*self.n_contacts],
524
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+4*self.n_contacts],
525
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+5*self.n_contacts],
526
+ self._flight_info_now[self._name_to_idx_map[timeline_name]+6*self.n_contacts])
527
+
528
+ def get_flight_info_all(self):
529
+ return self._flight_info_now
530
+
531
+ def set_ref_pos(self,
532
+ timeline_name:str,
533
+ ref_height: np.array = None,
534
+ threshold: float = 0.05):
535
+
536
+ if ref_height is not None:
537
+ self._ref_trjs[timeline_name][2, :]=ref_height
538
+ if ref_height>threshold:
539
+ self.add_flight(timeline_name=timeline_name)
540
+ this_flight_token_idx=self._contact_timelines[timeline_name].getPhaseIdx(self._flight_phases[timeline_name])[-1]
541
+ active_phases=self._contact_timelines[timeline_name].getActivePhases()
542
+ active_phases[this_flight_token_idx].setItemReference(f'z_{timeline_name}',
543
+ self._ref_trjs[timeline_name])
544
+ else:
545
+ self.add_stand(timeline_name=timeline_name)
546
+
547
+ def set_force_feedback(self,
548
+ timeline_name: str,
549
+ force_norm: float):
550
+
551
+ flight_tokens=self._contact_timelines[timeline_name].getPhaseIdx(self._flight_phases[timeline_name])
552
+ contact_tokens=self._contact_phases[timeline_name].getPhaseIdx(self._contact_phases[timeline_name])
553
+ if not len(flight_tokens)==0:
554
+ first_flight=flight_tokens[0]
555
+ first_flight
556
+
557
+ def check_horizon_full(self,
558
+ timeline_name):
559
+ timeline = self._contact_timelines[timeline_name]
560
+ if timeline.getEmptyNodes() > 0:
561
+ error = f"Empty nodes detected over the horizon for timeline {timeline}! Make sure to fill the whole horizon with valid phases!!"
562
+ Journal.log(self.__class__.__name__,
563
+ "check_horizon_full",
564
+ error,
565
+ LogType.EXCEP,
566
+ throw_when_excep = True)
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
+
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
other/horizon_based/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]
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
+
other/horizon_based/utils/__init__.py ADDED
File without changes
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
+
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
rhc.py ADDED
@@ -0,0 +1,1262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ self._class_name = self._class_name_base # will append controller index upon registration
145
+
146
+ self._contact_force_base_loc_aux=np.zeros((1,3),dtype=self._dtype)
147
+ self._norm_grav_vector_w=np.zeros((1,3),dtype=self._dtype)
148
+ self._norm_grav_vector_w[:, 2]=-1.0
149
+ self._norm_grav_vector_base_loc=np.zeros((1,3),dtype=self._dtype)
150
+
151
+ self._init() # initialize controller
152
+
153
+ if not hasattr(self, '_rhc_fpaths'):
154
+ self._rhc_fpaths = []
155
+ self._rhc_fpaths.append(os.path.abspath(__file__))
156
+
157
+ def __init_subclass__(cls, **kwargs):
158
+ super().__init_subclass__(**kwargs)
159
+ # Get the file path of the class being initialized and append it to the list
160
+ if not hasattr(cls, '_rhc_fpaths'):
161
+ cls._rhc_fpaths = []
162
+ child_class_file_path = os.path.abspath(inspect.getfile(cls))
163
+ cls._rhc_fpaths.append(child_class_file_path)
164
+
165
+ def this_paths(self):
166
+ return self._rhc_fpaths
167
+
168
+ def __del__(self):
169
+ self._close()
170
+
171
+ def _handle_sigint(self, signum, frame):
172
+ if self._verbose:
173
+ Journal.log(self._class_name,
174
+ "_handle_sigint",
175
+ "SIGINT received",
176
+ LogType.WARN)
177
+ self._term_req_received = True
178
+
179
+ def _set_rhc_pred_idx(self):
180
+ # default to last node
181
+ self._pred_node_idx=self._n_nodes-1
182
+
183
+ def _set_rhc_cmds_idx(self):
184
+ # use index 2 by default to compensate for
185
+ # the inevitable action delay
186
+ # (get_state, trigger sol -> +dt -> apply sol )
187
+ # if we apply action from second node (idenx 1)
188
+ # that action will already be one dt old. We assume we were already
189
+ # applying the right action to get to the state of node idx 1 and get the
190
+ # cmds for reaching the state at idx 1
191
+ self._rhc_cmds_node_idx=2
192
+
193
+ def _close(self):
194
+ if not self._closed:
195
+ self._unregister_from_cluster()
196
+ if self.robot_cmds is not None:
197
+ self.robot_cmds.close()
198
+ if self.robot_pred is not None:
199
+ self.robot_pred.close()
200
+ if self.rhc_pred_delta is not None:
201
+ self.rhc_pred_delta.close()
202
+ if self.robot_state is not None:
203
+ self.robot_state.close()
204
+ if self.rhc_status is not None:
205
+ self.rhc_status.close()
206
+ if self.rhc_internal is not None:
207
+ self.rhc_internal.close()
208
+ if self.cluster_stats is not None:
209
+ self.cluster_stats.close()
210
+ if self._remote_triggerer is not None:
211
+ self._remote_triggerer.close()
212
+ if self._remote_term is not None:
213
+ self._remote_term.close()
214
+ self._closed = True
215
+
216
+ def close(self):
217
+ self._close()
218
+
219
+ def get_file_paths(self):
220
+ # can be overriden by child
221
+ base_paths = []
222
+ base_paths.append(self._this_path)
223
+ return base_paths
224
+
225
+ def init_rhc_task_cmds(self):
226
+
227
+ self.rhc_refs = self._init_rhc_task_cmds()
228
+ self.rhc_refs.reset()
229
+
230
+ def _init_states(self):
231
+
232
+ quat_remap = self._get_quat_remap()
233
+ self.robot_state = RobotState(namespace=self.namespace,
234
+ is_server=False,
235
+ q_remapping=quat_remap, # remapping from environment to controller
236
+ with_gpu_mirror=False,
237
+ with_torch_view=False,
238
+ safe=False,
239
+ verbose=self._verbose,
240
+ vlevel=VLevel.V2,
241
+ optimize_mem=True,
242
+ n_robots=1, # we just need the row corresponding to this controller
243
+ n_jnts=None, # got from server
244
+ n_contacts=None # got from server
245
+ )
246
+ self.robot_state.run()
247
+ self.robot_cmds = RhcCmds(namespace=self.namespace,
248
+ is_server=False,
249
+ q_remapping=quat_remap, # remapping from environment to controller
250
+ with_gpu_mirror=False,
251
+ with_torch_view=False,
252
+ safe=False,
253
+ verbose=self._verbose,
254
+ vlevel=VLevel.V2,
255
+ optimize_mem=True,
256
+ n_robots=1, # we just need the row corresponding to this controller
257
+ n_jnts=None, # got from server
258
+ n_contacts=None # got from server
259
+ )
260
+ self.robot_cmds.run()
261
+ self.robot_pred = RhcPred(namespace=self.namespace,
262
+ is_server=False,
263
+ q_remapping=quat_remap, # remapping from environment to controller
264
+ with_gpu_mirror=False,
265
+ with_torch_view=False,
266
+ safe=False,
267
+ verbose=self._verbose,
268
+ vlevel=VLevel.V2,
269
+ optimize_mem=True,
270
+ n_robots=1, # we just need the row corresponding to this controller
271
+ n_jnts=None, # got from server
272
+ n_contacts=None # got from server
273
+ )
274
+ self.robot_pred.run()
275
+ self.rhc_pred_delta = RhcPredDelta(namespace=self.namespace,
276
+ is_server=False,
277
+ q_remapping=quat_remap, # remapping from environment to controller
278
+ with_gpu_mirror=False,
279
+ with_torch_view=False,
280
+ safe=False,
281
+ verbose=self._verbose,
282
+ vlevel=VLevel.V2,
283
+ optimize_mem=True,
284
+ n_robots=1, # we just need the row corresponding to this controller
285
+ n_jnts=None, # got from server
286
+ n_contacts=None # got from server
287
+ )
288
+ self.rhc_pred_delta.run()
289
+
290
+ def _rhc(self, rti: bool = True):
291
+ if self._debug:
292
+ self._rhc_db(rti=rti)
293
+ else:
294
+ self._rhc_min(rti=rti)
295
+
296
+ def _rhc_db(self, rti: bool = True):
297
+ # rhc with debug data
298
+ self._start_time = time.perf_counter()
299
+
300
+ self.robot_state.synch_from_shared_mem(robot_idx=self.controller_index, robot_idx_view=self.controller_index_np) # updates robot state with
301
+ # latest data on shared mem
302
+
303
+ self._compute_pred_delta()
304
+
305
+ if not self.failed():
306
+ # we can solve only if not in failure state
307
+ if rti:
308
+ self._failed = not self._solve() # solve actual TO with RTI
309
+ else:
310
+ self._failed = not self._bootstrap() # full bootstrap solve
311
+ if (self._failed):
312
+ # perform failure procedure
313
+ self._on_failure()
314
+ else:
315
+ if not self._allow_triggering_when_failed:
316
+ Journal.log(self._class_name,
317
+ "solve",
318
+ f"Received solution req, but controller is in failure state. " + \
319
+ " You should have reset() the controller!",
320
+ LogType.EXCEP,
321
+ throw_when_excep = True)
322
+ else:
323
+ if self._verbose:
324
+ Journal.log(self._class_name,
325
+ "solve",
326
+ f"Received solution req, but controller is in failure state. No solution will be performed. " + \
327
+ " Use the reset() method to continue solving!",
328
+ LogType.WARN)
329
+
330
+ self._write_cmds_from_sol() # we update update the views of the cmds
331
+ # from the latest solution
332
+
333
+ # in debug, rhc internal state is streamed over
334
+ # shared mem.
335
+ self._update_rhc_internal()
336
+ self._profiling_data_dict["full_solve_dt"] = time.perf_counter() - self._start_time
337
+ self._update_profiling_data() # updates all profiling data
338
+ if self._verbose:
339
+ Journal.log(self._class_name,
340
+ "solve",
341
+ f"RHC full solve loop execution time -> " + str(self._profiling_data_dict["full_solve_dt"]),
342
+ LogType.INFO,
343
+ throw_when_excep = True)
344
+
345
+ def _rhc_min(self, rti: bool = True):
346
+
347
+ self.robot_state.synch_from_shared_mem(robot_idx=self.controller_index, robot_idx_view=self.controller_index_np) # updates robot state with
348
+ # latest data on shared mem
349
+
350
+ self._compute_pred_delta()
351
+
352
+ if not self.failed():
353
+ # we can solve only if not in failure state
354
+ if rti:
355
+ self._failed = not self._solve() # solve actual TO with RTI
356
+ else:
357
+ self._failed = not self._bootstrap() # full bootstrap solve
358
+ if (self._failed):
359
+ # perform failure procedure
360
+ self._on_failure()
361
+ else:
362
+ if not self._allow_triggering_when_failed:
363
+ Journal.log(self._class_name,
364
+ "solve",
365
+ f"Received solution req, but controller is in failure state. " + \
366
+ " You should have reset() the controller!",
367
+ LogType.EXCEP,
368
+ throw_when_excep = True)
369
+ else:
370
+ if self._verbose:
371
+ Journal.log(self._class_name,
372
+ "solve",
373
+ f"Received solution req, but controller is in failure state. No solution will be performed. " + \
374
+ " Use the reset() method to continue solving!",
375
+ LogType.WARN)
376
+
377
+ self._write_cmds_from_sol() # we update the views of the cmds
378
+ # from the latest solution even if failed
379
+
380
+ def solve_once(self):
381
+ # run a single iteration of the solve loop (used for pooling)
382
+ if self._term_req_received:
383
+ return False
384
+
385
+ if not self._remote_triggerer.wait(self._remote_triggerer_timeout):
386
+ Journal.log(self._class_name,
387
+ f"solve",
388
+ "Didn't receive any remote trigger req within timeout!",
389
+ LogType.EXCEP,
390
+ throw_when_excep = False)
391
+ return False
392
+
393
+ self._received_trigger = True
394
+
395
+ if self.rhc_status.resets.read_retry(row_index=self.controller_index,
396
+ col_index=0,
397
+ row_index_view=0)[0]:
398
+ self.reset() # rhc is reset
399
+
400
+ if self.rhc_status.trigger.read_retry(row_index=self.controller_index,
401
+ col_index=0,
402
+ row_index_view=0)[0]:
403
+ rti_solve = self.rhc_status.rti_solve.read_retry(row_index=self.controller_index,
404
+ col_index=0,
405
+ row_index_view=0)[0]
406
+ self._rhc(rti=rti_solve) # run solution with requested solve mode
407
+ self.rhc_status.trigger.write_retry(False,
408
+ row_index=self.controller_index,
409
+ col_index=0,
410
+ row_index_view=0) # allow next solution trigger
411
+
412
+ self._remote_triggerer.ack() # send ack signal to server
413
+ self._received_trigger = False
414
+
415
+ self._term_req_received = self._term_req_received or self._remote_term.read_retry(row_index=0,
416
+ col_index=0,
417
+ row_index_view=0)[0]
418
+
419
+ if self._term_req_received:
420
+ self.close()
421
+ return False
422
+
423
+ return True
424
+
425
+ def solve(self):
426
+
427
+ # run the solution loop and wait for trigger signals
428
+ # using cond. variables (efficient)
429
+ while True:
430
+ if not self.solve_once():
431
+ break
432
+
433
+ self.close() # is not stricly necessary
434
+
435
+ def reset(self):
436
+
437
+ if not self._closed:
438
+ self.reset_rhc_data()
439
+ self._failed = False # allow triggering
440
+ self._n_resets += 1
441
+ self.rhc_status.fails.write_retry(False,
442
+ row_index=self.controller_index,
443
+ col_index=0,
444
+ row_index_view=0)
445
+ self.rhc_status.resets.write_retry(False,
446
+ row_index=self.controller_index,
447
+ col_index=0,
448
+ row_index_view=0)
449
+
450
+ def _create_jnt_maps(self):
451
+
452
+ # retrieve env-side joint names from shared mem
453
+ self._env_side_jnt_names = self.robot_state.jnt_names()
454
+ self._check_jnt_names_compatibility() # will raise exception if not self._allow_less_jnts
455
+ if not self._got_jnt_names_from_controllers:
456
+ exception = f"Cannot run the solve(). assign_env_side_jnt_names() was not called!"
457
+ Journal.log(self._class_name,
458
+ "_create_jnt_maps",
459
+ exception,
460
+ LogType.EXCEP,
461
+ throw_when_excep = True)
462
+
463
+ # robot homer guarantees that _controller_side_jnt_names is at least contained in self._env_side_jnt_names (or equal)
464
+ self._to_controller = [self._env_side_jnt_names.index(element) for element in self._controller_side_jnt_names]
465
+ # set joint remappings for shared data from env data to controller
466
+ # all shared data is by convention specified according to the env (jnts are ordered that way)
467
+ # the remapping is used so that when data is returned, its a remapped view from env to controller,
468
+ # so that data can be assigned direclty from the rhc controller without any issues
469
+ self.robot_state.set_jnts_remapping(jnts_remapping=self._to_controller)
470
+ self.robot_cmds.set_jnts_remapping(jnts_remapping=self._to_controller)
471
+ self.robot_pred.set_jnts_remapping(jnts_remapping=self._to_controller)
472
+ self.rhc_pred_delta.set_jnts_remapping(jnts_remapping=self._to_controller)
473
+
474
+ return True
475
+
476
+ def reset_rhc_data(self):
477
+
478
+ self._reset() # custom reset (e.g. it should set the current solution to some
479
+ # default solution, like a bootstrap)
480
+
481
+ self.rhc_refs.reset() # reset rhc refs to default (has to be called after _reset)
482
+
483
+ self._write_cmds_from_sol() # use latest solution (e.g. from bootstrap if called before running
484
+ # the first solve) as default state
485
+
486
+ def failed(self):
487
+ return self._failed
488
+
489
+ def robot_mass(self):
490
+ return self._robot_mass
491
+
492
+ def _assign_cntrl_index(self, reg_state: np.ndarray):
493
+ state = reg_state.flatten() # ensure 1D tensor
494
+ free_spots = np.nonzero(~state.flatten())[0]
495
+ return free_spots[0].item() # just return the first free spot
496
+
497
+ def _register_to_cluster(self):
498
+
499
+ self.rhc_status = RhcStatus(is_server=False,
500
+ namespace=self.namespace,
501
+ verbose=self._verbose,
502
+ vlevel=VLevel.V2,
503
+ with_torch_view=False,
504
+ with_gpu_mirror=False,
505
+ optimize_mem=True,
506
+ cluster_size=1, # we just need the row corresponding to this controller
507
+ n_contacts=None, # we get this from server
508
+ n_nodes=None # we get this from server
509
+ )
510
+ self.rhc_status.run() # rhc status (reg. flags, failure, tot cost, tot cnstrl viol, etc...)
511
+
512
+ # acquire semaphores since we have to perform non-atomic operations
513
+ # on the whole memory views
514
+ self.rhc_status.registration.data_sem_acquire()
515
+ self.rhc_status.controllers_counter.data_sem_acquire()
516
+ self.rhc_status.controllers_counter.synch_all(retry = True,
517
+ read = True)
518
+
519
+ available_spots = self.rhc_status.cluster_size
520
+ # from here on all pre registration ops can be done
521
+
522
+ # incrementing cluster controllers counter
523
+ controllers_counter = self.rhc_status.controllers_counter.get_numpy_mirror()
524
+ if controllers_counter[0, 0] + 1 > available_spots: # no space left -> return
525
+ self.rhc_status.controllers_counter.data_sem_release()
526
+ self.rhc_status.registration.data_sem_release()
527
+ exception = "Cannot register to cluster. No space left " + \
528
+ f"({controllers_counter[0, 0]} controllers already registered)"
529
+ Journal.log(self._class_name,
530
+ "_register_to_cluster",
531
+ exception,
532
+ LogType.EXCEP,
533
+ throw_when_excep = True)
534
+
535
+ # now we can register
536
+
537
+ # increment controllers counter
538
+ controllers_counter += 1
539
+ self.controller_index = controllers_counter.item() -1
540
+
541
+ # actually register to cluster
542
+ self.rhc_status.controllers_counter.synch_all(retry = True,
543
+ read = False) # writes to shared mem (just one for all controllers (i.e n_rows = 1))
544
+
545
+ # read current registration state
546
+ self.rhc_status.registration.synch_all(retry = True,
547
+ read = True,
548
+ row_index=self.controller_index,
549
+ row_index_view=0)
550
+ registrations = self.rhc_status.registration.get_numpy_mirror()
551
+ # self.controller_index = self._assign_cntrl_index(registrations)
552
+
553
+
554
+ self._class_name_base = self._class_name_base+str(self.controller_index)
555
+ # self.controller_index_np = np.array(self.controller_index)
556
+ 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, :])
557
+
558
+ registrations[self.controller_index_np, 0] = True
559
+ self.rhc_status.registration.synch_all(retry = True,
560
+ read = False,
561
+ row_index=self.controller_index,
562
+ col_index=0,
563
+ row_index_view=0)
564
+
565
+ # now all heavy stuff that would otherwise make the registration slow
566
+ self._remote_term = SharedTWrapper(namespace=self.namespace,
567
+ basename="RemoteTermination",
568
+ is_server=False,
569
+ verbose = self._verbose,
570
+ vlevel = VLevel.V2,
571
+ with_gpu_mirror=False,
572
+ with_torch_view=False,
573
+ dtype=dtype.Bool)
574
+ self._remote_term.run()
575
+
576
+ # other initializations
577
+
578
+ self.cluster_stats = RhcProfiling(is_server=False,
579
+ name=self.namespace,
580
+ verbose=self._verbose,
581
+ vlevel=VLevel.V2,
582
+ safe=True,
583
+ optimize_mem=True,
584
+ cluster_size=1 # we just need the row corresponding to this controller
585
+ ) # profiling data
586
+ self.cluster_stats.run()
587
+ self.cluster_stats.synch_info()
588
+
589
+ self._create_jnt_maps()
590
+ self.init_rhc_task_cmds() # initializes rhc interface to external commands (defined by child class)
591
+ self._consinstency_checks() # sanity checks
592
+
593
+ if self._homer is None:
594
+ self._init_robot_homer() # call this in case it wasn't called by child
595
+
596
+ self._robot_mass = self._get_robot_mass() # uses child class implemented method
597
+ self._contact_f_scale = self._get_robot_mass() * 9.81
598
+
599
+ # writing some static info about this controller
600
+ # self.rhc_status.rhc_static_info.synch_all(retry = True,
601
+ # read = True,
602
+ # row_index=self.controller_index,
603
+ # col_index=0) # first read current static info from other controllers (not necessary if optimize_mem==True)
604
+ self.rhc_status.rhc_static_info.set(data=np.array(self._dt),
605
+ data_type="dts",
606
+ rhc_idxs=self.controller_index_np,
607
+ gpu=False)
608
+ self.rhc_status.rhc_static_info.set(data=np.array(self._t_horizon),
609
+ data_type="horizons",
610
+ rhc_idxs=self.controller_index_np,
611
+ gpu=False)
612
+ self.rhc_status.rhc_static_info.set(data=np.array(self._n_nodes),
613
+ data_type="nnodes",
614
+ rhc_idxs=self.controller_index_np,
615
+ gpu=False)
616
+ # writing some static rhc info which is only available after problem init
617
+ self.rhc_status.rhc_static_info.set(data=np.array(len(self._get_contacts())),
618
+ data_type="ncontacts",
619
+ rhc_idxs=self.controller_index_np,
620
+ gpu=False)
621
+ self.rhc_status.rhc_static_info.set(data=np.array(self.robot_mass()),
622
+ data_type="robot_mass",
623
+ rhc_idxs=self.controller_index_np,
624
+ gpu=False)
625
+ self.rhc_status.rhc_static_info.set(data=np.array(self._pred_node_idx),
626
+ data_type="pred_node_idx",
627
+ rhc_idxs=self.controller_index_np,
628
+ gpu=False)
629
+
630
+ self.rhc_status.rhc_static_info.synch_retry(row_index=self.controller_index,
631
+ col_index=0,
632
+ row_index_view=0,
633
+ n_rows=1, n_cols=self.rhc_status.rhc_static_info.n_cols,
634
+ read=False)
635
+
636
+ # we set homings also for joints which are not in the rhc homing map
637
+ # since this is usually required on server side
638
+
639
+ homing_full = self._homer_env.get_homing().reshape(1,
640
+ self.robot_cmds.n_jnts())
641
+
642
+ null_action = np.zeros((1, self.robot_cmds.n_jnts()),
643
+ dtype=self._dtype)
644
+
645
+ self.robot_cmds.jnts_state.set(data=homing_full, data_type="q",
646
+ robot_idxs=self.controller_index_np,
647
+ no_remap=True)
648
+ self.robot_cmds.jnts_state.set(data=null_action, data_type="v",
649
+ robot_idxs=self.controller_index_np,
650
+ no_remap=True)
651
+ self.robot_cmds.jnts_state.set(data=null_action, data_type="eff",
652
+ robot_idxs=self.controller_index_np,
653
+ no_remap=True)
654
+
655
+ # write all joints (including homing for env-only ones)
656
+ self.robot_cmds.jnts_state.synch_retry(row_index=self.controller_index, col_index=0,
657
+ row_index_view=0,
658
+ n_rows=1, n_cols=self.robot_cmds.jnts_state.n_cols,
659
+ read=False) # only write data corresponding to this controller
660
+
661
+ self.reset() # reset controller
662
+ self._n_resets=0
663
+
664
+ # for last we create the trigger client
665
+ self._remote_triggerer = RemoteTriggererClnt(namespace=self.namespace,
666
+ verbose=self._verbose,
667
+ vlevel=VLevel.V2) # remote triggering
668
+ self._remote_triggerer.run()
669
+
670
+ if self._debug:
671
+ # internal solution is published on shared mem
672
+ # we assume the user has made available the cost
673
+ # and constraint data at this point (e.g. through
674
+ # the solution of a bootstrap)
675
+ cost_data = self._get_cost_info()
676
+ constr_data = self._get_constr_info()
677
+ config = RhcInternal.Config(is_server=True,
678
+ enable_q= True,
679
+ enable_v=True,
680
+ enable_a=True,
681
+ enable_a_dot=False,
682
+ enable_f=True,
683
+ enable_f_dot=False,
684
+ enable_eff=False,
685
+ cost_names=cost_data[0],
686
+ cost_dims=cost_data[1],
687
+ constr_names=constr_data[0],
688
+ constr_dims=constr_data[1],
689
+ )
690
+ self.rhc_internal = RhcInternal(config=config,
691
+ namespace=self.namespace,
692
+ rhc_index = self.controller_index,
693
+ n_contacts=self.n_contacts,
694
+ n_jnts=self.n_dofs,
695
+ jnt_names=self._controller_side_jnt_names,
696
+ n_nodes=self._n_nodes,
697
+ verbose = self._verbose,
698
+ vlevel=VLevel.V2,
699
+ force_reconnection=True,
700
+ safe=True)
701
+ self.rhc_internal.run()
702
+
703
+ self._class_name = self._class_name + f"-{self.controller_index}"
704
+
705
+ Journal.log(self._class_name,
706
+ "_register_to_cluster",
707
+ f"controller registered",
708
+ LogType.STAT,
709
+ throw_when_excep = True)
710
+
711
+ # we can now release everything so that other controllers can register
712
+ self.rhc_status.controllers_counter.data_sem_release()
713
+ self.rhc_status.registration.data_sem_release()
714
+
715
+ self._registered = True
716
+
717
+ def _unregister_from_cluster(self):
718
+
719
+ if self._received_trigger:
720
+ # received interrupt during solution -->
721
+ # send ack signal to server anyway
722
+ self._remote_triggerer.ack()
723
+ if self._registered:
724
+ # acquire semaphores since we have to perform operations
725
+ # on the whole memory views
726
+ self.rhc_status.registration.data_sem_acquire()
727
+ self.rhc_status.controllers_counter.data_sem_acquire()
728
+ self.rhc_status.registration.write_retry(False,
729
+ row_index=self.controller_index,
730
+ col_index=0,
731
+ row_index_view=0)
732
+ self._deactivate()
733
+ # decrementing controllers counter
734
+ self.rhc_status.controllers_counter.synch_all(retry = True,
735
+ read = True)
736
+ controllers_counter = self.rhc_status.controllers_counter.get_numpy_mirror()
737
+ controllers_counter -= 1
738
+ self.rhc_status.controllers_counter.synch_all(retry = True,
739
+ read = False)
740
+ Journal.log(self._class_name,
741
+ "_unregister_from_cluster",
742
+ "Done",
743
+ LogType.STAT,
744
+ throw_when_excep = True)
745
+ # we can now release everything
746
+ self.rhc_status.registration.data_sem_release()
747
+ self.rhc_status.controllers_counter.data_sem_release()
748
+ self._registered = False
749
+
750
+ def _get_quat_remap(self):
751
+ # to be overridden by child class if necessary
752
+ return [0, 1, 2, 3]
753
+
754
+ def _consinstency_checks(self):
755
+
756
+ # check controller dt
757
+ server_side_cluster_dt = self.cluster_stats.get_info(info_name="cluster_dt")
758
+ if not (abs(server_side_cluster_dt - self._dt) < 1e-4):
759
+ exception = f"Trying to initialize a controller with control dt {self._dt}, which" + \
760
+ f"does not match the cluster control dt {server_side_cluster_dt}"
761
+ Journal.log(self._class_name,
762
+ "_consinstency_checks",
763
+ exception,
764
+ LogType.EXCEP,
765
+ throw_when_excep = True)
766
+ # check contact names
767
+
768
+ server_side_contact_names = set(self.robot_state.contact_names())
769
+ control_side_contact_names = set(self._get_contacts())
770
+
771
+ if (not server_side_contact_names == control_side_contact_names) and self._verbose:
772
+ warn = f"Controller-side contact names do not match server-side names!" + \
773
+ f"\nServer: {self.robot_state.contact_names()}\n Controller: {self._get_contacts()}"
774
+ Journal.log(self._class_name,
775
+ "_consinstency_checks",
776
+ warn,
777
+ LogType.WARN,
778
+ throw_when_excep = True)
779
+ if not len(self.robot_state.contact_names()) == len(self._get_contacts()):
780
+ # at least, we need the n of contacts to match!
781
+ exception = f"Controller-side n contacts {self._get_contacts()} do not match " + \
782
+ f"server-side n contacts {len(self.robot_state.contact_names())}!"
783
+ Journal.log(self._class_name,
784
+ "_consinstency_checks",
785
+ exception,
786
+ LogType.EXCEP,
787
+ throw_when_excep = True)
788
+
789
+ def _init(self):
790
+
791
+ stat = f"Trying to initialize RHC controller " + \
792
+ f"with dt: {self._dt} s, t_horizon: {self._t_horizon} s, n_intervals: {self._n_intervals}"
793
+ Journal.log(self._class_name,
794
+ "_init",
795
+ stat,
796
+ LogType.STAT,
797
+ throw_when_excep = True)
798
+
799
+ self._init_states() # initializes shared mem. states
800
+
801
+ self._init_problem() # we call the child's initialization method for the actual problem
802
+ self._post_problem_init()
803
+
804
+ self._register_to_cluster() # registers the controller to the cluster
805
+
806
+ Journal.log(self._class_name,
807
+ "_init",
808
+ f"RHC controller initialized with cluster index {self.controller_index} on process {os.getpid()}",
809
+ LogType.STAT,
810
+ throw_when_excep = True)
811
+
812
+ def _deactivate(self):
813
+ # signal controller deactivation over shared mem
814
+ self.rhc_status.activation_state.write_retry(False,
815
+ row_index=self.controller_index,
816
+ col_index=0,
817
+ row_index_view=0)
818
+ # also set cmds to homing for safety
819
+ # self.reset_rhc_data()
820
+
821
+ def _on_failure(self):
822
+
823
+ self.rhc_status.fails.write_retry(True,
824
+ row_index=self.controller_index,
825
+ col_index=0,
826
+ row_index_view=0)
827
+ self._deactivate()
828
+ self._n_fails += 1
829
+ self.rhc_status.controllers_fail_counter.write_retry(self._n_fails,
830
+ row_index=self.controller_index,
831
+ col_index=0,
832
+ row_index_view=0)
833
+
834
+ def _init_robot_homer(self):
835
+ self._homer = RobotHomer(srdf_path=self.srdf_path,
836
+ jnt_names=self._controller_side_jnt_names,
837
+ verbose=self._verbose)
838
+
839
+ self._homer_env = RobotHomer(srdf_path=self.srdf_path,
840
+ jnt_names=self.robot_state.jnt_names(),
841
+ verbose=self._verbose)
842
+
843
+ def _update_profiling_data(self):
844
+
845
+ # updated debug data on shared memory
846
+ # with the latest info available
847
+ self.cluster_stats.solve_loop_dt.write_retry(self._profiling_data_dict["full_solve_dt"],
848
+ row_index=self.controller_index,
849
+ col_index=0,
850
+ row_index_view=0)
851
+ self.cluster_stats.rti_sol_time.write_retry(self._profiling_data_dict["rti_solve_dt"],
852
+ row_index=self.controller_index,
853
+ col_index=0,
854
+ row_index_view=0)
855
+ self.cluster_stats.prb_update_dt.write_retry(self._profiling_data_dict["problem_update_dt"],
856
+ row_index=self.controller_index,
857
+ col_index=0,
858
+ row_index_view=0)
859
+ self.cluster_stats.phase_shift_dt.write_retry(self._profiling_data_dict["phases_shift_dt"],
860
+ row_index=self.controller_index,
861
+ col_index=0,
862
+ row_index_view=0)
863
+ self.cluster_stats.task_ref_update_dt.write_retry(self._profiling_data_dict["task_ref_update"],
864
+ row_index=self.controller_index,
865
+ col_index=0,
866
+ row_index_view=0)
867
+
868
+ def _write_cmds_from_sol(self):
869
+
870
+ # gets data from the solution and updates the view on the shared data
871
+
872
+ # cmds for robot
873
+ # jnts
874
+ 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)
875
+ 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)
876
+ 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)
877
+ 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)
878
+ # root
879
+ 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)
880
+ 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)
881
+ 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)
882
+ 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)
883
+ f_contact = self._get_f_from_sol()
884
+ if f_contact is not None:
885
+ contact_names = self.robot_state.contact_names()
886
+ node_idx_f_estimate=self._rhc_cmds_node_idx-1 # we always write the force to reach the desired state (prev node)
887
+ rhc_q_estimate=self._get_root_full_q_from_sol(node_idx=node_idx_f_estimate)[:, 3:7]
888
+ for i in range(len(contact_names)):
889
+ contact = contact_names[i]
890
+ contact_idx = i*3
891
+ contact_force_rhc_world=f_contact[contact_idx:(contact_idx+3), node_idx_f_estimate:node_idx_f_estimate+1].T
892
+ world2base_frame(v_w=contact_force_rhc_world,
893
+ q_b=rhc_q_estimate,
894
+ v_out=self._contact_force_base_loc_aux,
895
+ is_q_wijk=False # horizon q is ijkw
896
+ )
897
+ self.robot_cmds.contact_wrenches.set(data=self._contact_force_base_loc_aux,
898
+ data_type="f",
899
+ robot_idxs=self.controller_index_np,
900
+ contact_name=contact)
901
+
902
+ # prediction data from MPC horizon
903
+ 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)
904
+ 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)
905
+ 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)
906
+ 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)
907
+ 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)
908
+ 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)
909
+ 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)
910
+ 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)
911
+
912
+ # write robot commands
913
+ self.robot_cmds.jnts_state.synch_retry(row_index=self.controller_index, col_index=0,
914
+ row_index_view=0,
915
+ n_rows=1, n_cols=self.robot_cmds.jnts_state.n_cols,
916
+ read=False) # jnt state
917
+ self.robot_cmds.root_state.synch_retry(row_index=self.controller_index, col_index=0,
918
+ row_index_view=0,
919
+ n_rows=1, n_cols=self.robot_cmds.root_state.n_cols,
920
+ read=False) # root state, in case it was written
921
+ self.robot_cmds.contact_wrenches.synch_retry(row_index=self.controller_index, col_index=0,
922
+ row_index_view=0,
923
+ n_rows=1, n_cols=self.robot_cmds.contact_wrenches.n_cols,
924
+ read=False) # contact state
925
+
926
+ # write robot pred
927
+ self.robot_pred.jnts_state.synch_retry(row_index=self.controller_index, col_index=0,
928
+ row_index_view=0,
929
+ n_rows=1, n_cols=self.robot_cmds.jnts_state.n_cols,
930
+ read=False)
931
+ self.robot_pred.root_state.synch_retry(row_index=self.controller_index, col_index=0,
932
+ row_index_view=0,
933
+ n_rows=1, n_cols=self.robot_cmds.root_state.n_cols,
934
+ read=False)
935
+
936
+ # we also fill other data (cost, constr. violation, etc..)
937
+ self.rhc_status.rhc_cost.write_retry(self._get_rhc_cost(),
938
+ row_index=self.controller_index,
939
+ col_index=0,
940
+ row_index_view=0)
941
+ self.rhc_status.rhc_constr_viol.write_retry(self._get_rhc_constr_viol(),
942
+ row_index=self.controller_index,
943
+ col_index=0,
944
+ row_index_view=0)
945
+ self.rhc_status.rhc_n_iter.write_retry(self._get_rhc_niter_to_sol(),
946
+ row_index=self.controller_index,
947
+ col_index=0,
948
+ row_index_view=0)
949
+ self.rhc_status.rhc_nodes_cost.write_retry(data=self._get_rhc_nodes_cost(),
950
+ row_index=self.controller_index,
951
+ col_index=0,
952
+ row_index_view=0)
953
+ self.rhc_status.rhc_nodes_constr_viol.write_retry(data=self._get_rhc_nodes_constr_viol(),
954
+ row_index=self.controller_index,
955
+ col_index=0,
956
+ row_index_view=0)
957
+ self.rhc_status.rhc_fail_idx.write_retry(self._get_failure_index(),
958
+ row_index=self.controller_index,
959
+ col_index=0,
960
+ row_index_view=0) # write idx on shared mem
961
+
962
+ def _compute_pred_delta(self):
963
+
964
+ # measurements
965
+ q_full_root_meas = self.robot_state.root_state.get(data_type="q_full", robot_idxs=self.controller_index_np)
966
+ twist_root_meas = self.robot_state.root_state.get(data_type="twist", robot_idxs=self.controller_index_np)
967
+ a_root_meas = self.robot_state.root_state.get(data_type="a_full", robot_idxs=self.controller_index_np)
968
+ g_vec_root_meas = self.robot_state.root_state.get(data_type="gn", robot_idxs=self.controller_index_np)
969
+
970
+ q_jnts_meas = self.robot_state.jnts_state.get(data_type="q", robot_idxs=self.controller_index_np)
971
+ v_jnts_meas = self.robot_state.jnts_state.get(data_type="v", robot_idxs=self.controller_index_np)
972
+ a_jnts_meas = self.robot_state.jnts_state.get(data_type="a", robot_idxs=self.controller_index_np)
973
+ eff_jnts_meas = self.robot_state.jnts_state.get(data_type="eff", robot_idxs=self.controller_index_np)
974
+
975
+ # prediction from rhc
976
+ delta_root_q_full=self._get_root_full_q_from_sol(node_idx=1)-q_full_root_meas
977
+ delta_root_twist=self._get_root_twist_from_sol(node_idx=1)-twist_root_meas
978
+ delta_root_a=self._get_root_a_from_sol(node_idx=0)-a_root_meas
979
+ delta_g_vec=self._get_norm_grav_vector_from_sol(node_idx=0)-g_vec_root_meas
980
+
981
+ delta_jnts_q=self._get_jnt_q_from_sol(node_idx=1)-q_jnts_meas
982
+ delta_jnts_v=self._get_jnt_v_from_sol(node_idx=1)-v_jnts_meas
983
+ delta_jnts_a=self._get_jnt_a_from_sol(node_idx=0)-a_jnts_meas
984
+ delta_jnts_eff=self._get_jnt_eff_from_sol(node_idx=0)-eff_jnts_meas
985
+
986
+ # writing pred. errors
987
+ self.rhc_pred_delta.root_state.set(data=delta_root_q_full, data_type="q_full", robot_idxs=self.controller_index_np)
988
+ self.rhc_pred_delta.root_state.set(data=delta_root_twist,data_type="twist", robot_idxs=self.controller_index_np)
989
+ self.rhc_pred_delta.root_state.set(data=delta_root_a,data_type="a_full", robot_idxs=self.controller_index_np)
990
+ self.rhc_pred_delta.root_state.set(data=delta_g_vec, data_type="gn", robot_idxs=self.controller_index_np)
991
+
992
+ self.rhc_pred_delta.jnts_state.set(data=delta_jnts_q,data_type="q", robot_idxs=self.controller_index_np)
993
+ self.rhc_pred_delta.jnts_state.set(data=delta_jnts_v,data_type="v", robot_idxs=self.controller_index_np)
994
+ self.rhc_pred_delta.jnts_state.set(data=delta_jnts_a,data_type="a", robot_idxs=self.controller_index_np)
995
+ self.rhc_pred_delta.jnts_state.set(data=delta_jnts_eff, data_type="eff", robot_idxs=self.controller_index_np)
996
+
997
+ # write on shared memory
998
+ self.rhc_pred_delta.jnts_state.synch_retry(row_index=self.controller_index,
999
+ col_index=0,
1000
+ n_rows=1,
1001
+ row_index_view=0,
1002
+ n_cols=self.robot_cmds.jnts_state.n_cols,
1003
+ read=False) # jnt state
1004
+ self.rhc_pred_delta.root_state.synch_retry(row_index=self.controller_index,
1005
+ col_index=0,
1006
+ n_rows=1,
1007
+ row_index_view=0,
1008
+ n_cols=self.robot_cmds.root_state.n_cols,
1009
+ read=False) # root state
1010
+
1011
+ def _assign_controller_side_jnt_names(self,
1012
+ jnt_names: List[str]):
1013
+
1014
+ self._controller_side_jnt_names = jnt_names
1015
+ self._got_jnt_names_from_controllers = True
1016
+
1017
+ def _check_jnt_names_compatibility(self):
1018
+
1019
+ set_rhc = set(self._controller_side_jnt_names)
1020
+ set_env = set(self._env_side_jnt_names)
1021
+
1022
+ if not set_rhc == set_env:
1023
+ rhc_is_missing=set_env-set_rhc
1024
+ env_is_missing=set_rhc-set_env
1025
+
1026
+ msg_type=LogType.WARN
1027
+ message=""
1028
+ if not len(rhc_is_missing)==0: # allowed
1029
+ message = "\nSome env-side joint names are missing on rhc client-side!\n" + \
1030
+ "RHC-SERVER-SIDE-> \n" + \
1031
+ " ".join(self._env_side_jnt_names) + "\n" +\
1032
+ "RHC-CLIENT-SIDE -> \n" + \
1033
+ " ".join(self._controller_side_jnt_names) + "\n" \
1034
+ "\MISSING -> \n" + \
1035
+ " ".join(list(rhc_is_missing)) + "\n"
1036
+ if not self._allow_less_jnts: # raise exception
1037
+ msg_type=LogType.EXCEP
1038
+
1039
+ if not len(env_is_missing)==0: # not allowed
1040
+ message = "\nSome rhc-side joint names are missing on rhc server-side!\n" + \
1041
+ "RHC-SERVER-SIDE-> \n" + \
1042
+ " ".join(self._env_side_jnt_names) + \
1043
+ "RHC-CLIENT-SIDE -> \n" + \
1044
+ " ".join(self._controller_side_jnt_names) + "\n" \
1045
+ "\nmissing -> \n" + \
1046
+ " ".join(list(env_is_missing))
1047
+ msg_type=LogType.EXCEP
1048
+
1049
+ if msg_type==LogType.WARN and not self._verbose:
1050
+ return
1051
+
1052
+ Journal.log(self._class_name,
1053
+ "_check_jnt_names_compatibility",
1054
+ message,
1055
+ msg_type,
1056
+ throw_when_excep = True)
1057
+
1058
+ def _get_cost_info(self):
1059
+ # to be overridden by child class
1060
+ return None, None
1061
+
1062
+ def _get_constr_info(self):
1063
+ # to be overridden by child class
1064
+ return None, None
1065
+
1066
+ def _get_fail_idx(self):
1067
+ # to be overriden by parent
1068
+ return 0.0
1069
+
1070
+ def _get_failure_index(self):
1071
+ fail_idx=self._get_fail_idx()/self._fail_idx_thresh
1072
+ if fail_idx>1.0:
1073
+ fail_idx=1.0
1074
+ return fail_idx
1075
+
1076
+ def _check_rhc_failure(self):
1077
+ # we use fail idx viol to detect failures
1078
+ idx = self._get_failure_index()
1079
+ return idx>=1.0
1080
+
1081
+ def _update_rhc_internal(self):
1082
+ # data which is not enabled in the config is not actually
1083
+ # written so overhead is minimal for non-enabled data
1084
+ self.rhc_internal.write_q(data= self._get_q_from_sol(),
1085
+ retry=True)
1086
+ self.rhc_internal.write_v(data= self._get_v_from_sol(),
1087
+ retry=True)
1088
+ self.rhc_internal.write_a(data= self._get_a_from_sol(),
1089
+ retry=True)
1090
+ self.rhc_internal.write_a_dot(data= self._get_a_dot_from_sol(),
1091
+ retry=True)
1092
+ self.rhc_internal.write_f(data= self._get_f_from_sol(),
1093
+ retry=True)
1094
+ self.rhc_internal.write_f_dot(data= self._get_f_dot_from_sol(),
1095
+ retry=True)
1096
+ self.rhc_internal.write_eff(data= self._get_eff_from_sol(),
1097
+ retry=True)
1098
+ for cost_idx in range(self.rhc_internal.config.n_costs):
1099
+ # iterate over all costs and update all values
1100
+ cost_name = self.rhc_internal.config.cost_names[cost_idx]
1101
+ self.rhc_internal.write_cost(data= self._get_cost_from_sol(cost_name = cost_name),
1102
+ cost_name = cost_name,
1103
+ retry=True)
1104
+ for constr_idx in range(self.rhc_internal.config.n_constr):
1105
+ # iterate over all constraints and update all values
1106
+ constr_name = self.rhc_internal.config.constr_names[constr_idx]
1107
+ self.rhc_internal.write_constr(data= self._get_constr_from_sol(constr_name=constr_name),
1108
+ constr_name = constr_name,
1109
+ retry=True)
1110
+
1111
+ def _get_contacts(self):
1112
+ contact_names = self._get_contact_names()
1113
+ self._got_contact_names = True
1114
+ return contact_names
1115
+
1116
+ def _get_q_from_sol(self):
1117
+ # to be overridden by child class
1118
+ return None
1119
+
1120
+ def _get_v_from_sol(self):
1121
+ # to be overridden by child class
1122
+ return None
1123
+
1124
+ def _get_a_from_sol(self):
1125
+ # to be overridden by child class
1126
+ return None
1127
+
1128
+ def _get_a_dot_from_sol(self):
1129
+ # to be overridden by child class
1130
+ return None
1131
+
1132
+ def _get_f_from_sol(self):
1133
+ # to be overridden by child class
1134
+ return None
1135
+
1136
+ def _get_f_dot_from_sol(self):
1137
+ # to be overridden by child class
1138
+ return None
1139
+
1140
+ def _get_eff_from_sol(self):
1141
+ # to be overridden by child class
1142
+ return None
1143
+
1144
+ def _get_cost_from_sol(self,
1145
+ cost_name: str):
1146
+ # to be overridden by child class
1147
+ return None
1148
+
1149
+ def _get_constr_from_sol(self,
1150
+ constr_name: str):
1151
+ # to be overridden by child class
1152
+ return None
1153
+
1154
+ @abstractmethod
1155
+ def _reset(self):
1156
+ pass
1157
+
1158
+ @abstractmethod
1159
+ def _init_rhc_task_cmds(self):
1160
+ pass
1161
+
1162
+ @abstractmethod
1163
+ def _get_robot_jnt_names(self):
1164
+ pass
1165
+
1166
+ @abstractmethod
1167
+ def _get_contact_names(self):
1168
+ pass
1169
+
1170
+ @abstractmethod
1171
+ def _get_jnt_q_from_sol(self, node_idx=1) -> np.ndarray:
1172
+ pass
1173
+
1174
+ @abstractmethod
1175
+ def _get_jnt_v_from_sol(self, node_idx=1) -> np.ndarray:
1176
+ pass
1177
+
1178
+ @abstractmethod
1179
+ def _get_jnt_a_from_sol(self, node_idx=0) -> np.ndarray:
1180
+ pass
1181
+
1182
+ @abstractmethod
1183
+ def _get_jnt_eff_from_sol(self, node_idx=0) -> np.ndarray:
1184
+ pass
1185
+
1186
+ @abstractmethod
1187
+ def _get_root_full_q_from_sol(self, node_idx=1) -> np.ndarray:
1188
+ pass
1189
+
1190
+ @abstractmethod
1191
+ def _get_full_q_from_sol(self, node_idx=1) -> np.ndarray:
1192
+ pass
1193
+
1194
+ @abstractmethod
1195
+ def _get_root_twist_from_sol(self, node_idx=1) -> np.ndarray:
1196
+ pass
1197
+
1198
+ @abstractmethod
1199
+ def _get_root_a_from_sol(self, node_idx=0) -> np.ndarray:
1200
+ pass
1201
+
1202
+ def _get_norm_grav_vector_from_sol(self, node_idx=1) -> np.ndarray:
1203
+ rhc_q=self._get_root_full_q_from_sol(node_idx=node_idx)[:, 3:7]
1204
+ world2base_frame(v_w=self._norm_grav_vector_w,q_b=rhc_q,v_out=self._norm_grav_vector_base_loc,
1205
+ is_q_wijk=False)
1206
+ return self._norm_grav_vector_base_loc
1207
+
1208
+ def _get_rhc_cost(self):
1209
+ # to be overridden
1210
+ return np.nan
1211
+
1212
+ def _get_rhc_constr_viol(self):
1213
+ # to be overridden
1214
+ return np.nan
1215
+
1216
+ def _get_rhc_nodes_cost(self):
1217
+ # to be overridden
1218
+ return np.zeros((1,self.rhc_status.n_nodes), dtype=self._dtype)
1219
+
1220
+ def _get_rhc_nodes_constr_viol(self):
1221
+ # to be overridden
1222
+ return np.zeros((1,self.rhc_status.n_nodes), dtype=self._dtype)
1223
+
1224
+ def _get_rhc_niter_to_sol(self) -> np.ndarray:
1225
+ # to be overridden
1226
+ return np.nan
1227
+
1228
+ @abstractmethod
1229
+ def _update_open_loop(self):
1230
+ # updates rhc controller
1231
+ # using the internal state
1232
+ pass
1233
+
1234
+ @abstractmethod
1235
+ def _update_closed_loop(self):
1236
+ # uses meas. from robot
1237
+ pass
1238
+
1239
+ @abstractmethod
1240
+ def _solve(self) -> bool:
1241
+ pass
1242
+
1243
+ @abstractmethod
1244
+ def _bootstrap(self) -> bool:
1245
+ pass
1246
+
1247
+ @abstractmethod
1248
+ def _get_ndofs(self):
1249
+ pass
1250
+
1251
+ abstractmethod
1252
+ def _get_robot_mass(self):
1253
+ pass
1254
+
1255
+ @abstractmethod
1256
+ def _init_problem(self):
1257
+ # initialized horizon's TO problem
1258
+ pass
1259
+
1260
+ @abstractmethod
1261
+ def _post_problem_init(self):
1262
+ pass
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")
sactor_critic_algo.py ADDED
The diff for this file is too large to render. See raw diff
 
training_env_base.py ADDED
@@ -0,0 +1,2002 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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(reset_on_init=self._env_opts["reset_on_init"])
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
+ self._check_for_env_opts("reset_on_init", bool)
222
+
223
+ # parse action repeat opt + get some sim information
224
+ if self._env_opts["action_repeat"] <=0:
225
+ self._env_opts["action_repeat"] = 1
226
+ self._action_repeat=self._env_opts["action_repeat"]
227
+ # parse remote sim info
228
+ sim_info = {}
229
+ sim_info_shared = SharedEnvInfo(namespace=self._namespace,
230
+ is_server=False,
231
+ safe=False,
232
+ verbose=self._verbose,
233
+ vlevel=self._vlevel)
234
+ sim_info_shared.run()
235
+ sim_info_keys = sim_info_shared.param_keys
236
+ sim_info_data = sim_info_shared.get().flatten()
237
+ for i in range(len(sim_info_keys)):
238
+ sim_info[sim_info_keys[i]] = sim_info_data[i]
239
+ if "substepping_dt" in sim_info_keys:
240
+ self._substep_dt=sim_info["substepping_dt"]
241
+ self._env_opts.update(sim_info)
242
+
243
+ self._env_opts["substep_dt"]=self._substep_dt
244
+
245
+ self._env_opts["override_agent_refs"]=self._override_agent_refs
246
+
247
+ self._env_opts["episode_timeout_lb"] = round(self._env_opts["episode_timeout_lb"]/self._action_repeat)
248
+ self._env_opts["episode_timeout_ub"] = round(self._env_opts["episode_timeout_ub"]/self._action_repeat)
249
+
250
+ self._env_opts["n_steps_task_rand_lb"] = round(self._env_opts["n_steps_task_rand_lb"]/self._action_repeat)
251
+ self._env_opts["n_steps_task_rand_ub"] = round(self._env_opts["n_steps_task_rand_ub"]/self._action_repeat)
252
+
253
+ if self._env_opts["random_reset_freq"] <=0:
254
+ self._env_opts["use_random_safety_reset"]=False
255
+ self._env_opts["random_reset_freq"]=-1
256
+ self._random_reset_active=self._env_opts["use_random_safety_reset"]
257
+
258
+ self._env_opts["random_trunc_freq"] = round(self._env_opts["random_trunc_freq"]/self._action_repeat)
259
+ self._env_opts["random_trunc_freq_delta"] = round(self._env_opts["random_trunc_freq_delta"]/self._action_repeat)
260
+
261
+ if self._env_opts["random_trunc_freq"] <=0:
262
+ self._env_opts["use_random_trunc"]=False
263
+ self._env_opts["random_trunc_freq"]=-1
264
+
265
+ self._full_db=False
266
+ if "full_env_db" in self._env_opts:
267
+ self._full_db=self._env_opts["full_env_db"]
268
+
269
+ def _check_for_env_opts(self,
270
+ name: str,
271
+ expected_type):
272
+ if not (name in self._env_opts):
273
+ Journal.log(self.__class__.__name__,
274
+ "_check_for_env_opts",
275
+ f"Required option {name} missing for env opts!",
276
+ LogType.EXCEP,
277
+ throw_when_excep=True)
278
+ if not isinstance(self._env_opts[name], expected_type):
279
+ Journal.log(self.__class__.__name__,
280
+ "_check_for_env_opts",
281
+ f"Option {name} in env opts is not of type {expected_type} (got {type(self._env_opts[name])})!",
282
+ LogType.EXCEP,
283
+ throw_when_excep=True)
284
+
285
+ def __del__(self):
286
+
287
+ self.close()
288
+
289
+ def _demo_setup(self):
290
+
291
+ self._demo_envs_idxs=None
292
+ self._demo_envs_idxs_bool=None
293
+ self._n_demo_envs=round(self._env_opts["demo_envs_perc"]*self._n_envs)
294
+ self._add_demos=False
295
+ if not self._n_demo_envs >0:
296
+ Journal.log(self.__class__.__name__,
297
+ "__init__",
298
+ "will not use demo environments",
299
+ LogType.INFO,
300
+ throw_when_excep=False)
301
+ else:
302
+ Journal.log(self.__class__.__name__,
303
+ "__init__",
304
+ f"Will run with {self._n_demo_envs} demonstration envs.",
305
+ LogType.INFO)
306
+ self._demo_envs_idxs = torch.randperm(self._n_envs, device=self._device)[:self._n_demo_envs]
307
+ self._demo_envs_idxs_bool=torch.full((self._n_envs, ), dtype=torch.bool, device=self._device,
308
+ fill_value=False)
309
+ self._demo_envs_idxs_bool[self._demo_envs_idxs]=True
310
+
311
+ self._init_demo_envs() # custom logic
312
+
313
+ demo_idxs_str=", ".join(map(str, self._demo_envs_idxs.tolist()))
314
+ Journal.log(self.__class__.__name__,
315
+ "__init__",
316
+ f"Demo env. indexes are [{demo_idxs_str}]",
317
+ LogType.INFO)
318
+
319
+ def env_opts(self):
320
+ return self._env_opts
321
+
322
+ def demo_env_idxs(self, get_bool: bool=False):
323
+ if get_bool:
324
+ return self._demo_envs_idxs_bool
325
+ else:
326
+ return self._demo_envs_idxs
327
+
328
+ def _init_demo_envs(self):
329
+ pass
330
+
331
+ def n_demo_envs(self):
332
+ return self._n_demo_envs
333
+
334
+ def demo_active(self):
335
+ return self._add_demos
336
+
337
+ def switch_demo(self, active: bool = False):
338
+ if self._demo_envs_idxs is not None:
339
+ self._add_demos=active
340
+ else:
341
+ Journal.log(self.__class__.__name__,
342
+ "switch_demo",
343
+ f"Cannot switch demostrations on. No demo envs available!",
344
+ LogType.EXCEP,
345
+ throw_when_excep=True)
346
+
347
+ def _get_this_file_path(self):
348
+ return self._this_path
349
+
350
+ def episode_timeout_bounds(self):
351
+ return self._env_opts["episode_timeout_lb"], self._env_opts["episode_timeout_ub"]
352
+
353
+ def task_rand_timeout_bounds(self):
354
+ return self._env_opts["n_steps_task_rand_lb"], self._env_opts["n_steps_task_rand_ub"]
355
+
356
+ def n_action_reps(self):
357
+ return self._action_repeat
358
+
359
+ def get_file_paths(self):
360
+ from aug_mpc.utils.sys_utils import PathsGetter
361
+ path_getter = PathsGetter()
362
+ base_paths = []
363
+ base_paths.append(self._get_this_file_path())
364
+ base_paths.append(path_getter.REMOTENVPATH)
365
+ for script_path in path_getter.SCRIPTSPATHS:
366
+ base_paths.append(script_path)
367
+
368
+ # rhc files
369
+ from EigenIPC.PyEigenIPC import StringTensorClient
370
+ from perf_sleep.pyperfsleep import PerfSleep
371
+ shared_rhc_shared_files = StringTensorClient(
372
+ basename="SharedRhcFilesDropDir",
373
+ name_space=self._namespace,
374
+ verbose=self._verbose,
375
+ vlevel=VLevel.V2)
376
+ shared_rhc_shared_files.run()
377
+ shared_rhc_files_vals=[""]*shared_rhc_shared_files.length()
378
+ while not shared_rhc_shared_files.read_vec(shared_rhc_files_vals, 0):
379
+ nsecs = 1000000000 # 1 sec
380
+ PerfSleep.thread_sleep(nsecs) # we just keep it alive
381
+ rhc_list=[]
382
+ for rhc_files in shared_rhc_files_vals:
383
+ file_list = rhc_files.split(", ")
384
+ rhc_list.extend(file_list)
385
+ rhc_list = list(set(rhc_list)) # removing duplicates
386
+ base_paths.extend(rhc_list)
387
+
388
+ # world interface files
389
+ get_world_interface_paths = self.get_world_interface_paths()
390
+ base_paths.extend(get_world_interface_paths)
391
+ return base_paths
392
+
393
+ def get_world_interface_paths(self):
394
+ paths = []
395
+ shared_world_iface_files = StringTensorClient(
396
+ basename="SharedWorldInterfaceFilesDropDir",
397
+ name_space=self._namespace,
398
+ verbose=self._verbose,
399
+ vlevel=VLevel.V2)
400
+ shared_world_iface_files.run()
401
+ world_iface_vals=[""]*shared_world_iface_files.length()
402
+ while not shared_world_iface_files.read_vec(world_iface_vals, 0):
403
+ nsecs = 1000000000 # 1 sec
404
+ PerfSleep.thread_sleep(nsecs) # keep alive while waiting
405
+ shared_world_iface_files.close()
406
+ for files in world_iface_vals:
407
+ if files == "":
408
+ continue
409
+ file_list = files.split(", ")
410
+ for f in file_list:
411
+ if f not in paths:
412
+ paths.append(f)
413
+ return paths
414
+
415
+ def get_aux_dir(self):
416
+ empty_list = []
417
+ return empty_list
418
+
419
+ def _init_step(self, reset_on_init: bool = True):
420
+
421
+ self._check_controllers_registered(retry=True)
422
+ self._activate_rhc_controllers()
423
+
424
+ # just an auxiliary tensor
425
+ initial_reset_aux = self._terminations.get_torch_mirror(gpu=self._use_gpu).clone()
426
+ initial_reset_aux[:, :] = reset_on_init # we reset all sim envs first
427
+ init_step_ok=True
428
+ init_step_ok=self._remote_sim_step() and init_step_ok
429
+ if not init_step_ok:
430
+ return False
431
+ init_step_ok=self._remote_reset(reset_mask=initial_reset_aux) and init_step_ok
432
+ if not init_step_ok:
433
+ return False
434
+
435
+ for i in range(self._env_opts["n_preinit_steps"]): # perform some
436
+ # dummy remote env stepping to make sure to have meaningful
437
+ # initializations (doesn't increment step counter)
438
+ init_step_ok=self._remote_sim_step() and init_step_ok # 1 remote sim. step
439
+ if not init_step_ok:
440
+ return False
441
+ init_step_ok=self._send_remote_reset_req() and init_step_ok # fake reset request
442
+ if not init_step_ok:
443
+ return False
444
+
445
+ self.reset()
446
+
447
+ return init_step_ok
448
+
449
+ def _debug(self):
450
+
451
+ if self._use_gpu:
452
+ # using non_blocking which is not safe when GPU->CPU
453
+ self._obs.synch_mirror(from_gpu=True,non_blocking=True) # copy data from gpu to cpu view
454
+ self._next_obs.synch_mirror(from_gpu=True,non_blocking=True)
455
+ self._actions.synch_mirror(from_gpu=True,non_blocking=True)
456
+ self._truncations.synch_mirror(from_gpu=True,non_blocking=True)
457
+ self._sub_truncations.synch_mirror(from_gpu=True,non_blocking=True)
458
+ self._terminations.synch_mirror(from_gpu=True,non_blocking=True)
459
+ self._sub_terminations.synch_mirror(from_gpu=True,non_blocking=True)
460
+ self._tot_rewards.synch_mirror(from_gpu=True,non_blocking=True)
461
+ self._sub_rewards.synch_mirror(from_gpu=True,non_blocking=True)
462
+ # if we want reliable db data then we should synchronize data streams
463
+ torch.cuda.synchronize()
464
+
465
+ # copy CPU view on shared memory
466
+ self._obs.synch_all(read=False, retry=True)
467
+ self._next_obs.synch_all(read=False, retry=True)
468
+ self._actions.synch_all(read=False, retry=True)
469
+ self._tot_rewards.synch_all(read=False, retry=True)
470
+ self._sub_rewards.synch_all(read=False, retry=True)
471
+ self._truncations.synch_all(read=False, retry = True)
472
+ self._sub_truncations.synch_all(read=False, retry = True)
473
+ self._terminations.synch_all(read=False, retry = True)
474
+ self._sub_terminations.synch_all(read=False, retry = True)
475
+
476
+ self._debug_agent_refs()
477
+
478
+ def _debug_agent_refs(self):
479
+ if self._use_gpu:
480
+ if not self._override_agent_refs:
481
+ self._agent_refs.rob_refs.root_state.synch_mirror(from_gpu=True,non_blocking=False)
482
+ if not self._override_agent_refs:
483
+ self._agent_refs.rob_refs.root_state.synch_all(read=False, retry = True)
484
+
485
+ def _remote_sim_step(self):
486
+
487
+ self._remote_stepper.trigger() # triggers simulation + RHC
488
+ if not self._remote_stepper.wait_ack_from(1, self._timeout):
489
+ Journal.log(self.__class__.__name__,
490
+ "_remote_sim_step",
491
+ "Remote sim. env step ack not received within timeout",
492
+ LogType.EXCEP,
493
+ throw_when_excep = False)
494
+ return False
495
+ return True
496
+
497
+ def _remote_reset(self,
498
+ reset_mask: torch.Tensor = None):
499
+
500
+ reset_reqs = self._remote_reset_req.get_torch_mirror()
501
+ if reset_mask is None: # just send the signal to allow stepping, but do not reset any of
502
+ # the remote envs
503
+ reset_reqs[:, :] = False
504
+ else:
505
+ reset_reqs[:, :] = reset_mask # remotely reset envs corresponding to
506
+ # the mask (True--> to be reset)
507
+ self._remote_reset_req.synch_all(read=False, retry=True) # write on shared buffer
508
+ remote_reset_ok = self._send_remote_reset_req() # process remote request
509
+
510
+ if reset_mask is not None:
511
+ self._synch_state(gpu=self._use_gpu) # if some env was reset, we use _obs
512
+ # to hold the states, including resets, while _next_obs will always hold the
513
+ # state right after stepping the sim env
514
+ # (could be a bit more efficient, since in theory we only need to read the envs
515
+ # corresponding to the reset_mask)
516
+
517
+
518
+ return remote_reset_ok
519
+
520
+ def _send_remote_reset_req(self):
521
+
522
+ self._remote_resetter.trigger()
523
+ if not self._remote_resetter.wait_ack_from(1, self._timeout): # remote reset completed
524
+ Journal.log(self.__class__.__name__,
525
+ "_post_step",
526
+ "Remote reset did not complete within the prescribed timeout!",
527
+ LogType.EXCEP,
528
+ throw_when_excep = False)
529
+ return False
530
+ return True
531
+
532
+ def step(self,
533
+ action):
534
+
535
+ actions_norm = action.detach() # IMPORTANT: assumes actions are already normalized in [-1, 1]
536
+
537
+ actions = self._actions.get_torch_mirror(gpu=self._use_gpu) # will hold agent actions (real range)
538
+
539
+ # scale normalized actions to physical space before interfacing with controllers
540
+ actions[:, :] = actions_norm*self._actions_scale + self._actions_offset
541
+
542
+ self._override_actions_with_demo() # if necessary override some actions with expert demonstrations
543
+ # (getting actions with get_actions will return the modified actions tensor)
544
+
545
+ actions.clamp_(self._actions_lb, self._actions_ub) # just to be safe
546
+
547
+ if self._act_mem_buffer is not None: # store norm actions in memory buffer
548
+ self._act_mem_buffer.update(new_data=actions_norm)
549
+
550
+ if self._env_opts["use_action_smoothing"]:
551
+ self._apply_actions_smoothing() # smooth actions if enabled (the tensor returned by
552
+ # get_actions does not contain smoothing and can be safely employed for experience collection)
553
+
554
+ self._apply_actions_to_rhc() # apply last agent actions to rhc controller
555
+
556
+ stepping_ok = True
557
+ tot_rewards = self._tot_rewards.get_torch_mirror(gpu=self._use_gpu)
558
+ sub_rewards = self._sub_rewards.get_torch_mirror(gpu=self._use_gpu)
559
+ next_obs = self._next_obs.get_torch_mirror(gpu=self._use_gpu)
560
+ tot_rewards.zero_()
561
+ sub_rewards.zero_()
562
+ self._substep_rewards.zero_()
563
+ next_obs.zero_() # necessary for substep obs
564
+
565
+ for i in range(0, self._action_repeat):
566
+
567
+ self._pre_substep() # custom logic @ substep freq
568
+
569
+ stepping_ok = stepping_ok and self._check_controllers_registered(retry=False) # does not make sense to run training
570
+ # if we lost some controllers
571
+ stepping_ok = stepping_ok and self._remote_sim_step() # blocking,
572
+
573
+ # no sim substepping is allowed to fail
574
+ self._synch_state(gpu=self._use_gpu) # read state from shared mem (done in substeps also,
575
+ # since substeps rewards will need updated substep obs)
576
+
577
+ self._custom_post_substp_pre_rew() # custom substepping logic
578
+ self._compute_substep_rewards()
579
+ self._assemble_substep_rewards() # includes rewards clipping
580
+ self._custom_post_substp_post_rew() # custom substepping logic
581
+
582
+ # fill substep obs
583
+ self._fill_substep_obs(self._substep_obs)
584
+ self._assemble_substep_obs()
585
+ if not i==(self._action_repeat-1):
586
+ # sends reset signal to complete remote step sequence,
587
+ # but does not reset any remote env
588
+ stepping_ok = stepping_ok and self._remote_reset(reset_mask=None)
589
+ else: # last substep
590
+
591
+ self._fill_step_obs(next_obs) # update next obs
592
+ self._clamp_obs(next_obs) # good practice
593
+ obs = self._obs.get_torch_mirror(gpu=self._use_gpu)
594
+ obs[:, :] = next_obs # start from next observation, unless reset (handled in post_step())
595
+
596
+ self._compute_step_rewards() # implemented by child
597
+
598
+ tot_rewards = self._tot_rewards.get_torch_mirror(gpu=self._use_gpu)
599
+ sub_rewards = self._sub_rewards.get_torch_mirror(gpu=self._use_gpu)
600
+ self._clamp_rewards(sub_rewards) # clamp all sub rewards
601
+
602
+ tot_rewards[:, :] = torch.sum(sub_rewards, dim=1, keepdim=True)
603
+
604
+ scale=1 # scale tot rew by the number of action repeats
605
+ if self._env_opts["srew_drescaling"]: # scale rewards depending on the n of subrewards
606
+ scale*=sub_rewards.shape[1] # n. dims rescaling
607
+ tot_rewards.mul_(1/scale)
608
+
609
+ self._substep_abs_counter.increment() # @ substep freq
610
+
611
+ if not stepping_ok:
612
+ return False
613
+
614
+ stepping_ok = stepping_ok and self._post_step() # post sub-stepping operations
615
+ # (if action_repeat > 1, then just the db data at the last substep is logged)
616
+ # also, if a reset of an env occurs, obs will hold the reset state
617
+
618
+ return stepping_ok
619
+
620
+ def _post_step(self):
621
+
622
+ # first increment counters
623
+ self._ep_timeout_counter.increment() # episode timeout
624
+ self._task_rand_counter.increment() # task randomization
625
+ if self._rand_trunc_counter is not None: # random truncations (for removing temp. correlations)
626
+ self._rand_trunc_counter.increment()
627
+
628
+ # check truncation and termination conditions
629
+ self._check_truncations() # defined in child env
630
+ self._check_terminations()
631
+ terminated = self._terminations.get_torch_mirror(gpu=self._use_gpu)
632
+ truncated = self._truncations.get_torch_mirror(gpu=self._use_gpu)
633
+ ignore_ep_end=None
634
+ if self._rand_trunc_counter is not None:
635
+ ignore_ep_end=self._rand_trunc_counter.time_limits_reached()
636
+ if self._use_gpu:
637
+ ignore_ep_end=ignore_ep_end.cuda()
638
+
639
+ truncated = torch.logical_or(truncated,
640
+ ignore_ep_end) # add truncation (sub truncations defined in child env
641
+ # remain untouched)
642
+
643
+ episode_finished = torch.logical_or(terminated,
644
+ truncated)
645
+ episode_finished_cpu = episode_finished.cpu()
646
+
647
+ if self._rand_safety_reset_counter is not None and self._random_reset_active:
648
+ self._rand_safety_reset_counter.increment(to_be_incremented=episode_finished_cpu.flatten())
649
+ # truncated[:,:] = torch.logical_or(truncated,
650
+ # self._rand_safety_reset_counter.time_limits_reached().cuda())
651
+
652
+ if self._act_mem_buffer is not None:
653
+ self._act_mem_buffer.reset(to_be_reset=episode_finished.flatten(),
654
+ init_data=self._normalize_actions(self.default_action))
655
+
656
+ if self._action_smoother_continuous is not None:
657
+ self._action_smoother_continuous.reset(to_be_reset=episode_finished.flatten(),
658
+ reset_val=self.default_action[:, self._is_continuous_actions])
659
+ if self._action_smoother_discrete is not None:
660
+ self._action_smoother_discrete.reset(to_be_reset=episode_finished.flatten(),
661
+ reset_val=self.default_action[:, ~self._is_continuous_actions])
662
+
663
+ # debug step if required (IMPORTANT: must be before remote reset so that we always db
664
+ # actual data from the step and not after reset)
665
+ if self._is_debug:
666
+ self._debug() # copies db data on shared memory
667
+ ignore_ep_end_cpu=ignore_ep_end if not self._use_gpu else ignore_ep_end.cpu()
668
+ self._update_custom_db_data(episode_finished=episode_finished_cpu,
669
+ ignore_ep_end=ignore_ep_end_cpu # ignore data if random trunc
670
+ )
671
+ self._episodic_rewards_metrics.update(rewards = self._sub_rewards.get_torch_mirror(gpu=False),
672
+ ep_finished=episode_finished_cpu,
673
+ ignore_ep_end=ignore_ep_end_cpu # ignore data if random trunc
674
+ )
675
+
676
+ # remotely reset envs
677
+ to_be_reset=self._to_be_reset()
678
+ to_be_reset_custom=self._custom_reset()
679
+ if to_be_reset_custom is not None:
680
+ to_be_reset[:, :] = torch.logical_or(to_be_reset,to_be_reset_custom)
681
+ rm_reset_ok = self._remote_reset(reset_mask=to_be_reset)
682
+
683
+ self._custom_post_step(episode_finished=episode_finished) # any additional logic from child env
684
+ # here, before actual reset taskes place (at this point the state is the reset one)
685
+
686
+ # updating also prev pos and orientation in case some env was reset
687
+ self._prev_root_p_substep[:, :]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)
688
+ self._prev_root_q_substep[:, :]=self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
689
+
690
+ obs = self._obs.get_torch_mirror(gpu=self._use_gpu)
691
+ self._fill_step_obs(obs)
692
+ self._clamp_obs(obs)
693
+
694
+ # updating prev step quantities
695
+ self._prev_root_p_step[:, :]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)
696
+ self._prev_root_q_step[:, :]=self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
697
+
698
+ # synchronize and reset counters for finished episodes
699
+ self._ep_timeout_counter.reset(to_be_reset=episode_finished)
700
+ self._task_rand_counter.reset(to_be_reset=episode_finished)
701
+ self._substep_abs_counter.reset(to_be_reset=torch.logical_or(terminated,to_be_reset),
702
+ randomize_offsets=True # otherwise timers across envs would be strongly correlated
703
+ ) # reset only if resetting environment or if terminal
704
+
705
+ if self._rand_trunc_counter is not None:
706
+ # only reset when safety truncation was is triggered
707
+ self._rand_trunc_counter.reset(to_be_reset=self._rand_trunc_counter.time_limits_reached(),
708
+ randomize_limits=True, # we need to randomize otherwise the other counters will synchronize
709
+ # with the episode counters
710
+ randomize_offsets=False # always restart at 0
711
+ )
712
+ # safety reset counter is only when it reches its reset interval (just to keep
713
+ # the counter bounded)
714
+ if self._rand_safety_reset_counter is not None and self._random_reset_active:
715
+ self._rand_safety_reset_counter.reset(to_be_reset=self._rand_safety_reset_counter.time_limits_reached())
716
+
717
+ return rm_reset_ok
718
+
719
+ def _to_be_reset(self):
720
+ # always reset if a termination occurred or if there's a random safety reset
721
+ # request
722
+ terminated = self._terminations.get_torch_mirror(gpu=self._use_gpu)
723
+ to_be_reset=terminated.clone()
724
+ if (self._rand_safety_reset_counter is not None) and self._random_reset_active:
725
+ to_be_reset=torch.logical_or(to_be_reset,
726
+ self._rand_safety_reset_counter.time_limits_reached())
727
+
728
+ return to_be_reset
729
+
730
+ def _custom_reset(self):
731
+ # can be overridden by child
732
+ return None
733
+
734
+ def _apply_actions_smoothing(self):
735
+
736
+ actions = self._actions.get_torch_mirror(gpu=self._use_gpu)
737
+ actual_actions=self.get_actual_actions() # will write smoothed actions here
738
+ if self._action_smoother_continuous is not None:
739
+ self._action_smoother_continuous.update(new_signal=
740
+ actions[:, self._is_continuous_actions])
741
+ actual_actions[:, self._is_continuous_actions]=self._action_smoother_continuous.get()
742
+ if self._action_smoother_discrete is not None:
743
+ self._action_smoother_discrete.update(new_signal=
744
+ actions[:, ~self._is_continuous_actions])
745
+ actual_actions[:, ~self._is_continuous_actions]=self._action_smoother_discrete.get()
746
+
747
+ def _update_custom_db_data(self,
748
+ episode_finished,
749
+ ignore_ep_end):
750
+
751
+ # update defaults
752
+ self.custom_db_data["RhcRefsFlag"].update(new_data=self._rhc_refs.contact_flags.get_torch_mirror(gpu=False),
753
+ ep_finished=episode_finished,
754
+ ignore_ep_end=ignore_ep_end) # before potentially resetting the flags, get data
755
+ self.custom_db_data["Actions"].update(new_data=self._actions.get_torch_mirror(gpu=False),
756
+ ep_finished=episode_finished,
757
+ ignore_ep_end=ignore_ep_end)
758
+ self.custom_db_data["Obs"].update(new_data=self._obs.get_torch_mirror(gpu=False),
759
+ ep_finished=episode_finished,
760
+ ignore_ep_end=ignore_ep_end)
761
+
762
+ self.custom_db_data["SubTerminations"].update(new_data=self._sub_terminations.get_torch_mirror(gpu=False),
763
+ ep_finished=episode_finished,
764
+ ignore_ep_end=ignore_ep_end)
765
+ self.custom_db_data["SubTruncations"].update(new_data=self._sub_truncations.get_torch_mirror(gpu=False),
766
+ ep_finished=episode_finished,
767
+ ignore_ep_end=ignore_ep_end)
768
+
769
+ self.custom_db_data["Terminations"].update(new_data=self._terminations.get_torch_mirror(gpu=False),
770
+ ep_finished=episode_finished,
771
+ ignore_ep_end=ignore_ep_end)
772
+ self.custom_db_data["Truncations"].update(new_data=self._truncations.get_torch_mirror(gpu=False),
773
+ ep_finished=episode_finished,
774
+ ignore_ep_end=ignore_ep_end)
775
+
776
+
777
+ self._get_custom_db_data(episode_finished=episode_finished, ignore_ep_end=ignore_ep_end)
778
+
779
+ def reset_custom_db_data(self, keep_track: bool = False):
780
+ # to be called periodically to reset custom db data stat. collection
781
+ for custom_db_data in self.custom_db_data.values():
782
+ custom_db_data.reset(keep_track=keep_track)
783
+
784
+ def _assemble_substep_rewards(self):
785
+ # by default assemble substep rewards by averaging
786
+ sub_rewards = self._sub_rewards.get_torch_mirror(gpu=self._use_gpu)
787
+
788
+ # average over substeps depending on scale
789
+ # sub_rewards[:, self._is_substep_rew] = sub_rewards[:, self._is_substep_rew] + \
790
+ # self._substep_rewards[:, self._is_substep_rew]/self._action_repeat
791
+ sub_rewards[:, self._is_substep_rew] += self._substep_rewards[:, self._is_substep_rew]/self._action_repeat
792
+
793
+ def _assemble_substep_obs(self):
794
+ next_obs = self._next_obs.get_torch_mirror(gpu=self._use_gpu)
795
+ next_obs[:, self._is_substep_obs] += self._substep_obs[:, self._is_substep_obs]/self._action_repeat
796
+
797
+ def randomize_task_refs(self,
798
+ env_indxs: torch.Tensor = None):
799
+
800
+ if self._override_agent_refs:
801
+ self._override_refs(env_indxs=env_indxs)
802
+ else:
803
+ self._randomize_task_refs(env_indxs=env_indxs)
804
+
805
+ def reset(self):
806
+
807
+ self.randomize_task_refs(env_indxs=None) # randomize all refs across envs
808
+
809
+ self._obs.reset()
810
+ self._actions.reset()
811
+ self._next_obs.reset()
812
+ self._sub_rewards.reset()
813
+ self._tot_rewards.reset()
814
+ self._terminations.reset()
815
+ self._sub_terminations.reset()
816
+ self._truncations.reset()
817
+ self._sub_truncations.reset()
818
+
819
+ self._ep_timeout_counter.reset(randomize_offsets=True)
820
+ self._task_rand_counter.reset()
821
+ self._task_rand_counter.sync_counters(other_counter=self._ep_timeout_counter)
822
+ if self._rand_safety_reset_counter is not None:
823
+ self._rand_safety_reset_counter.reset()
824
+ self._substep_abs_counter.reset()
825
+
826
+ if self._act_mem_buffer is not None:
827
+ self._act_mem_buffer.reset_all(init_data=self._normalize_actions(self.default_action))
828
+
829
+ if self._action_smoother_continuous is not None:
830
+ self._action_smoother_continuous.reset(reset_val=self.default_action[:, self._is_continuous_actions])
831
+ if self._action_smoother_discrete is not None:
832
+ self._action_smoother_discrete.reset(reset_val=self.default_action[:, ~self._is_continuous_actions])
833
+
834
+ self._synch_state(gpu=self._use_gpu) # read obs from shared mem
835
+
836
+ # just calling custom post step to ensure tak refs are updated
837
+ terminated = self._terminations.get_torch_mirror(gpu=self._use_gpu)
838
+ truncated = self._truncations.get_torch_mirror(gpu=self._use_gpu)
839
+ episode_finished = torch.logical_or(terminated,
840
+ truncated)
841
+ self._custom_post_step(episode_finished=episode_finished)
842
+
843
+ obs = self._obs.get_torch_mirror(gpu=self._use_gpu)
844
+ next_obs = self._next_obs.get_torch_mirror(gpu=self._use_gpu)
845
+ self._fill_step_obs(obs) # initialize observations
846
+ self._clamp_obs(obs) # to avoid bad things
847
+ self._fill_step_obs(next_obs) # and next obs
848
+ self._clamp_obs(next_obs)
849
+
850
+ self.reset_custom_db_data(keep_track=False)
851
+ self._episodic_rewards_metrics.reset(keep_track=False)
852
+
853
+ self._prev_root_p_step[:, :]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)
854
+ self._prev_root_q_step[:, :]=self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
855
+ self._prev_root_p_substep[:, :]=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)
856
+ self._prev_root_q_substep[:, :]=self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
857
+
858
+ def is_ready(self):
859
+ return self._ready
860
+
861
+ def close(self):
862
+
863
+ if not self._closed:
864
+
865
+ # close all shared mem. clients
866
+ self._robot_state.close()
867
+ self._rhc_cmds.close()
868
+ self._rhc_pred.close()
869
+ self._rhc_refs.close()
870
+ self._rhc_status.close()
871
+
872
+ self._remote_stepper.close()
873
+
874
+ self._ep_timeout_counter.close()
875
+ self._task_rand_counter.close()
876
+ if self._rand_safety_reset_counter is not None:
877
+ self._rand_safety_reset_counter.close()
878
+
879
+ # closing env.-specific shared data
880
+ self._obs.close()
881
+ self._next_obs.close()
882
+ self._actions.close()
883
+ if self._actual_actions is not None:
884
+ self._actual_actions.close()
885
+ self._sub_rewards.close()
886
+ self._tot_rewards.close()
887
+
888
+ self._terminations.close()
889
+ self._sub_terminations.close()
890
+ self._truncations.close()
891
+ self._sub_truncations.close()
892
+
893
+ self._closed = True
894
+
895
+ def get_obs(self, clone:bool=False):
896
+ if clone:
897
+ return self._obs.get_torch_mirror(gpu=self._use_gpu).detach().clone()
898
+ else:
899
+ return self._obs.get_torch_mirror(gpu=self._use_gpu).detach()
900
+
901
+ def get_next_obs(self, clone:bool=False):
902
+ if clone:
903
+ return self._next_obs.get_torch_mirror(gpu=self._use_gpu).detach().clone()
904
+ else:
905
+ return self._next_obs.get_torch_mirror(gpu=self._use_gpu).detach()
906
+
907
+ def get_actions(self, clone:bool=False, normalized: bool = False):
908
+ actions = self._actions.get_torch_mirror(gpu=self._use_gpu).detach()
909
+ if normalized:
910
+ normalized_actions = self._normalize_actions(actions)
911
+ return normalized_actions.clone() if clone else normalized_actions
912
+ return actions.clone() if clone else actions
913
+
914
+ def get_actual_actions(self, clone:bool=False, normalized: bool = False):
915
+ if self._env_opts["use_action_smoothing"]:
916
+ actions = self._actual_actions.get_torch_mirror(gpu=self._use_gpu).detach()
917
+ else: # actual action coincides with the one from the agent + possible modif.
918
+ actions = self.get_actions(clone=False, normalized=False)
919
+ if normalized:
920
+ normalized_actions = self._normalize_actions(actions)
921
+ return normalized_actions.clone() if clone else normalized_actions
922
+ return actions.clone() if clone else actions
923
+
924
+ def _normalize_actions(self, actions: torch.Tensor):
925
+ scale = torch.where(self._actions_scale == 0.0,
926
+ torch.ones_like(self._actions_scale),
927
+ self._actions_scale)
928
+ normalized = (actions - self._actions_offset)/scale
929
+ zero_scale_mask = torch.eq(self._actions_scale, 0.0).squeeze(0)
930
+ if torch.any(zero_scale_mask):
931
+ normalized[:, zero_scale_mask] = 0.0
932
+ return normalized
933
+
934
+ def get_rewards(self, clone:bool=False):
935
+ if clone:
936
+ return self._tot_rewards.get_torch_mirror(gpu=self._use_gpu).detach().clone()
937
+ else:
938
+ return self._tot_rewards.get_torch_mirror(gpu=self._use_gpu).detach()
939
+
940
+ def get_terminations(self, clone:bool=False):
941
+ if clone:
942
+ return self._terminations.get_torch_mirror(gpu=self._use_gpu).detach().clone()
943
+ else:
944
+ return self._terminations.get_torch_mirror(gpu=self._use_gpu).detach()
945
+
946
+ def get_truncations(self, clone:bool=False):
947
+ if clone:
948
+ return self._truncations.get_torch_mirror(gpu=self._use_gpu).detach().clone()
949
+ else:
950
+ return self._truncations.get_torch_mirror(gpu=self._use_gpu).detach()
951
+
952
+ def obs_dim(self):
953
+
954
+ return self._obs_dim
955
+
956
+ def actions_dim(self):
957
+
958
+ return self._actions_dim
959
+
960
+ def ep_rewards_metrics(self):
961
+
962
+ return self._episodic_rewards_metrics
963
+
964
+ def using_gpu(self):
965
+
966
+ return self._use_gpu
967
+
968
+ def name(self):
969
+
970
+ return self._env_name
971
+
972
+ def n_envs(self):
973
+
974
+ return self._n_envs
975
+
976
+ def dtype(self):
977
+
978
+ return self._dtype
979
+
980
+ def obs_names(self):
981
+ return self._get_obs_names()
982
+
983
+ def action_names(self):
984
+ return self._get_action_names()
985
+
986
+ def sub_rew_names(self):
987
+ return self._get_rewards_names()
988
+
989
+ def sub_term_names(self):
990
+ return self._get_sub_term_names()
991
+
992
+ def sub_trunc_names(self):
993
+ return self._get_sub_trunc_names()
994
+
995
+ def _get_obs_names(self):
996
+ # to be overridden by child class
997
+ return None
998
+
999
+ def get_robot_jnt_names(self):
1000
+ return self._robot_state.jnt_names()
1001
+
1002
+ def _get_action_names(self):
1003
+ # to be overridden by child class
1004
+ return None
1005
+
1006
+ def _get_rewards_names(self):
1007
+ # to be overridden by child class
1008
+ return None
1009
+
1010
+ def _get_sub_term_names(self):
1011
+ # to be overridden by child class
1012
+ sub_term_names = []
1013
+ sub_term_names.append("rhc_failure")
1014
+ sub_term_names.append("robot_capsize")
1015
+ sub_term_names.append("rhc_capsize")
1016
+
1017
+ return sub_term_names
1018
+
1019
+ def _get_sub_trunc_names(self):
1020
+ # to be overridden by child class
1021
+ sub_trunc_names = []
1022
+ sub_trunc_names.append("ep_timeout")
1023
+
1024
+ return sub_trunc_names
1025
+
1026
+ def _get_custom_db_data(self, episode_finished):
1027
+ # to be overridden by child class
1028
+ pass
1029
+
1030
+ def set_observed_joints(self):
1031
+ # ny default observe all joints available
1032
+ return self._robot_state.jnt_names()
1033
+
1034
+ def _set_jnts_blacklist_pattern(self):
1035
+ self._jnt_q_blacklist_patterns=[]
1036
+
1037
+ def get_observed_joints(self):
1038
+ return self._observed_jnt_names
1039
+
1040
+ def _init_obs(self, obs_dim: int):
1041
+
1042
+ device = "cuda" if self._use_gpu else "cpu"
1043
+
1044
+ obs_threshold_default = 1e3
1045
+ self._obs_threshold_lb = -obs_threshold_default # used for clipping observations
1046
+ self._obs_threshold_ub = obs_threshold_default
1047
+
1048
+ self._obs_ub = torch.full((1, obs_dim), dtype=self._dtype, device=device,
1049
+ fill_value=1.0)
1050
+ self._obs_lb = torch.full((1, obs_dim), dtype=self._dtype, device=device,
1051
+ fill_value=-1.0)
1052
+ self._obs_scale = (self._obs_ub - self._obs_lb)/2.0
1053
+ self._obs_offset = (self._obs_ub + self._obs_lb)/2.0
1054
+
1055
+ if not self._obs_dim==len(self._get_obs_names()):
1056
+ error=f"obs dim {self._obs_dim} does not match obs names length {len(self._get_obs_names())}!!"
1057
+ Journal.log(self.__class__.__name__,
1058
+ "_init_obs",
1059
+ error,
1060
+ LogType.EXCEP,
1061
+ throw_when_excep = True)
1062
+
1063
+ self._obs = Observations(namespace=self._namespace,
1064
+ n_envs=self._n_envs,
1065
+ obs_dim=self._obs_dim,
1066
+ obs_names=self._get_obs_names(),
1067
+ env_names=None,
1068
+ is_server=True,
1069
+ verbose=self._verbose,
1070
+ vlevel=self._vlevel,
1071
+ safe=True,
1072
+ force_reconnection=True,
1073
+ with_gpu_mirror=self._use_gpu,
1074
+ fill_value=0.0)
1075
+
1076
+ self._next_obs = NextObservations(namespace=self._namespace,
1077
+ n_envs=self._n_envs,
1078
+ obs_dim=self._obs_dim,
1079
+ obs_names=self._get_obs_names(),
1080
+ env_names=None,
1081
+ is_server=True,
1082
+ verbose=self._verbose,
1083
+ vlevel=self._vlevel,
1084
+ safe=True,
1085
+ force_reconnection=True,
1086
+ with_gpu_mirror=self._use_gpu,
1087
+ fill_value=0.0)
1088
+
1089
+ self._obs.run()
1090
+ self._next_obs.run()
1091
+
1092
+ self._is_substep_obs = torch.zeros((self.obs_dim(),), dtype=torch.bool, device=device)
1093
+ self._is_substep_obs.fill_(False) # default to all step obs
1094
+
1095
+ # not super memory efficient
1096
+ self._substep_obs=torch.full_like(self._obs.get_torch_mirror(gpu=self._use_gpu), fill_value=0.0)
1097
+
1098
+ def _init_actions(self, actions_dim: int):
1099
+
1100
+ device = "cuda" if self._use_gpu else "cpu"
1101
+ # action scalings to be applied to agent's output
1102
+ self._actions_ub = torch.full((1, actions_dim), dtype=self._dtype, device=device,
1103
+ fill_value=1.0)
1104
+ self._actions_lb = torch.full((1, actions_dim), dtype=self._dtype, device=device,
1105
+ fill_value=-1.0)
1106
+ self._actions_scale = (self._actions_ub - self._actions_lb)/2.0
1107
+ self._actions_offset = (self._actions_ub + self._actions_lb)/2.0
1108
+
1109
+ if not self._actions_dim==len(self._get_action_names()):
1110
+ error=f"action dim {self._actions_dim} does not match action names length {len(self._get_action_names())}!!"
1111
+ Journal.log(self.__class__.__name__,
1112
+ "_init_actions",
1113
+ error,
1114
+ LogType.EXCEP,
1115
+ throw_when_excep = True)
1116
+ self._actions = Actions(namespace=self._namespace,
1117
+ n_envs=self._n_envs,
1118
+ action_dim=self._actions_dim,
1119
+ action_names=self._get_action_names(),
1120
+ env_names=None,
1121
+ is_server=True,
1122
+ verbose=self._verbose,
1123
+ vlevel=self._vlevel,
1124
+ safe=True,
1125
+ force_reconnection=True,
1126
+ with_gpu_mirror=self._use_gpu,
1127
+ fill_value=0.0)
1128
+
1129
+ self._actions.run()
1130
+
1131
+ self.default_action = torch.full_like(input=self.get_actions(),fill_value=0.0)
1132
+ self.safe_action = torch.full_like(input=self.get_actions(),fill_value=0.0)
1133
+
1134
+ if self._env_opts["use_action_history"]:
1135
+ self._act_mem_buffer=MemBuffer(name="ActionMemBuf",
1136
+ data_tensor=self._actions.get_torch_mirror(),
1137
+ data_names=self._get_action_names(),
1138
+ debug=self._debug,
1139
+ horizon=self._env_opts["actions_history_size"],
1140
+ dtype=self._dtype,
1141
+ use_gpu=self._use_gpu)
1142
+
1143
+ # default to all continuous actions (changes the way noise is added)
1144
+ self._is_continuous_actions=torch.full((actions_dim, ),
1145
+ dtype=torch.bool, device=device,
1146
+ fill_value=True)
1147
+
1148
+ def _init_action_smoothing(self):
1149
+
1150
+ continuous_actions=self.get_actions()[:, self._is_continuous_actions]
1151
+ discrete_actions=self.get_actions()[:, ~self._is_continuous_actions]
1152
+ self._action_smoother_continuous=ExponentialSignalSmoother(signal=continuous_actions,
1153
+ update_dt=self._substep_dt*self._action_repeat, # rate at which actions are decided by agent
1154
+ smoothing_horizon=self._env_opts["smoothing_horizon_c"],
1155
+ target_smoothing=0.5,
1156
+ debug=self._debug,
1157
+ dtype=self._dtype,
1158
+ use_gpu=self._use_gpu,
1159
+ name="ActionSmootherContinuous")
1160
+ self._action_smoother_discrete=ExponentialSignalSmoother(signal=discrete_actions,
1161
+ update_dt=self._substep_dt*self._action_repeat, # rate at which actions are decided by agent
1162
+ smoothing_horizon=self._env_opts["smoothing_horizon_d"],
1163
+ target_smoothing=0.5,
1164
+ debug=self._debug,
1165
+ dtype=self._dtype,
1166
+ use_gpu=self._use_gpu,
1167
+ name="ActionSmootherDiscrete")
1168
+
1169
+ # we also need somewhere to keep the actual actions after smoothing
1170
+ self._actual_actions = Actions(namespace=self._namespace+"_actual",
1171
+ n_envs=self._n_envs,
1172
+ action_dim=self._actions_dim,
1173
+ action_names=self._get_action_names(),
1174
+ env_names=None,
1175
+ is_server=True,
1176
+ verbose=self._verbose,
1177
+ vlevel=self._vlevel,
1178
+ safe=True,
1179
+ force_reconnection=True,
1180
+ with_gpu_mirror=self._use_gpu,
1181
+ fill_value=0.0)
1182
+ self._actual_actions.run()
1183
+
1184
+ def _init_rewards(self):
1185
+
1186
+ reward_thresh_default = 1.0
1187
+ n_sub_rewards = len(self._get_rewards_names())
1188
+ device = "cuda" if self._use_gpu else "cpu"
1189
+ self._reward_thresh_lb = torch.full((1, n_sub_rewards), dtype=self._dtype, fill_value=-reward_thresh_default, device=device) # used for clipping rewards
1190
+ self._reward_thresh_ub = torch.full((1, n_sub_rewards), dtype=self._dtype, fill_value=reward_thresh_default, device=device)
1191
+
1192
+ self._sub_rewards = SubRewards(namespace=self._namespace,
1193
+ n_envs=self._n_envs,
1194
+ n_rewards=n_sub_rewards,
1195
+ reward_names=self._get_rewards_names(),
1196
+ env_names=None,
1197
+ is_server=True,
1198
+ verbose=self._verbose,
1199
+ vlevel=self._vlevel,
1200
+ safe=True,
1201
+ force_reconnection=True,
1202
+ with_gpu_mirror=self._use_gpu,
1203
+ fill_value=0.0)
1204
+
1205
+ self._tot_rewards = TotRewards(namespace=self._namespace,
1206
+ n_envs=self._n_envs,
1207
+ reward_names=["total_reward"],
1208
+ env_names=None,
1209
+ is_server=True,
1210
+ verbose=self._verbose,
1211
+ vlevel=self._vlevel,
1212
+ safe=True,
1213
+ force_reconnection=True,
1214
+ with_gpu_mirror=self._use_gpu,
1215
+ fill_value=0.0)
1216
+
1217
+ self._sub_rewards.run()
1218
+ self._tot_rewards.run()
1219
+
1220
+ self._substep_rewards = self._sub_rewards.get_torch_mirror(gpu=self._use_gpu).detach().clone()
1221
+ # used to hold substep rewards (not super mem. efficient)
1222
+ self._is_substep_rew = torch.zeros((self._substep_rewards.shape[1],),dtype=torch.bool,device=device)
1223
+ self._is_substep_rew.fill_(True) # default to all substep rewards
1224
+
1225
+ self._episodic_rewards_metrics = EpisodicRewards(reward_tensor=self._sub_rewards.get_torch_mirror(),
1226
+ reward_names=self._get_rewards_names(),
1227
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1228
+ store_transitions=self._full_db,
1229
+ max_ep_duration=self._max_ep_length())
1230
+ self._episodic_rewards_metrics.set_constant_data_scaling(scaling=self._get_reward_scaling())
1231
+
1232
+ def _get_reward_scaling(self):
1233
+ # to be overridden by child (default to no scaling)
1234
+ return 1
1235
+
1236
+ def _max_ep_length(self):
1237
+ #.should be overriden by child
1238
+ return self._env_opts["episode_timeout_ub"]
1239
+
1240
+ def _init_custom_db_data(self):
1241
+
1242
+ self.custom_db_data = {}
1243
+ # by default always log this contact data
1244
+ rhc_latest_contact_ref = self._rhc_refs.contact_flags.get_torch_mirror()
1245
+ contact_names = self._rhc_refs.rob_refs.contact_names()
1246
+ stepping_data = EpisodicData("RhcRefsFlag", rhc_latest_contact_ref, contact_names,
1247
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1248
+ store_transitions=self._full_db,
1249
+ max_ep_duration=self._max_ep_length())
1250
+ self._add_custom_db_data(db_data=stepping_data)
1251
+
1252
+ # log also action data
1253
+ actions = self._actions.get_torch_mirror()
1254
+ action_names = self._get_action_names()
1255
+ action_data = EpisodicData("Actions", actions, action_names,
1256
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1257
+ store_transitions=self._full_db,
1258
+ max_ep_duration=self._max_ep_length())
1259
+ self._add_custom_db_data(db_data=action_data)
1260
+
1261
+ # and observations
1262
+ observations = self._obs.get_torch_mirror()
1263
+ observations_names = self._get_obs_names()
1264
+ obs_data = EpisodicData("Obs", observations, observations_names,
1265
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1266
+ store_transitions=self._full_db,
1267
+ max_ep_duration=self._max_ep_length())
1268
+ self._add_custom_db_data(db_data=obs_data)
1269
+
1270
+ # log sub-term and sub-truncations data
1271
+ t_scaling=1 # 1 so that we log an interpretable data in terms of why the episode finished
1272
+ data_scaling = torch.full((self._n_envs, 1),
1273
+ fill_value=t_scaling,
1274
+ dtype=torch.int32,device="cpu")
1275
+ sub_term = self._sub_terminations.get_torch_mirror()
1276
+ term = self._terminations.get_torch_mirror()
1277
+ sub_termination_names = self.sub_term_names()
1278
+
1279
+ sub_term_data = EpisodicData("SubTerminations", sub_term, sub_termination_names,
1280
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1281
+ store_transitions=self._full_db,
1282
+ max_ep_duration=self._max_ep_length())
1283
+ sub_term_data.set_constant_data_scaling(enable=True,scaling=data_scaling)
1284
+ self._add_custom_db_data(db_data=sub_term_data)
1285
+ term_data = EpisodicData("Terminations", term, ["terminations"],
1286
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1287
+ store_transitions=self._full_db,
1288
+ max_ep_duration=self._max_ep_length())
1289
+ term_data.set_constant_data_scaling(enable=True,scaling=data_scaling)
1290
+ self._add_custom_db_data(db_data=term_data)
1291
+
1292
+ sub_trunc = self._sub_truncations.get_torch_mirror()
1293
+ trunc = self._truncations.get_torch_mirror()
1294
+ sub_truncations_names = self.sub_trunc_names()
1295
+ sub_trunc_data = EpisodicData("SubTruncations", sub_trunc, sub_truncations_names,
1296
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1297
+ store_transitions=self._full_db,
1298
+ max_ep_duration=self._max_ep_length())
1299
+ sub_trunc_data.set_constant_data_scaling(enable=True,scaling=data_scaling)
1300
+ self._add_custom_db_data(db_data=sub_trunc_data)
1301
+ trunc_data = EpisodicData("Truncations", trunc, ["truncations"],
1302
+ ep_vec_freq=self._env_opts["vec_ep_freq_metrics_db"],
1303
+ store_transitions=self._full_db,
1304
+ max_ep_duration=self._max_ep_length())
1305
+ trunc_data.set_constant_data_scaling(enable=True,scaling=data_scaling)
1306
+ self._add_custom_db_data(db_data=trunc_data)
1307
+
1308
+ def _add_custom_db_data(self, db_data: EpisodicData):
1309
+ self.custom_db_data[db_data.name()] = db_data
1310
+
1311
+ def _init_terminations(self):
1312
+
1313
+ # Boolean array indicating whether each environment episode has terminated after
1314
+ # the current step. An episode termination could occur based on predefined conditions
1315
+ # in the environment, such as reaching a goal or exceeding a time limit.
1316
+
1317
+ self._terminations = Terminations(namespace=self._namespace,
1318
+ n_envs=self._n_envs,
1319
+ is_server=True,
1320
+ verbose=self._verbose,
1321
+ vlevel=self._vlevel,
1322
+ safe=True,
1323
+ force_reconnection=True,
1324
+ with_gpu_mirror=self._use_gpu,
1325
+ fill_value=False)
1326
+ self._terminations.run()
1327
+
1328
+ sub_t_names = self.sub_term_names()
1329
+ self._sub_terminations = SubTerminations(namespace=self._namespace,
1330
+ n_envs=self._n_envs,
1331
+ n_term=len(sub_t_names),
1332
+ term_names=sub_t_names,
1333
+ is_server=True,
1334
+ verbose=self._verbose,
1335
+ vlevel=self._vlevel,
1336
+ safe=True,
1337
+ force_reconnection=True,
1338
+ with_gpu_mirror=self._use_gpu,
1339
+ fill_value=False)
1340
+ self._sub_terminations.run()
1341
+
1342
+ device = "cuda" if self._use_gpu else "cpu"
1343
+ self._is_capsized=torch.zeros((self._n_envs,1),
1344
+ dtype=torch.bool, device=device)
1345
+ self._is_rhc_capsized=torch.zeros((self._n_envs,1),
1346
+ dtype=torch.bool, device=device)
1347
+ self._max_pitch_angle=60.0*math.pi/180.0
1348
+
1349
+ def _init_truncations(self):
1350
+
1351
+ self._truncations = Truncations(namespace=self._namespace,
1352
+ n_envs=self._n_envs,
1353
+ is_server=True,
1354
+ verbose=self._verbose,
1355
+ vlevel=self._vlevel,
1356
+ safe=True,
1357
+ force_reconnection=True,
1358
+ with_gpu_mirror=self._use_gpu,
1359
+ fill_value=False)
1360
+
1361
+ self._truncations.run()
1362
+
1363
+ sub_trc_names = self.sub_trunc_names()
1364
+ self._sub_truncations = SubTruncations(namespace=self._namespace,
1365
+ n_envs=self._n_envs,
1366
+ n_trunc=len(sub_trc_names),
1367
+ truc_names=sub_trc_names,
1368
+ is_server=True,
1369
+ verbose=self._verbose,
1370
+ vlevel=self._vlevel,
1371
+ safe=True,
1372
+ force_reconnection=True,
1373
+ with_gpu_mirror=self._use_gpu,
1374
+ fill_value=False)
1375
+ self._sub_truncations.run()
1376
+
1377
+ def _update_jnt_blacklist(self):
1378
+ device = "cuda" if self._use_gpu else "cpu"
1379
+ all_available_jnts=self.get_observed_joints()
1380
+ blacklist=[]
1381
+ for i in range(len(all_available_jnts)):
1382
+ for pattern in self._jnt_q_blacklist_patterns:
1383
+ if pattern in all_available_jnts[i]:
1384
+ # stop at first pattern match
1385
+ blacklist.append(i)
1386
+ break
1387
+ if not len(blacklist)==0:
1388
+ self._jnt_q_blacklist_idxs=torch.tensor(blacklist, dtype=torch.int, device=device)
1389
+
1390
+ def _attach_to_shared_mem(self):
1391
+
1392
+ # runs shared mem clients for getting observation and setting RHC commands
1393
+
1394
+ # remote stepping data
1395
+ self._remote_stepper = RemoteStepperSrvr(namespace=self._namespace,
1396
+ verbose=self._verbose,
1397
+ vlevel=self._vlevel,
1398
+ force_reconnection=True)
1399
+ self._remote_stepper.run()
1400
+ self._remote_resetter = RemoteResetSrvr(namespace=self._namespace,
1401
+ verbose=self._verbose,
1402
+ vlevel=self._vlevel,
1403
+ force_reconnection=True)
1404
+ self._remote_resetter.run()
1405
+ self._remote_reset_req = RemoteResetRequest(namespace=self._namespace,
1406
+ is_server=False,
1407
+ verbose=self._verbose,
1408
+ vlevel=self._vlevel,
1409
+ safe=True)
1410
+ self._remote_reset_req.run()
1411
+
1412
+ self._jnts_remapping=None
1413
+ self._jnt_q_blacklist_idxs=None
1414
+
1415
+ self._robot_state = RobotState(namespace=self._namespace,
1416
+ is_server=False,
1417
+ safe=self._safe_shared_mem,
1418
+ verbose=self._verbose,
1419
+ vlevel=self._vlevel,
1420
+ with_gpu_mirror=self._use_gpu,
1421
+ with_torch_view=True,
1422
+ enable_height_sensor=self._env_opts["add_heightmap_obs"])
1423
+
1424
+ self._rhc_cmds = RhcCmds(namespace=self._namespace,
1425
+ is_server=False,
1426
+ safe=self._safe_shared_mem,
1427
+ verbose=self._verbose,
1428
+ vlevel=self._vlevel,
1429
+ with_gpu_mirror=self._use_gpu,
1430
+ with_torch_view=True)
1431
+
1432
+ self._rhc_pred = RhcPred(namespace=self._namespace,
1433
+ is_server=False,
1434
+ safe=self._safe_shared_mem,
1435
+ verbose=self._verbose,
1436
+ vlevel=self._vlevel,
1437
+ with_gpu_mirror=self._use_gpu,
1438
+ with_torch_view=True)
1439
+
1440
+ self._rhc_refs = RhcRefs(namespace=self._namespace,
1441
+ is_server=False,
1442
+ safe=self._safe_shared_mem,
1443
+ verbose=self._verbose,
1444
+ vlevel=self._vlevel,
1445
+ with_gpu_mirror=self._use_gpu,
1446
+ with_torch_view=True)
1447
+
1448
+ self._rhc_status = RhcStatus(namespace=self._namespace,
1449
+ is_server=False,
1450
+ verbose=self._verbose,
1451
+ vlevel=self._vlevel,
1452
+ with_gpu_mirror=self._use_gpu,
1453
+ with_torch_view=True)
1454
+
1455
+ self._robot_state.run()
1456
+ self._n_envs = self._robot_state.n_robots()
1457
+ self._n_jnts = self._robot_state.n_jnts()
1458
+ self._n_contacts = self._robot_state.n_contacts() # we assume same n contacts for all rhcs for now
1459
+
1460
+ self._rhc_cmds.run()
1461
+ self._rhc_pred.run()
1462
+ self._rhc_refs.run()
1463
+ self._rhc_status.run()
1464
+ # we read rhc info now and just this time, since it's assumed to be static
1465
+ self._check_controllers_registered(retry=True) # blocking
1466
+ # (we need controllers to be connected to read meaningful data)
1467
+
1468
+ self._rhc_status.rhc_static_info.synch_all(read=True,retry=True)
1469
+ if self._use_gpu:
1470
+ self._rhc_status.rhc_static_info.synch_mirror(from_gpu=False,non_blocking=False)
1471
+ rhc_horizons=self._rhc_status.rhc_static_info.get("horizons",gpu=self._use_gpu)
1472
+ rhc_nnodes=self._rhc_status.rhc_static_info.get("nnodes",gpu=self._use_gpu)
1473
+ rhc_dts=self._rhc_status.rhc_static_info.get("dts",gpu=self._use_gpu)
1474
+
1475
+ # height sensor metadata (client side)
1476
+ if self._env_opts["add_heightmap_obs"]:
1477
+ self._height_grid_size = self._robot_state.height_sensor.grid_size
1478
+ self._height_flat_dim = self._robot_state.height_sensor.n_cols
1479
+ rhc_ncontacts=self._rhc_status.rhc_static_info.get("ncontacts",gpu=self._use_gpu)
1480
+ robot_mass=self._rhc_status.rhc_static_info.get("robot_mass",gpu=self._use_gpu)
1481
+ pred_node_idxs_rhc=self._rhc_status.rhc_static_info.get("pred_node_idx",gpu=self._use_gpu)
1482
+
1483
+ self._n_nodes_rhc=torch.round(rhc_nnodes) # we assume nodes are static during an env lifetime
1484
+ self._rhc_horizons=rhc_horizons
1485
+ self._rhc_dts=rhc_dts
1486
+ self._n_contacts_rhc=rhc_ncontacts
1487
+ self._rhc_robot_masses=robot_mass
1488
+ if (self._rhc_robot_masses == 0).any():
1489
+ zero_indices = torch.nonzero(self._rhc_robot_masses == 0, as_tuple=True)
1490
+ print(zero_indices) # This will print the indices of zero elements
1491
+ Journal.log(self.__class__.__name__,
1492
+ "_attach_to_shared_mem",
1493
+ "Found at least one robot with 0 mass from RHC static info!!",
1494
+ LogType.EXCEP,
1495
+ throw_when_excep=True)
1496
+
1497
+ self._rhc_robot_weight=robot_mass*9.81
1498
+ self._pred_node_idxs_rhc=pred_node_idxs_rhc
1499
+ self._pred_horizon_rhc=self._pred_node_idxs_rhc*self._rhc_dts
1500
+
1501
+ # run server for agent commands
1502
+ self._agent_refs = AgentRefs(namespace=self._namespace,
1503
+ is_server=True,
1504
+ n_robots=self._n_envs,
1505
+ n_jnts=self._robot_state.n_jnts(),
1506
+ n_contacts=self._robot_state.n_contacts(),
1507
+ contact_names=self._robot_state.contact_names(),
1508
+ q_remapping=None,
1509
+ with_gpu_mirror=self._use_gpu,
1510
+ force_reconnection=True,
1511
+ safe=False,
1512
+ verbose=self._verbose,
1513
+ vlevel=self._vlevel,
1514
+ fill_value=0)
1515
+ self._agent_refs.run()
1516
+ q_init_agent_refs=torch.full_like(self._robot_state.root_state.get(data_type="q", gpu=self._use_gpu),fill_value=0.0)
1517
+ q_init_agent_refs[:, 0]=1.0
1518
+ self._agent_refs.rob_refs.root_state.set(data_type="q", data=q_init_agent_refs,
1519
+ gpu=self._use_gpu)
1520
+ if self._use_gpu:
1521
+ self._agent_refs.rob_refs.root_state.synch_mirror(from_gpu=True,non_blocking=True)
1522
+ self._agent_refs.rob_refs.root_state.synch_all(read=False, retry=True)
1523
+ # episode steps counters (for detecting episode truncations for
1524
+ # time limits)
1525
+ self._ep_timeout_counter = EpisodesCounter(namespace=self._namespace,
1526
+ n_envs=self._n_envs,
1527
+ n_steps_lb=self._env_opts["episode_timeout_lb"],
1528
+ n_steps_ub=self._env_opts["episode_timeout_ub"],
1529
+ randomize_offsets_at_startup=True, # this has to be randomized
1530
+ is_server=True,
1531
+ verbose=self._verbose,
1532
+ vlevel=self._vlevel,
1533
+ safe=True,
1534
+ force_reconnection=True,
1535
+ with_gpu_mirror=self._use_gpu,
1536
+ debug=self._debug) # handles step counter through episodes and through envs
1537
+ self._ep_timeout_counter.run()
1538
+ self._task_rand_counter = TaskRandCounter(namespace=self._namespace,
1539
+ n_envs=self._n_envs,
1540
+ n_steps_lb=self._env_opts["n_steps_task_rand_lb"],
1541
+ n_steps_ub=self._env_opts["n_steps_task_rand_ub"],
1542
+ randomize_offsets_at_startup=False, # not necessary since it will be synched with the timeout counter
1543
+ is_server=True,
1544
+ verbose=self._verbose,
1545
+ vlevel=self._vlevel,
1546
+ safe=True,
1547
+ force_reconnection=True,
1548
+ with_gpu_mirror=self._use_gpu,
1549
+ debug=self._debug) # handles step counter through episodes and through envs
1550
+ self._task_rand_counter.run()
1551
+ self._task_rand_counter.sync_counters(other_counter=self._ep_timeout_counter)
1552
+ if self._env_opts["use_random_trunc"]:
1553
+ self._rand_trunc_counter=RandomTruncCounter(namespace=self._namespace,
1554
+ n_envs=self._n_envs,
1555
+ n_steps_lb=self._env_opts["random_trunc_freq"]-self._env_opts["random_trunc_freq_delta"],
1556
+ n_steps_ub=self._env_opts["random_trunc_freq"],
1557
+ randomize_offsets_at_startup=True,
1558
+ is_server=True,
1559
+ verbose=self._verbose,
1560
+ vlevel=self._vlevel,
1561
+ safe=True,
1562
+ force_reconnection=True,
1563
+ with_gpu_mirror=self._use_gpu,
1564
+ debug=False)
1565
+ self._rand_trunc_counter.run()
1566
+ # self._rand_trunc_counter.sync_counters(other_counter=self._ep_timeout_counter)
1567
+ if self._env_opts["use_random_safety_reset"]:
1568
+ self._rand_safety_reset_counter=SafetyRandResetsCounter(namespace=self._namespace,
1569
+ n_envs=self._n_envs,
1570
+ n_steps_lb=self._env_opts["random_reset_freq"],
1571
+ n_steps_ub=self._env_opts["random_reset_freq"],
1572
+ randomize_offsets_at_startup=True,
1573
+ is_server=True,
1574
+ verbose=self._verbose,
1575
+ vlevel=self._vlevel,
1576
+ safe=True,
1577
+ force_reconnection=True,
1578
+ with_gpu_mirror=self._use_gpu,
1579
+ debug=False)
1580
+ self._rand_safety_reset_counter.run()
1581
+ # self._rand_safety_reset_counter.sync_counters(other_counter=self._ep_timeout_counter)
1582
+
1583
+ # timer to track abs time in each env (reset logic to be implemented in child)
1584
+ self._substep_abs_counter = SubStepAbsCounter(namespace=self._namespace,
1585
+ n_envs=self._n_envs,
1586
+ n_steps_lb=1e9,
1587
+ n_steps_ub=1e9,
1588
+ randomize_offsets_at_startup=True, # randomizing startup offsets
1589
+ is_server=True,
1590
+ verbose=self._verbose,
1591
+ vlevel=self._vlevel,
1592
+ safe=True,
1593
+ force_reconnection=True,
1594
+ with_gpu_mirror=self._use_gpu,
1595
+ debug=self._debug)
1596
+ self._substep_abs_counter.run()
1597
+
1598
+ # debug data servers
1599
+ traing_env_param_dict = {}
1600
+ traing_env_param_dict["use_gpu"] = self._use_gpu
1601
+ traing_env_param_dict["debug"] = self._is_debug
1602
+ traing_env_param_dict["n_preinit_steps"] = self._env_opts["n_preinit_steps"]
1603
+ traing_env_param_dict["n_preinit_steps"] = self._n_envs
1604
+
1605
+ self._training_sim_info = SharedTrainingEnvInfo(namespace=self._namespace,
1606
+ is_server=True,
1607
+ training_env_params_dict=traing_env_param_dict,
1608
+ safe=False,
1609
+ force_reconnection=True,
1610
+ verbose=self._verbose,
1611
+ vlevel=self._vlevel)
1612
+ self._training_sim_info.run()
1613
+
1614
+ self._observed_jnt_names=self.set_observed_joints()
1615
+ self._set_jnts_blacklist_pattern()
1616
+ self._update_jnt_blacklist()
1617
+
1618
+ self._prev_root_p_substep=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu).clone()
1619
+ self._prev_root_q_substep=self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu).clone()
1620
+ self._prev_root_p_step=self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu).clone()
1621
+ self._prev_root_q_step=self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu).clone()
1622
+
1623
+ def _activate_rhc_controllers(self):
1624
+ self._rhc_status.activation_state.get_torch_mirror()[:, :] = True
1625
+ self._rhc_status.activation_state.synch_all(read=False, retry=True) # activates all controllers
1626
+
1627
+ def _synch_state(self,
1628
+ gpu: bool = True):
1629
+
1630
+ # read from shared memory on CPU
1631
+ # robot state
1632
+ self._robot_state.root_state.synch_all(read = True, retry = True)
1633
+ self._robot_state.jnts_state.synch_all(read = True, retry = True)
1634
+ # rhc cmds
1635
+ self._rhc_cmds.root_state.synch_all(read = True, retry = True)
1636
+ self._rhc_cmds.jnts_state.synch_all(read = True, retry = True)
1637
+ self._rhc_cmds.contact_wrenches.synch_all(read = True, retry = True)
1638
+ # rhc pred
1639
+ self._rhc_pred.root_state.synch_all(read = True, retry = True)
1640
+ # self._rhc_pred.jnts_state.synch_all(read = True, retry = True)
1641
+ # self._rhc_pred.contact_wrenches.synch_all(read = True, retry = True)
1642
+ # refs for root link and contacts
1643
+ self._rhc_refs.rob_refs.root_state.synch_all(read = True, retry = True)
1644
+ self._rhc_refs.contact_flags.synch_all(read = True, retry = True)
1645
+ self._rhc_refs.flight_info.synch_all(read = True, retry = True)
1646
+ self._rhc_refs.flight_settings_req.synch_all(read = True, retry = True)
1647
+ self._rhc_refs.rob_refs.contact_pos.synch_all(read = True, retry = True)
1648
+ # rhc cost
1649
+ self._rhc_status.rhc_cost.synch_all(read = True, retry = True)
1650
+ # rhc constr. violations
1651
+ self._rhc_status.rhc_constr_viol.synch_all(read = True, retry = True)
1652
+ # failure states
1653
+ self._rhc_status.fails.synch_all(read = True, retry = True)
1654
+ # tot cost and cnstr viol on nodes + step variable
1655
+ self._rhc_status.rhc_nodes_cost.synch_all(read = True, retry = True)
1656
+ self._rhc_status.rhc_nodes_constr_viol.synch_all(read = True, retry = True)
1657
+ self._rhc_status.rhc_fcn.synch_all(read = True, retry = True)
1658
+ self._rhc_status.rhc_fail_idx.synch_all(read = True, retry = True)
1659
+ if self._env_opts["add_heightmap_obs"]:
1660
+ self._robot_state.height_sensor.synch_all(read=True, retry=True)
1661
+ if gpu:
1662
+ # copies data to "mirror" on GPU --> we can do it non-blocking since
1663
+ # in this direction it should be safe
1664
+ self._robot_state.root_state.synch_mirror(from_gpu=False,non_blocking=True) # copies shared data on GPU
1665
+ self._robot_state.jnts_state.synch_mirror(from_gpu=False,non_blocking=True)
1666
+ self._rhc_cmds.root_state.synch_mirror(from_gpu=False,non_blocking=True)
1667
+ self._rhc_cmds.jnts_state.synch_mirror(from_gpu=False,non_blocking=True)
1668
+ self._rhc_cmds.contact_wrenches.synch_mirror(from_gpu=False,non_blocking=True)
1669
+ self._rhc_pred.root_state.synch_mirror(from_gpu=False,non_blocking=True)
1670
+ # self._rhc_pred.jnts_state.synch_mirror(from_gpu=False,non_blocking=True)
1671
+ # self._rhc_pred.contact_wrenches.synch_mirror(from_gpu=False,non_blocking=True)
1672
+ self._rhc_refs.rob_refs.root_state.synch_mirror(from_gpu=False,non_blocking=True)
1673
+ self._rhc_refs.contact_flags.synch_mirror(from_gpu=False,non_blocking=True)
1674
+ self._rhc_refs.rob_refs.contact_pos.synch_mirror(from_gpu=False,non_blocking=True)
1675
+ self._rhc_refs.flight_info.synch_mirror(from_gpu=False,non_blocking=True)
1676
+ self._rhc_refs.flight_settings_req.synch_mirror(from_gpu=False,non_blocking=True)
1677
+ self._rhc_status.rhc_cost.synch_mirror(from_gpu=False,non_blocking=True)
1678
+ self._rhc_status.rhc_constr_viol.synch_mirror(from_gpu=False,non_blocking=True)
1679
+ self._rhc_status.fails.synch_mirror(from_gpu=False,non_blocking=True)
1680
+ self._rhc_status.rhc_nodes_cost.synch_mirror(from_gpu=False,non_blocking=True)
1681
+ self._rhc_status.rhc_nodes_constr_viol.synch_mirror(from_gpu=False,non_blocking=True)
1682
+ self._rhc_status.rhc_fcn.synch_mirror(from_gpu=False,non_blocking=True)
1683
+ self._rhc_status.rhc_fail_idx.synch_mirror(from_gpu=False,non_blocking=True)
1684
+ if self._env_opts["add_heightmap_obs"]:
1685
+ self._robot_state.height_sensor.synch_mirror(from_gpu=False, non_blocking=True)
1686
+ torch.cuda.synchronize() # ensuring that all the streams on the GPU are completed \
1687
+ # before the CPU continues execution
1688
+
1689
+ def _override_refs(self,
1690
+ env_indxs: torch.Tensor = None):
1691
+
1692
+ # just used for setting agent refs externally (i.e. from shared mem on CPU)
1693
+ self._agent_refs.rob_refs.root_state.synch_all(read=True,retry=True) # first read from mem
1694
+ if self._use_gpu:
1695
+ # copies latest refs to GPU
1696
+ self._agent_refs.rob_refs.root_state.synch_mirror(from_gpu=False,non_blocking=False)
1697
+
1698
+ def _clamp_obs(self,
1699
+ obs: torch.Tensor):
1700
+ if self._is_debug:
1701
+ self._check_finite(obs, "observations", False)
1702
+ torch.nan_to_num(input=obs, out=obs, nan=self._obs_threshold_ub,
1703
+ posinf=self._obs_threshold_ub,
1704
+ neginf=self._obs_threshold_lb) # prevent nans
1705
+
1706
+ obs.clamp_(self._obs_threshold_lb, self._obs_threshold_ub)
1707
+
1708
+ def _clamp_rewards(self,
1709
+ rewards: torch.Tensor):
1710
+ if self._is_debug:
1711
+ self._check_finite(rewards, "rewards", False)
1712
+ torch.nan_to_num(input=rewards, out=rewards, nan=0.0,
1713
+ posinf=None,
1714
+ neginf=None) # prevent nans
1715
+ rewards.clamp_(self._reward_thresh_lb, self._reward_thresh_ub)
1716
+
1717
+ def get_actions_lb(self):
1718
+ return self._actions_lb
1719
+
1720
+ def get_actions_ub(self):
1721
+ return self._actions_ub
1722
+
1723
+ def get_actions_scale(self):
1724
+ return self._actions_scale
1725
+
1726
+ def get_actions_offset(self):
1727
+ return self._actions_offset
1728
+
1729
+ def get_obs_lb(self):
1730
+ return self._obs_lb
1731
+
1732
+ def get_obs_ub(self):
1733
+ return self._obs_ub
1734
+
1735
+ def get_obs_scale(self):
1736
+ self._obs_scale = (self._obs_ub - self._obs_lb)/2.0
1737
+ return self._obs_scale
1738
+
1739
+ def get_obs_offset(self):
1740
+ self._obs_offset = (self._obs_ub + self._obs_lb)/2.0
1741
+ return self._obs_offset
1742
+
1743
+ def switch_random_reset(self, on: bool = True):
1744
+ self._random_reset_active=on
1745
+
1746
+ def set_jnts_remapping(self,
1747
+ remapping: List = None):
1748
+
1749
+ self._jnts_remapping=remapping
1750
+ if self._jnts_remapping is not None:
1751
+ self._robot_state.set_jnts_remapping(jnts_remapping=self._jnts_remapping)
1752
+ self._rhc_cmds.set_jnts_remapping(jnts_remapping=self._jnts_remapping)
1753
+ self._rhc_pred.set_jnts_remapping(jnts_remapping=self._jnts_remapping)
1754
+ # we need to also update the list of observed joints to match
1755
+ available_joints=self._robot_state.jnt_names()
1756
+ # the remapping ordering
1757
+ self._observed_jnt_names=[]
1758
+ for i in range(len(available_joints)):
1759
+ self._observed_jnt_names.append(available_joints[self._jnts_remapping[i]])
1760
+
1761
+ self._update_jnt_blacklist()
1762
+
1763
+ updated_obs_names=self._get_obs_names() # get updated obs names (should use get_observed_joints
1764
+ # internally, so that jnt names are updated)
1765
+
1766
+ # also update jnt obs names on shared memory
1767
+ names_old=self._obs.get_obs_names()
1768
+ names_old_next=self._next_obs.get_obs_names()
1769
+ names_old[:]=updated_obs_names
1770
+ names_old_next[:]=updated_obs_names
1771
+ self._obs.update_names()
1772
+ self._next_obs.update_names()
1773
+
1774
+ # also update
1775
+ if "Obs" in self.custom_db_data:
1776
+ db_obs_names=self.custom_db_data["Obs"].data_names()
1777
+ db_obs_names[:]=updated_obs_names
1778
+
1779
+ def _check_finite(self,
1780
+ tensor: torch.Tensor,
1781
+ name: str,
1782
+ throw: bool = False):
1783
+ if not torch.isfinite(tensor).all().item():
1784
+ exception = f"Found nonfinite elements in {name} tensor!!"
1785
+ non_finite_idxs=torch.nonzero(~torch.isfinite(tensor))
1786
+ n_nonf_elems=non_finite_idxs.shape[0]
1787
+
1788
+ if name=="observations":
1789
+ for i in range(n_nonf_elems):
1790
+ db_msg=f"{self.obs_names()[non_finite_idxs[i,1]]} (env. {non_finite_idxs[i,0]}):" + \
1791
+ f" {tensor[non_finite_idxs[i,0],non_finite_idxs[i,1]].item()}"
1792
+ print(db_msg)
1793
+ if name=="rewards":
1794
+ for i in range(n_nonf_elems):
1795
+ db_msg=f"{self.sub_rew_names()[non_finite_idxs[i,1]]} (env. {non_finite_idxs[i,0]}):" + \
1796
+ f" {tensor[non_finite_idxs[i,0],non_finite_idxs[i,1]].item()}"
1797
+ print(db_msg)
1798
+ print(tensor)
1799
+ Journal.log(self.__class__.__name__,
1800
+ "_check_finite",
1801
+ exception,
1802
+ LogType.EXCEP,
1803
+ throw_when_excep = throw)
1804
+
1805
+ def _check_controllers_registered(self,
1806
+ retry: bool = False):
1807
+
1808
+ if retry:
1809
+ self._rhc_status.controllers_counter.synch_all(read=True, retry=True)
1810
+ n_connected_controllers = self._rhc_status.controllers_counter.get_torch_mirror()[0, 0].item()
1811
+ while not (n_connected_controllers == self._n_envs):
1812
+ warn = f"Expected {self._n_envs} controllers to be connected during training, " + \
1813
+ f"but got {n_connected_controllers}. Will wait for all to be connected..."
1814
+ Journal.log(self.__class__.__name__,
1815
+ "_check_controllers_registered",
1816
+ warn,
1817
+ LogType.WARN,
1818
+ throw_when_excep = False)
1819
+ nsecs = int(2 * 1000000000)
1820
+ PerfSleep.thread_sleep(nsecs)
1821
+ self._rhc_status.controllers_counter.synch_all(read=True, retry=True)
1822
+ n_connected_controllers = self._rhc_status.controllers_counter.get_torch_mirror()[0, 0].item()
1823
+ info = f"All {n_connected_controllers} controllers connected!"
1824
+ Journal.log(self.__class__.__name__,
1825
+ "_check_controllers_registered",
1826
+ info,
1827
+ LogType.INFO,
1828
+ throw_when_excep = False)
1829
+ return True
1830
+ else:
1831
+ self._rhc_status.controllers_counter.synch_all(read=True, retry=True)
1832
+ n_connected_controllers = self._rhc_status.controllers_counter.get_torch_mirror()[0, 0].item()
1833
+ if not (n_connected_controllers == self._n_envs):
1834
+ exception = f"Expected {self._n_envs} controllers to be connected during training, " + \
1835
+ f"but got {n_connected_controllers}. Aborting..."
1836
+ Journal.log(self.__class__.__name__,
1837
+ "_check_controllers_registered",
1838
+ exception,
1839
+ LogType.EXCEP,
1840
+ throw_when_excep = False)
1841
+ return False
1842
+ return True
1843
+
1844
+ def _check_truncations(self):
1845
+
1846
+ self._check_sub_truncations()
1847
+ sub_truncations = self._sub_truncations.get_torch_mirror(gpu=self._use_gpu)
1848
+ truncations = self._truncations.get_torch_mirror(gpu=self._use_gpu)
1849
+ truncations[:, :] = torch.any(sub_truncations,dim=1,keepdim=True)
1850
+
1851
+ def _check_terminations(self):
1852
+
1853
+ self._check_sub_terminations()
1854
+ sub_terminations = self._sub_terminations.get_torch_mirror(gpu=self._use_gpu)
1855
+ terminations = self._terminations.get_torch_mirror(gpu=self._use_gpu)
1856
+ terminations[:, :] = torch.any(sub_terminations,dim=1,keepdim=True)
1857
+
1858
+ def _check_sub_truncations(self):
1859
+ # default behaviour-> to be overriden by child
1860
+ sub_truncations = self._sub_truncations.get_torch_mirror(gpu=self._use_gpu)
1861
+ sub_truncations[:, 0:1]=self._ep_timeout_counter.time_limits_reached()
1862
+
1863
+ def _check_sub_terminations(self):
1864
+ # default behaviour-> to be overriden by child
1865
+ sub_terminations = self._sub_terminations.get_torch_mirror(gpu=self._use_gpu)
1866
+ robot_q_meas = self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
1867
+ robot_q_pred = self._rhc_cmds.root_state.get(data_type="q",gpu=self._use_gpu)
1868
+
1869
+ # terminate when either the real robot or the prediction from the MPC are capsized
1870
+ check_capsize(quat=robot_q_meas,max_angle=self._max_pitch_angle,
1871
+ output_t=self._is_capsized)
1872
+ check_capsize(quat=robot_q_pred,max_angle=self._max_pitch_angle,
1873
+ output_t=self._is_rhc_capsized)
1874
+
1875
+ sub_terminations[:, 0:1] = self._rhc_status.fails.get_torch_mirror(gpu=self._use_gpu)
1876
+ sub_terminations[:, 1:2] = self._is_capsized
1877
+ sub_terminations[:, 2:3] = self._is_rhc_capsized
1878
+
1879
+ def is_action_continuous(self):
1880
+ return self._is_continuous_actions
1881
+
1882
+ def is_action_discrete(self):
1883
+ return ~self._is_continuous_actions
1884
+
1885
+ @abstractmethod
1886
+ def _pre_substep(self):
1887
+ pass
1888
+
1889
+ @abstractmethod
1890
+ def _custom_post_step(self,episode_finished):
1891
+ pass
1892
+
1893
+ @abstractmethod
1894
+ def _custom_post_substp_post_rew(self):
1895
+ pass
1896
+
1897
+ @abstractmethod
1898
+ def _custom_post_substp_pre_rew(self):
1899
+ pass
1900
+
1901
+ @abstractmethod
1902
+ def _apply_actions_to_rhc(self):
1903
+ pass
1904
+
1905
+ def _override_actions_with_demo(self):
1906
+ pass
1907
+
1908
+ @abstractmethod
1909
+ def _compute_substep_rewards(self):
1910
+ pass
1911
+
1912
+ @abstractmethod
1913
+ def _set_substep_rew(self):
1914
+ pass
1915
+
1916
+ @abstractmethod
1917
+ def _set_substep_obs(self):
1918
+ pass
1919
+
1920
+ @abstractmethod
1921
+ def _compute_step_rewards(self):
1922
+ pass
1923
+
1924
+ @abstractmethod
1925
+ def _fill_substep_obs(self,
1926
+ obs: torch.Tensor):
1927
+ pass
1928
+
1929
+ @abstractmethod
1930
+ def _fill_step_obs(self,
1931
+ obs: torch.Tensor):
1932
+ pass
1933
+
1934
+ @abstractmethod
1935
+ def _randomize_task_refs(self,
1936
+ env_indxs: torch.Tensor = None):
1937
+ pass
1938
+
1939
+ def _custom_post_init(self):
1940
+ pass
1941
+
1942
+ def _get_avrg_substep_root_twist(self,
1943
+ out: torch.Tensor,
1944
+ base_loc: bool = True):
1945
+ # to be called at each substep
1946
+ robot_p_meas = self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)
1947
+ robot_q_meas = self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
1948
+
1949
+ root_v_avrg_w=(robot_p_meas-self._prev_root_p_substep)/self._substep_dt
1950
+ root_omega_avrg_w=quaternion_to_angular_velocity(q_diff=quaternion_difference(self._prev_root_q_substep,robot_q_meas),\
1951
+ dt=self._substep_dt)
1952
+ twist_w=torch.cat((root_v_avrg_w,
1953
+ root_omega_avrg_w),
1954
+ dim=1)
1955
+ if not base_loc:
1956
+ self._prev_root_p_substep[:, :]=robot_p_meas
1957
+ self._prev_root_q_substep[:, :]=robot_q_meas
1958
+ out[:, :]=twist_w
1959
+ # rotate using the current (end-of-substep) orientation for consistency with other signals
1960
+ world2base_frame(t_w=twist_w, q_b=robot_q_meas, t_out=out)
1961
+ self._prev_root_p_substep[:, :]=robot_p_meas
1962
+ self._prev_root_q_substep[:, :]=robot_q_meas
1963
+
1964
+ def _get_avrg_step_root_twist(self,
1965
+ out: torch.Tensor,
1966
+ base_loc: bool = True):
1967
+ # to be called after substeps of actions repeats
1968
+ robot_p_meas = self._robot_state.root_state.get(data_type="p",gpu=self._use_gpu)
1969
+ robot_q_meas = self._robot_state.root_state.get(data_type="q",gpu=self._use_gpu)
1970
+
1971
+ dt=self._substep_dt*self._action_repeat # accounting for frame skipping
1972
+ root_v_avrg_w=(robot_p_meas-self._prev_root_p_step)/(dt)
1973
+ root_omega_avrg_w=quaternion_to_angular_velocity(q_diff=quaternion_difference(self._prev_root_q_step,robot_q_meas),\
1974
+ dt=dt)
1975
+ twist_w=torch.cat((root_v_avrg_w,
1976
+ root_omega_avrg_w),
1977
+ dim=1)
1978
+ if not base_loc:
1979
+ out[:, :]=twist_w
1980
+ # rotate using the current (end-of-step) orientation for consistency with other signals
1981
+ world2base_frame(t_w=twist_w, q_b=robot_q_meas, t_out=out)
1982
+
1983
+ def _get_avrg_rhc_root_twist(self,
1984
+ out: torch.Tensor,
1985
+ base_loc: bool = True):
1986
+
1987
+ rhc_root_p =self._rhc_cmds.root_state.get(data_type="p",gpu=self._use_gpu)
1988
+ rhc_root_q =self._rhc_cmds.root_state.get(data_type="q",gpu=self._use_gpu)
1989
+ rhc_root_p_pred =self._rhc_pred.root_state.get(data_type="p",gpu=self._use_gpu)
1990
+ rhc_root_q_pred =self._rhc_pred.root_state.get(data_type="q",gpu=self._use_gpu)
1991
+
1992
+ rhc_root_v_avrg_rhc_w=(rhc_root_p_pred-rhc_root_p)/self._pred_horizon_rhc
1993
+ rhc_root_omega_avrg_rhc_w=quaternion_to_angular_velocity(q_diff=quaternion_difference(rhc_root_q,rhc_root_q_pred),\
1994
+ dt=self._pred_horizon_rhc)
1995
+
1996
+ rhc_pred_avrg_twist_rhc_w = torch.cat((rhc_root_v_avrg_rhc_w,
1997
+ rhc_root_omega_avrg_rhc_w),
1998
+ dim=1)
1999
+ if not base_loc:
2000
+ out[:, :]=rhc_pred_avrg_twist_rhc_w
2001
+ # to rhc base frame (using first node as reference)
2002
+ world2base_frame(t_w=rhc_pred_avrg_twist_rhc_w, q_b=rhc_root_q, t_out=out)
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.4)
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.5)
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=False)
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"]
world_interface_base.py ADDED
@@ -0,0 +1,1719 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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, 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
+ self._env_opts["wheel_joint_patterns"]=["wheel"]
179
+ self._env_opts["filter_wheel_pos_ref"]=True
180
+ self._env_opts["zero_wheel_eff_ref"]=True
181
+
182
+ self._env_opts["enable_height_sensor"]=False
183
+ self._env_opts["height_sensor_resolution"]=0.16
184
+ self._env_opts["height_sensor_pixels"]=10
185
+ self._env_opts["height_sensor_lateral_offset"]=0.0
186
+ self._env_opts["height_sensor_forward_offset"]=0.0
187
+
188
+ self._env_opts["run_cluster_bootstrap"] = False
189
+
190
+ self._filter_step_ssteps_freq=None
191
+
192
+ self._env_opts.update(env_opts)
193
+
194
+ self.step_counter = 0 # global step counter
195
+ self._n_init_steps = n_init_step # n steps to be performed before applying solutions from control clusters
196
+ self._srdf_dump_paths = robot_srdf_paths
197
+ self._homers = {}
198
+ self._homing = None
199
+ self._jnt_imp_cntrl_shared_data = {}
200
+ self._jnt_imp_controllers = {}
201
+ self._jnt_imp_config_paths = {}
202
+
203
+ # control cluster data
204
+ self.cluster_sim_step_counters = {}
205
+ self.cluster_servers = {}
206
+ self._trigger_sol = {}
207
+ self._wait_sol = {}
208
+ self._cluster_dt = {}
209
+ self._robot_urdf_paths={}
210
+ self._robot_srdf_paths={}
211
+ self._contact_names={}
212
+ self._num_contacts={}
213
+
214
+ for i in range(len(self._robot_names)):
215
+ robot_name = self._robot_names[i]
216
+ self._cluster_dt[robot_name]=cluster_dt[i]
217
+ self._robot_urdf_paths[robot_name]=robot_urdf_paths[i]
218
+ self._robot_srdf_paths[robot_name]=robot_srdf_paths[i]
219
+ self._contact_names[robot_name]=None
220
+ self._num_contacts[robot_name]=n_contacts[i]
221
+ self._jnt_imp_config_paths[robot_name]=jnt_imp_config_paths[i]
222
+ # db data
223
+ self.debug_data = {}
224
+ self.debug_data["time_to_step_world"] = np.nan
225
+ self.debug_data["time_to_get_states_from_env"] = np.nan
226
+ self.debug_data["cluster_sol_time"] = {}
227
+ self.debug_data["cluster_state_update_dt"] = {}
228
+ self.debug_data["sim_time"] = {}
229
+ self.debug_data["cluster_time"] = {}
230
+
231
+ self._env_timer = time.perf_counter()
232
+
233
+ # remote sim stepping options
234
+ self._timeout = timeout_ms # timeout for remote stepping
235
+ self._use_remote_stepping = use_remote_stepping
236
+ # should use remote stepping
237
+ self._remote_steppers = {}
238
+ self._remote_resetters = {}
239
+ self._remote_reset_requests = {}
240
+ self._is_first_trigger = {}
241
+
242
+ self._closed = False
243
+
244
+ self._this_child_path=os.path.abspath(inspect.getfile(self.__class__))
245
+ self._descr_dump_path=dump_basepath+"/"+f"{self.__class__.__name__}"
246
+ self._urdf_dump_paths = {}
247
+ self._srdf_dump_paths = {}
248
+ self.xrdf_cmd_vals = [] # by default empty, needs to be overriden by
249
+ # child class
250
+ self._world_iface_files_server=None
251
+
252
+ self._override_low_lev_controller=override_low_lev_controller
253
+
254
+ self._root_p = {}
255
+ self._root_q = {}
256
+ self._jnts_q = {}
257
+ self._root_p_prev = {} # used for num differentiation
258
+ self._root_q_prev = {} # used for num differentiation
259
+ self._jnts_q_prev = {} # used for num differentiation
260
+ self._root_v_prev = {} # used for num differentiation
261
+ self._root_omega_prev = {} # used for num differentiation
262
+ self._root_p_default = {}
263
+ self._root_q_default = {}
264
+ self._jnts_q_default = {}
265
+
266
+ self._gravity_normalized = {}
267
+ self._gravity_normalized_base_loc = {}
268
+
269
+ self._root_v = {}
270
+ self._root_v_base_loc = {}
271
+ self._root_v_default = {}
272
+ self._root_omega = {}
273
+ self._root_omega_base_loc = {}
274
+ self._root_omega_default = {}
275
+ self._root_a = {}
276
+ self._root_a_base_loc = {}
277
+ self._root_alpha = {}
278
+ self._root_alpha_base_loc = {}
279
+
280
+ self._jnts_v = {}
281
+ self._jnt_vel_filter = {}
282
+ self._jnts_v_default = {}
283
+ self._jnts_eff = {}
284
+ self._jnts_eff_default = {}
285
+
286
+ self._root_pos_offsets = {}
287
+ self._root_q_offsets = {}
288
+ self._root_q_offsets_yaw = {}
289
+ self._root_q_yaw_rel_ws = {}
290
+
291
+ self._parse_env_opts()
292
+
293
+ self._enable_height_shared = self._env_opts["enable_height_sensor"]
294
+ self._height_sensor_resolution = self._env_opts["height_sensor_resolution"]
295
+ self._height_sensor_pixels = self._env_opts["height_sensor_pixels"]
296
+
297
+ self._pre_setup() # child's method
298
+
299
+ self._init_world() # after this point all info from sim or robot is
300
+ # available
301
+
302
+ self._publish_world_interface_files()
303
+
304
+ setup_ok=self._setup()
305
+ if not setup_ok:
306
+ self.close()
307
+
308
+ self._exit_request=False
309
+ signal.signal(signal.SIGINT, self.signal_handler)
310
+
311
+ def signal_handler(self, sig, frame):
312
+ Journal.log(self.__class__.__name__,
313
+ "signal_handler",
314
+ "received SIGINT -> cleaning up",
315
+ LogType.WARN)
316
+ self._exit_request=True
317
+
318
+ def __del__(self):
319
+ self.close()
320
+
321
+ def is_closed(self):
322
+ return self._closed
323
+
324
+ def close(self) -> None:
325
+ if not self._closed:
326
+ for i in range(len(self._robot_names)):
327
+ if self._robot_names[i] in self.cluster_servers:
328
+ self.cluster_servers[self._robot_names[i]].close()
329
+ if self._use_remote_stepping[i]: # remote signaling
330
+ if self._robot_names[i] in self._remote_reset_requests:
331
+ self._remote_reset_requests[self._robot_names[i]].close()
332
+ self._remote_resetters[self._robot_names[i]].close()
333
+ self._remote_steppers[self._robot_names[i]].close()
334
+ if self._robot_names[i] in self._jnt_imp_cntrl_shared_data:
335
+ jnt_imp_shared_data=self._jnt_imp_cntrl_shared_data[self._robot_names[i]]
336
+ if jnt_imp_shared_data is not None:
337
+ jnt_imp_shared_data.close()
338
+ if self._remote_exit_flag is not None:
339
+ self._remote_exit_flag.close()
340
+ if self._world_iface_files_server is not None:
341
+ self._world_iface_files_server.close()
342
+ self._close()
343
+ self._closed=True
344
+
345
+ def _collect_world_interface_files(self):
346
+ files = [self._this_child_path]
347
+ # prefer generated URDF/SRDF if available, fallback to provided xacros
348
+ if len(self._urdf_dump_paths) > 0:
349
+ files.extend(list(self._urdf_dump_paths.values()))
350
+ else:
351
+ files.extend(list(self._robot_urdf_paths.values()))
352
+ if len(self._srdf_dump_paths) > 0:
353
+ files.extend(list(self._srdf_dump_paths.values()))
354
+ else:
355
+ files.extend(list(self._robot_srdf_paths.values()))
356
+ files.extend(list(self._jnt_imp_config_paths.values()))
357
+ # remove duplicates while preserving order
358
+ unique_files=[]
359
+ for f in files:
360
+ if f not in unique_files:
361
+ unique_files.append(f)
362
+ return unique_files
363
+
364
+ def _publish_world_interface_files(self):
365
+
366
+ if not any(self._use_remote_stepping):
367
+ return
368
+ self._world_iface_files_server=StringTensorServer(length=1,
369
+ basename="SharedWorldInterfaceFilesDropDir",
370
+ name_space=self._robot_names[0],
371
+ verbose=self._verbose,
372
+ vlevel=self._vlevel,
373
+ force_reconnection=True)
374
+ self._world_iface_files_server.run()
375
+ combined_paths=", ".join(self._collect_world_interface_files())
376
+ while not self._world_iface_files_server.write_vec([combined_paths], 0):
377
+ Journal.log(self.__class__.__name__,
378
+ "_publish_world_interface_files",
379
+ f"Failed to pub world interface files. Retrying...",
380
+ LogType.WARN)
381
+ time.sleep(0.1)
382
+ Journal.log(self.__class__.__name__,
383
+ "_publish_world_interface_files",
384
+ f"World interface files advertised: {combined_paths}",
385
+ LogType.STAT)
386
+
387
+ def _setup(self) -> bool:
388
+
389
+ for i in range(len(self._robot_names)):
390
+ robot_name = self._robot_names[i]
391
+
392
+ # normalized gravity vector
393
+ self._gravity_normalized[robot_name]=torch.full_like(self._root_v[robot_name], fill_value=0.0)
394
+ self._gravity_normalized[robot_name][:, 2]=-1.0
395
+ self._gravity_normalized_base_loc[robot_name]=self._gravity_normalized[robot_name].detach().clone()
396
+
397
+ # Pre-allocate yaw-related buffers once and reuse them in root_q_yaw_rel().
398
+ q_ref = self._root_q[robot_name]
399
+ self._root_q_offsets_yaw[robot_name] = torch.zeros(
400
+ (self._num_envs,), dtype=q_ref.dtype, device=q_ref.device)
401
+ self._root_q_yaw_rel_ws[robot_name] = {
402
+ "yaw_abs": torch.zeros((self._num_envs,), dtype=q_ref.dtype, device=q_ref.device),
403
+ "yaw_rel": torch.zeros((self._num_envs,), dtype=q_ref.dtype, device=q_ref.device),
404
+ "yaw_sin": torch.zeros((self._num_envs,), dtype=q_ref.dtype, device=q_ref.device),
405
+ "yaw_cos": torch.zeros((self._num_envs,), dtype=q_ref.dtype, device=q_ref.device),
406
+ "q_abs_unit": torch.zeros_like(q_ref),
407
+ "q_yaw_abs": torch.zeros_like(q_ref),
408
+ "q_yaw_rel": torch.zeros_like(q_ref),
409
+ "q_yaw_abs_conj": torch.zeros_like(q_ref),
410
+ "q_pr": torch.zeros_like(q_ref),
411
+ "q_rel": torch.zeros_like(q_ref),
412
+ }
413
+
414
+ self.cluster_sim_step_counters[robot_name]=0
415
+ self._is_first_trigger[robot_name] = True
416
+ if not isinstance(self._cluster_dt[robot_name], (float)):
417
+ exception = f"cluster_dt[{i}] should be a float!"
418
+ Journal.log(self.__class__.__name__,
419
+ "_setup",
420
+ exception,
421
+ LogType.EXCEP,
422
+ throw_when_excep = False)
423
+ return False
424
+ self._cluster_dt[robot_name] = self._cluster_dt[robot_name]
425
+ self._trigger_sol[robot_name] = True # allow first trigger
426
+ self._wait_sol[robot_name] = False
427
+
428
+ # initialize a lrhc cluster server for communicating with rhc controllers
429
+ self.cluster_servers[robot_name] = AugMpcClusterServer(cluster_size=self._num_envs,
430
+ cluster_dt=self._cluster_dt[robot_name],
431
+ control_dt=self.physics_dt(),
432
+ jnt_names=self._robot_jnt_names(robot_name=robot_name),
433
+ n_contacts=self._n_contacts(robot_name=robot_name),
434
+ contact_linknames=self._contact_names[robot_name],
435
+ verbose=self._verbose,
436
+ vlevel=self._vlevel,
437
+ debug=self._debug,
438
+ robot_name=robot_name,
439
+ use_gpu=self._use_gpu,
440
+ force_reconnection=self._force_reconnection,
441
+ timeout_ms=self._timeout,
442
+ enable_height_sensor=self._enable_height_shared,
443
+ height_grid_size=self._height_sensor_pixels,
444
+ height_grid_resolution=self._height_sensor_resolution)
445
+ self.cluster_servers[robot_name].run()
446
+ self.debug_data["cluster_sol_time"][robot_name] = np.nan
447
+ self.debug_data["cluster_state_update_dt"][robot_name] = np.nan
448
+ self.debug_data["sim_time"][robot_name] = np.nan
449
+ # remote sim stepping
450
+ if self._use_remote_stepping[i]:
451
+ self._remote_steppers[robot_name] = RemoteStepperClnt(namespace=robot_name,
452
+ verbose=self._debug,
453
+ vlevel=self._vlevel)
454
+ self._remote_resetters[robot_name] = RemoteResetClnt(namespace=robot_name,
455
+ verbose=self._debug,
456
+ vlevel=self._vlevel)
457
+ self._remote_reset_requests[robot_name] = RemoteResetRequest(namespace=robot_name,
458
+ n_env=self._num_envs,
459
+ is_server=True,
460
+ verbose=self._debug,
461
+ vlevel=self._vlevel,
462
+ force_reconnection=self._force_reconnection,
463
+ safe=False)
464
+ self._remote_steppers[robot_name].run()
465
+ self._remote_resetters[robot_name].run()
466
+ self._remote_reset_requests[robot_name].run()
467
+ else:
468
+ self._remote_steppers[robot_name] = None
469
+ self._remote_reset_requests[robot_name] = None
470
+ self._remote_resetters[robot_name] = None
471
+
472
+ self._homers[robot_name] = RobotHomer(srdf_path=self._srdf_dump_paths[robot_name],
473
+ jnt_names=self._robot_jnt_names(robot_name=robot_name),
474
+ filter=True,
475
+ verbose=self._verbose)
476
+ robot_homing=torch.from_numpy(self._homers[robot_name].get_homing().reshape(1,-1))
477
+ if "cuda" in self._device:
478
+ robot_homing=robot_homing.cuda()
479
+ self._homing=robot_homing.repeat(self._num_envs, 1)
480
+
481
+ self._jnts_q_default[robot_name] = self._homing
482
+ self._set_jnts_to_homing(robot_name=robot_name)
483
+ self._set_root_to_defconfig(robot_name=robot_name)
484
+ self._reset_sim()
485
+
486
+ self._init_safe_cluster_actions(robot_name=robot_name)
487
+
488
+ Journal.log(self.__class__.__name__,
489
+ "_setup",
490
+ f"Will use joint impedance config at {self._jnt_imp_config_paths[robot_name]} for {robot_name}",
491
+ LogType.STAT)
492
+
493
+ self._jnt_imp_controllers[robot_name] = self._generate_jnt_imp_control(robot_name=robot_name)
494
+ self._jnt_imp_controllers[robot_name].set_velocity_controlled_joints(
495
+ name_patterns=self._env_opts["wheel_joint_patterns"],
496
+ filter_pos_ref=self._env_opts["filter_wheel_pos_ref"],
497
+ zero_eff_ref=self._env_opts["zero_wheel_eff_ref"])
498
+ self._jnt_imp_cntrl_shared_data[robot_name] = JntImpCntrlData(is_server=True,
499
+ n_envs=self._num_envs,
500
+ n_jnts=len(self._robot_jnt_names(robot_name=robot_name)),
501
+ jnt_names=self._robot_jnt_names(robot_name=robot_name),
502
+ namespace=robot_name,
503
+ verbose=self._verbose,
504
+ force_reconnection=self._force_reconnection,
505
+ vlevel=self._vlevel,
506
+ use_gpu=self._use_gpu,
507
+ safe=False)
508
+ self._jnt_imp_cntrl_shared_data[robot_name].run()
509
+
510
+ self._jnt_vel_filter[robot_name]=None
511
+ if self._env_opts["filter_jnt_vel"]:
512
+ self._jnt_vel_filter[robot_name]=FirstOrderFilter(dt=1.0/self._env_opts["filter_sampling_rate"],
513
+ filter_BW=self._env_opts["filter_cutoff_freq"],
514
+ rows=self._num_envs,
515
+ cols=len(self._robot_jnt_names(robot_name=robot_name)),
516
+ device=self._device,
517
+ dtype=self._dtype)
518
+
519
+ physics_rate=1.0/self.physics_dt()
520
+ self._filter_step_ssteps_freq=int(physics_rate/self._env_opts["filter_sampling_rate"])
521
+ if self._filter_step_ssteps_freq <=0:
522
+ Journal.log(self.__class__.__name__,
523
+ "_setup",
524
+ f"The filter_sampling_rate should be smaller that the physics rate ({physics_rate} Hz)",
525
+ LogType.EXCEP,
526
+ throw_when_excep=True)
527
+
528
+ for n in range(self._n_init_steps): # run some initialization steps
529
+ if hasattr(self, "_alter_twist_warmup"):
530
+ self._alter_twist_warmup(robot_name=robot_name, env_indxs=None)
531
+ self._step_world()
532
+
533
+ self._read_jnts_state_from_robot(robot_name=robot_name,
534
+ env_indxs=None)
535
+ self._read_root_state_from_robot(robot_name=robot_name,
536
+ env_indxs=None)
537
+ # allow child to perform additional warmup validations (e.g., terrain/tilt)
538
+ # retry_done = False
539
+ if hasattr(self, "_post_warmup_validation"):
540
+ failing = self._post_warmup_validation(robot_name=robot_name)
541
+ if failing is not None and failing.numel() > 0:
542
+ # retry: reset only failing envs, rerun warmup, revalidate once
543
+ failing = failing.to(self._device)
544
+ Journal.log(self.__class__.__name__,
545
+ "_setup",
546
+ f"Warmup validation failed for {robot_name}, envs indexes {failing.tolist()}",
547
+ LogType.EXCEP,
548
+ throw_when_excep=True)
549
+ else:
550
+ Journal.log(self.__class__.__name__,
551
+ "_setup",
552
+ f"Warmup validation passed for {robot_name}",
553
+ LogType.INFO)
554
+
555
+ # write some inits for all robots
556
+ # self._update_root_offsets(robot_name)
557
+ self._synch_default_root_states(robot_name=robot_name)
558
+ epsi=0.03 # adding a bit of height to avoid initial penetration
559
+ self._root_p_default[robot_name][:, 2]=self._root_p_default[robot_name][:, 2]+epsi
560
+
561
+ reset_ok=self._reset(env_indxs=None,
562
+ robot_name=robot_name,
563
+ reset_cluster=True,
564
+ reset_cluster_counter=False,
565
+ randomize=True,
566
+ acquire_offsets=True) # resets everything, updates the cluster with fresh reset states
567
+ # and acquire offsets
568
+ if not reset_ok:
569
+ return False
570
+
571
+ # cluster setup here
572
+ control_cluster=self.cluster_servers[robot_name]
573
+
574
+ control_cluster.pre_trigger()
575
+ to_be_activated=control_cluster.get_inactive_controllers()
576
+ if to_be_activated is not None:
577
+ control_cluster.activate_controllers(
578
+ idxs=to_be_activated)
579
+
580
+ if self._env_opts["run_cluster_bootstrap"]:
581
+ cluster_setup_ok=self._setup_mpc_cluster(robot_name)
582
+ if not cluster_setup_ok:
583
+ return False
584
+ self._set_cluster_actions(robot_name=robot_name) # write last cmds
585
+ self._apply_cmds_to_jnt_imp_control(robot_name=robot_name) # apply to robot
586
+
587
+ if self._use_remote_stepping[i]:
588
+ step_wait_ok = self._wait_for_remote_step_req(robot_name=robot_name)
589
+ if not step_wait_ok:
590
+ return False
591
+
592
+ self._set_startup_jnt_imp_gains(robot_name=robot_name) # set gains to
593
+ # startup config (usually lower)
594
+
595
+ control_cluster.pre_trigger()
596
+ control_cluster.trigger_solution(bootstrap=False) # trigger first solution (in real-time iteration) before first call to step to ensure that first solution is ready when step is called the first time
597
+
598
+ if self._env_opts["add_remote_exit_flag"]:
599
+ self._remote_exit_flag=SharedTWrapper(namespace = self._robot_names[0],# use first robot as name
600
+ basename = "IbridoRemoteEnvExitFlag",
601
+ is_server = True,
602
+ n_rows = 1,
603
+ n_cols = 1,
604
+ verbose = True,
605
+ vlevel = self._vlevel,
606
+ safe = False,
607
+ dtype=dtype.Bool,
608
+ force_reconnection=True,
609
+ fill_value = False)
610
+ self._remote_exit_flag.run()
611
+
612
+ self._setup_done=True
613
+
614
+ return self._setup_done
615
+
616
+ def _setup_mpc_cluster(self, robot_name: str):
617
+
618
+ control_cluster = self.cluster_servers[robot_name]
619
+
620
+ # self._set_state_to_cluster(robot_name=robot_name)
621
+ rhc_state = control_cluster.get_state()
622
+ root_twist=rhc_state.root_state.get(data_type="twist", robot_idxs = None, gpu=self._use_gpu)
623
+ jnt_v=rhc_state.jnts_state.get(data_type="v", robot_idxs = None, gpu=self._use_gpu)
624
+ root_twist[:, :]=0 # override meas state to make sure MPC bootstrap uses zero velocity
625
+ jnt_v[:, :]=0
626
+
627
+ control_cluster.write_robot_state()
628
+
629
+ # trigger bootstrap solution (solvers will run up to convergence)
630
+ control_cluster.trigger_solution(bootstrap=True) # this will trigger the bootstrap solver with the initial state,
631
+ # which will run until convergence before returning
632
+ wait_ok=control_cluster.wait_for_solution() # blocking
633
+ if not wait_ok:
634
+ return False
635
+ failed = control_cluster.get_failed_controllers(gpu=self._use_gpu)
636
+ if failed is not None:
637
+ failed_idxs = torch.nonzero(failed).squeeze(-1)
638
+ if failed_idxs.numel() > 0:
639
+ Journal.log(self.__class__.__name__,
640
+ "_setup",
641
+ f"Bootstrap solution failed for {robot_name} | n_failed: {failed_idxs.numel()}, idxs: {failed_idxs.cpu().tolist()}",
642
+ LogType.EXCEP,
643
+ throw_when_excep=False)
644
+ return False
645
+
646
+ return True
647
+
648
+ def step(self) -> bool:
649
+
650
+ success=False
651
+
652
+ if self._remote_exit_flag is not None:
653
+ # check for exit request
654
+ self._remote_exit_flag.synch_all(read=True, retry = False)
655
+ self._exit_request=self._exit_request or \
656
+ bool(self._remote_exit_flag.get_numpy_mirror()[0, 0].item())
657
+
658
+ if self._exit_request:
659
+ self.close()
660
+
661
+ if self.is_running() and (not self.is_closed()):
662
+ if self._debug:
663
+ pre_step_ok=self._pre_step_db()
664
+ if not pre_step_ok:
665
+ return False
666
+ self._env_timer=time.perf_counter()
667
+ self._step_world()
668
+ self.debug_data["time_to_step_world"] = \
669
+ time.perf_counter() - self._env_timer
670
+ self._post_world_step_db()
671
+ success=True
672
+ else:
673
+ pre_step_ok=self._pre_step()
674
+ if not pre_step_ok:
675
+ return False
676
+ self._step_world()
677
+ self._post_world_step()
678
+ success=True
679
+
680
+ return success
681
+
682
+ def render(self, mode:str="human") -> None:
683
+ self._render_sim(mode)
684
+
685
+ def reset(self,
686
+ env_indxs: torch.Tensor = None,
687
+ reset_cluster: bool = False,
688
+ reset_cluster_counter = False,
689
+ randomize: bool = False,
690
+ reset_sim: bool = False) -> None:
691
+
692
+ for i in range(len(self._robot_names)):
693
+ robot_name=self._robot_names[i]
694
+ reset_ok=self._reset(robot_name=robot_name,
695
+ env_indxs=env_indxs,
696
+ randomize=randomize,
697
+ reset_cluster=reset_cluster,
698
+ reset_cluster_counter=reset_cluster_counter)
699
+ if not reset_ok:
700
+ return False
701
+ self._set_startup_jnt_imp_gains(robot_name=robot_name,
702
+ env_indxs=env_indxs)
703
+
704
+ if reset_sim:
705
+ self._reset_sim()
706
+
707
+ return True
708
+
709
+ def _reset_cluster(self,
710
+ robot_name: str,
711
+ env_indxs: torch.Tensor = None,
712
+ reset_cluster_counter: bool = False):
713
+
714
+ control_cluster = self.cluster_servers[robot_name]
715
+
716
+ reset_ok=control_cluster.reset_controllers(idxs=env_indxs)
717
+ if not reset_ok:
718
+ return False
719
+
720
+ self._set_state_to_cluster(robot_name=robot_name,
721
+ env_indxs=env_indxs)
722
+ control_cluster.write_robot_state() # writes to shared memory
723
+
724
+ if reset_cluster_counter:
725
+ self.cluster_sim_step_counters[robot_name] = 0
726
+
727
+ return True
728
+
729
+ def _step_jnt_vel_filter(self,
730
+ robot_name: str,
731
+ env_indxs: torch.Tensor = None):
732
+
733
+ self._jnt_vel_filter[robot_name].update(refk=self.jnts_v(robot_name=robot_name, env_idxs=env_indxs),
734
+ idxs=env_indxs)
735
+
736
+ def _set_state_to_cluster(self,
737
+ robot_name: str,
738
+ env_indxs: torch.Tensor = None,
739
+ base_loc: bool = True):
740
+
741
+ if self._debug:
742
+ if not isinstance(env_indxs, (torch.Tensor, type(None))):
743
+ msg = "Provided env_indxs should be a torch tensor of indexes!"
744
+ raise Exception(f"[{self.__class__.__name__}]" + f"[{self.journal.exception}]: " + msg)
745
+
746
+ control_cluster = self.cluster_servers[robot_name]
747
+ # floating base
748
+ rhc_state = control_cluster.get_state()
749
+ # configuration
750
+ rhc_state.root_state.set(data=self.root_p_rel(robot_name=robot_name, env_idxs=env_indxs),
751
+ data_type="p", robot_idxs = env_indxs, gpu=self._use_gpu)
752
+ rhc_state.root_state.set(data=self.root_q(robot_name=robot_name, env_idxs=env_indxs),
753
+ data_type="q", robot_idxs = env_indxs, gpu=self._use_gpu)
754
+ # rhc_state.root_state.set(data=self.root_q_yaw_rel(robot_name=robot_name, env_idxs=env_indxs),
755
+ # data_type="q", robot_idxs = env_indxs, gpu=self._use_gpu)
756
+ # twist
757
+ rhc_state.root_state.set(data=self.root_v(robot_name=robot_name, env_idxs=env_indxs,base_loc=base_loc),
758
+ data_type="v", robot_idxs = env_indxs, gpu=self._use_gpu)
759
+ rhc_state.root_state.set(data=self.root_omega(robot_name=robot_name, env_idxs=env_indxs,base_loc=base_loc),
760
+ data_type="omega", robot_idxs = env_indxs, gpu=self._use_gpu)
761
+
762
+ # angular accc.
763
+ rhc_state.root_state.set(data=self.root_a(robot_name=robot_name, env_idxs=env_indxs,base_loc=base_loc),
764
+ data_type="a", robot_idxs = env_indxs, gpu=self._use_gpu)
765
+ rhc_state.root_state.set(data=self.root_alpha(robot_name=robot_name, env_idxs=env_indxs,base_loc=base_loc),
766
+ data_type="alpha", robot_idxs = env_indxs, gpu=self._use_gpu)
767
+
768
+ # gravity vec
769
+ rhc_state.root_state.set(data=self.gravity(robot_name=robot_name, env_idxs=env_indxs,base_loc=base_loc),
770
+ data_type="gn", robot_idxs = env_indxs, gpu=self._use_gpu)
771
+
772
+ # joints
773
+ rhc_state.jnts_state.set(data=self.jnts_q(robot_name=robot_name, env_idxs=env_indxs),
774
+ data_type="q", robot_idxs = env_indxs, gpu=self._use_gpu)
775
+
776
+ v_jnts=self.jnts_v(robot_name=robot_name, env_idxs=env_indxs)
777
+ if self._jnt_vel_filter[robot_name] is not None: # apply filtering
778
+ v_jnts=self._jnt_vel_filter[robot_name].get(idxs=env_indxs)
779
+ rhc_state.jnts_state.set(data=v_jnts,
780
+ data_type="v", robot_idxs = env_indxs, gpu=self._use_gpu)
781
+ rhc_state.jnts_state.set(data=self.jnts_eff(robot_name=robot_name, env_idxs=env_indxs),
782
+ data_type="eff", robot_idxs = env_indxs, gpu=self._use_gpu)
783
+
784
+ # height map
785
+ if self._enable_height_shared:
786
+ hdata = self._height_imgs[robot_name]
787
+ if env_indxs is not None:
788
+ hdata = hdata[env_indxs]
789
+ flat = hdata.reshape(hdata.shape[0], -1)
790
+ rhc_state.height_sensor.set(data=flat, data_type=None, robot_idxs=env_indxs, gpu=self._use_gpu)
791
+
792
+ # Updating contact state for selected contact links
793
+ self._update_contact_state(robot_name=robot_name, env_indxs=env_indxs)
794
+
795
+ def _update_contact_state(self,
796
+ robot_name: str,
797
+ env_indxs: torch.Tensor = None):
798
+ for i in range(0, self.cluster_servers[robot_name].n_contact_sensors()):
799
+ contact_link = self.cluster_servers[robot_name].contact_linknames()[i]
800
+ f_contact = self._get_contact_f(robot_name=robot_name,
801
+ contact_link=contact_link,
802
+ env_indxs=env_indxs)
803
+ if f_contact is not None:
804
+ self.cluster_servers[robot_name].get_state().contact_wrenches.set(data=f_contact, data_type="f",
805
+ contact_name=contact_link,
806
+ robot_idxs = env_indxs,
807
+ gpu=self._use_gpu)
808
+
809
+ def _init_safe_cluster_actions(self,
810
+ robot_name: str):
811
+
812
+ # this does not actually write on shared memory,
813
+ # but it's enough to get safe actions for the simulator before the
814
+ # cluster starts to receive data from the controllers
815
+ control_cluster = self.cluster_servers[robot_name]
816
+ rhc_cmds = control_cluster.get_actions()
817
+ n_jnts = rhc_cmds.n_jnts()
818
+
819
+ null_action = torch.zeros((self._num_envs, n_jnts),
820
+ dtype=self._dtype,
821
+ device=self._device)
822
+ rhc_cmds.jnts_state.set(data=self._homing, data_type="q", gpu=self._use_gpu)
823
+ rhc_cmds.jnts_state.set(data=null_action, data_type="v", gpu=self._use_gpu)
824
+ rhc_cmds.jnts_state.set(data=null_action, data_type="eff", gpu=self._use_gpu)
825
+
826
+ def _pre_step_db(self) -> None:
827
+
828
+ # cluster step logic here
829
+ for i in range(len(self._robot_names)):
830
+ robot_name = self._robot_names[i]
831
+
832
+ if self._override_low_lev_controller:
833
+ # if overriding low-lev jnt imp. this has to run at the highest
834
+ # freq possible
835
+ start=time.perf_counter()
836
+ self._read_jnts_state_from_robot(robot_name=robot_name)
837
+ self.debug_data["time_to_get_states_from_env"]= time.perf_counter()-start
838
+
839
+ self._write_state_to_jnt_imp(robot_name=robot_name)
840
+ self._apply_cmds_to_jnt_imp_control(robot_name=robot_name)
841
+
842
+ if self._jnt_vel_filter[robot_name] is not None and \
843
+ (self.cluster_sim_step_counters[robot_name]+1) % self._filter_step_ssteps_freq == 0:
844
+ # filter joint vel at a fixed frequency wrt sim steps
845
+ if not self._override_low_lev_controller:
846
+ # we need a fresh sensor reading
847
+ self._read_jnts_state_from_robot(robot_name=robot_name)
848
+ self._step_jnt_vel_filter(robot_name=robot_name, env_indxs=None)
849
+
850
+ control_cluster = self.cluster_servers[robot_name]
851
+ if control_cluster.is_cluster_instant(self.cluster_sim_step_counters[robot_name]):
852
+ wait_ok=control_cluster.wait_for_solution() # this is blocking
853
+ if not wait_ok:
854
+ return False
855
+ failed = control_cluster.get_failed_controllers(gpu=self._use_gpu)
856
+ self._set_cluster_actions(robot_name=robot_name) # write last cmds to low level control
857
+ if not self._override_low_lev_controller:
858
+ self._apply_cmds_to_jnt_imp_control(robot_name=robot_name) # apply to robot
859
+ # we can update the jnt state just at the rate at which the cluster needs it
860
+ start=time.perf_counter()
861
+ self._read_jnts_state_from_robot(robot_name=robot_name, env_indxs=None)
862
+ else:
863
+ # read state necessary for cluster
864
+ start=time.perf_counter()
865
+ self._read_root_state_from_robot(robot_name=robot_name,
866
+ env_indxs=None)
867
+ self.debug_data["time_to_get_states_from_env"]= time.perf_counter()-start
868
+ start=time.perf_counter()
869
+ self._set_state_to_cluster(robot_name=robot_name,
870
+ env_indxs=None)
871
+ control_cluster.write_robot_state()
872
+ self.debug_data["cluster_state_update_dt"][robot_name] = time.perf_counter()-start
873
+
874
+ self._update_jnt_imp_cntrl_shared_data() # only if debug_mode_jnt_imp is enabled
875
+
876
+ if self._use_remote_stepping[i]:
877
+ self._remote_steppers[robot_name].ack() # signal cluster stepping is finished
878
+ if failed is not None and self._env_opts["deact_when_failure"]: # deactivate robot completely
879
+ self._deactivate(env_indxs=failed,
880
+ robot_name=robot_name)
881
+ wait_reset_ok=self._process_remote_reset_req(robot_name=robot_name) # wait for remote reset request (blocking)
882
+ wait_step_ok=self._wait_for_remote_step_req(robot_name=robot_name)
883
+ if not wait_reset_ok or not wait_step_ok:
884
+ return False
885
+ else:
886
+ if failed is not None:
887
+ reset_ok=self._reset(env_indxs=failed,
888
+ robot_name=robot_name,
889
+ reset_cluster=True,
890
+ reset_cluster_counter=False,
891
+ randomize=True)
892
+ if not reset_ok:
893
+ return False
894
+ self._set_startup_jnt_imp_gains(robot_name=robot_name,
895
+ env_indxs=failed)
896
+
897
+ control_cluster.activate_controllers(idxs=control_cluster.get_inactive_controllers())
898
+
899
+ control_cluster.pre_trigger() # performs pre-trigger steps, like retrieving
900
+ # values of some rhc flags on shared memory
901
+ control_cluster.trigger_solution() # trigger only active controllers
902
+
903
+ return True
904
+
905
+ def _pre_step(self) -> None:
906
+
907
+ # cluster step logic here
908
+ for i in range(len(self._robot_names)):
909
+ robot_name = self._robot_names[i]
910
+ if self._override_low_lev_controller:
911
+ # if overriding low-lev jnt imp. this has to run at the highest
912
+ # freq possible
913
+ self._read_jnts_state_from_robot(robot_name=robot_name)
914
+ self._write_state_to_jnt_imp(robot_name=robot_name)
915
+ self._apply_cmds_to_jnt_imp_control(robot_name=robot_name)
916
+
917
+ if self._jnt_vel_filter[robot_name] is not None and \
918
+ (self.cluster_sim_step_counters[robot_name]+1) % self._filter_step_ssteps_freq == 0:
919
+ # filter joint vel at a fixed frequency wrt sim steps
920
+ if not self._override_low_lev_controller:
921
+ # we need a fresh sensor reading
922
+ self._read_jnts_state_from_robot(robot_name=robot_name)
923
+ self._step_jnt_vel_filter(robot_name=robot_name, env_indxs=None)
924
+
925
+ control_cluster = self.cluster_servers[robot_name]
926
+ if control_cluster.is_cluster_instant(self.cluster_sim_step_counters[robot_name]):
927
+ wait_ok=control_cluster.wait_for_solution() # this is blocking
928
+ if not wait_ok:
929
+ return False
930
+ failed = control_cluster.get_failed_controllers(gpu=self._use_gpu)
931
+ self._set_cluster_actions(robot_name=robot_name) # set last cmds to low level control
932
+ if not self._override_low_lev_controller:
933
+ self._apply_cmds_to_jnt_imp_control(robot_name=robot_name) # apply to robot
934
+ # we can update the jnt state just at the rate at which the cluster needs it
935
+ self._read_jnts_state_from_robot(robot_name=robot_name, env_indxs=None)
936
+ # read state necessary for cluster
937
+ self._read_root_state_from_robot(robot_name=robot_name,
938
+ env_indxs=None)
939
+ # write last robot state to the cluster of controllers
940
+ self._set_state_to_cluster(robot_name=robot_name,
941
+ env_indxs=None)
942
+ control_cluster.write_robot_state() # write on shared mem
943
+
944
+ if self._use_remote_stepping[i]:
945
+ self._remote_steppers[robot_name].ack() # signal cluster stepping is finished
946
+ if failed is not None and self._env_opts["deact_when_failure"]:
947
+ self._deactivate(env_indxs=failed,
948
+ robot_name=robot_name)
949
+ wait_reset_ok=self._process_remote_reset_req(robot_name=robot_name) # wait for remote reset request (blocking)
950
+ wait_step_ok=self._wait_for_remote_step_req(robot_name=robot_name)
951
+ if not wait_reset_ok or not wait_step_ok:
952
+ return False
953
+ else:
954
+ if failed is not None:
955
+ reset_ok=self._reset(env_indxs=failed,
956
+ robot_name=robot_name,
957
+ reset_cluster=True,
958
+ reset_cluster_counter=False,
959
+ randomize=True)
960
+ if not reset_ok:
961
+ return False
962
+ self._set_startup_jnt_imp_gains(robot_name=robot_name,
963
+ env_indxs=failed)
964
+ control_cluster.activate_controllers(idxs=control_cluster.get_inactive_controllers())
965
+
966
+ control_cluster.pre_trigger() # performs pre-trigger steps, like retrieving
967
+ # values of some rhc flags on shared memory
968
+ control_cluster.trigger_solution() # trigger only active controllers
969
+
970
+ return True
971
+
972
+ def _post_world_step_db(self) -> bool:
973
+
974
+ for i in range(len(self._robot_names)):
975
+ robot_name = self._robot_names[i]
976
+ control_cluster = self.cluster_servers[robot_name]
977
+ self.cluster_sim_step_counters[robot_name]+=1 # this has to be update with sim freq
978
+ if self._debug:
979
+ self.debug_data["sim_time"][robot_name]=self.world_time(robot_name=robot_name)
980
+ self.debug_data["cluster_sol_time"][robot_name] = \
981
+ control_cluster.solution_time()
982
+
983
+ self.step_counter +=1
984
+
985
+ def _post_world_step(self) -> bool:
986
+
987
+ for i in range(len(self._robot_names)):
988
+ robot_name = self._robot_names[i]
989
+ self.cluster_sim_step_counters[robot_name]+=1
990
+ self.step_counter +=1
991
+
992
+ def _reset(self,
993
+ robot_name: str,
994
+ env_indxs: torch.Tensor = None,
995
+ randomize: bool = False,
996
+ reset_cluster: bool = False,
997
+ reset_cluster_counter = False,
998
+ acquire_offsets: bool = False):
999
+
1000
+ # resets the state of target robot and env to the defaults
1001
+ self._reset_state(env_indxs=env_indxs,
1002
+ robot_name=robot_name,
1003
+ randomize=randomize)
1004
+
1005
+ # and jnt imp. controllers
1006
+ self._reset_jnt_imp_control(robot_name=robot_name,
1007
+ env_indxs=env_indxs)
1008
+
1009
+ # read reset state
1010
+ self._read_root_state_from_robot(robot_name=robot_name,
1011
+ env_indxs=env_indxs)
1012
+ self._read_jnts_state_from_robot(robot_name=robot_name,
1013
+ env_indxs=env_indxs)
1014
+
1015
+ if self._jnt_vel_filter[robot_name] is not None:
1016
+ self._jnt_vel_filter[robot_name].reset(idxs=env_indxs)
1017
+
1018
+ if acquire_offsets:
1019
+ self._update_root_offsets(robot_name=robot_name,
1020
+ env_indxs=env_indxs)
1021
+
1022
+ if reset_cluster: # reset controllers remotely
1023
+ reset_ok=self._reset_cluster(env_indxs=env_indxs,
1024
+ robot_name=robot_name,
1025
+ reset_cluster_counter=reset_cluster_counter)
1026
+ if not reset_ok:
1027
+ return False
1028
+
1029
+ return True
1030
+
1031
+ def _randomize_yaw(self,
1032
+ robot_name: str,
1033
+ env_indxs: torch.Tensor = None):
1034
+
1035
+ root_q_default = self._root_q_default[robot_name]
1036
+ if env_indxs is None:
1037
+ env_indxs = torch.arange(root_q_default.shape[0])
1038
+
1039
+ num_indices = env_indxs.shape[0]
1040
+ yaw_angles = torch.rand((num_indices,),
1041
+ device=root_q_default.device) * 2 * torch.pi # uniformly distributed random angles
1042
+
1043
+ # Compute cos and sin once
1044
+ cos_half = torch.cos(yaw_angles / 2)
1045
+ root_q_default[env_indxs, :] = torch.stack((cos_half,
1046
+ torch.zeros_like(cos_half),
1047
+ torch.zeros_like(cos_half),
1048
+ torch.sin(yaw_angles / 2)), dim=1).reshape(num_indices, 4)
1049
+
1050
+ def _deactivate(self,
1051
+ robot_name: str,
1052
+ env_indxs: torch.Tensor = None):
1053
+
1054
+ # deactivate jnt imp controllers for given robots and envs (makes the robot fall)
1055
+ self._jnt_imp_controllers[robot_name].deactivate(robot_indxs=env_indxs)
1056
+
1057
+ def _n_contacts(self, robot_name: str) -> List[int]:
1058
+ return self._num_contacts[robot_name]
1059
+
1060
+ def root_p(self,
1061
+ robot_name: str,
1062
+ env_idxs: torch.Tensor = None):
1063
+
1064
+ if env_idxs is None:
1065
+ return self._root_p[robot_name]
1066
+ else:
1067
+ return self._root_p[robot_name][env_idxs, :]
1068
+
1069
+ def root_p_rel(self,
1070
+ robot_name: str,
1071
+ env_idxs: torch.Tensor = None):
1072
+
1073
+ if env_idxs is None:
1074
+ rel_pos = torch.sub(self.root_p(robot_name=robot_name),
1075
+ self._root_pos_offsets[robot_name])
1076
+ else:
1077
+ rel_pos = torch.sub(self.root_p(robot_name=robot_name,
1078
+ env_idxs=env_idxs),
1079
+ self._root_pos_offsets[robot_name][env_idxs, :])
1080
+ return rel_pos
1081
+
1082
+ def root_q(self,
1083
+ robot_name: str,
1084
+ env_idxs: torch.Tensor = None):
1085
+
1086
+ if env_idxs is None:
1087
+ return self._root_q[robot_name]
1088
+ else:
1089
+ return self._root_q[robot_name][env_idxs, :]
1090
+
1091
+ def root_q_rel(self,
1092
+ robot_name: str,
1093
+ env_idxs: torch.Tensor = None):
1094
+
1095
+ if env_idxs is None:
1096
+ return quaternion_difference(self._root_q_offsets[robot_name],
1097
+ self.root_q(robot_name=robot_name))
1098
+ rel_q = quaternion_difference(self._root_q_offsets[robot_name][env_idxs, :],
1099
+ self.root_q(robot_name=robot_name,
1100
+ env_idxs=env_idxs))
1101
+ return rel_q
1102
+
1103
+ def _quat_to_yaw_wxyz(self, q: torch.Tensor, out: torch.Tensor = None):
1104
+ # Quaternion convention is w, x, y, z.
1105
+ w, x, y, z = q[:, 0], q[:, 1], q[:, 2], q[:, 3]
1106
+ num = 2.0 * (w * z + x * y)
1107
+ den = 1.0 - 2.0 * (y * y + z * z)
1108
+ if out is None:
1109
+ return torch.atan2(num, den)
1110
+ return torch.atan2(num, den, out=out)
1111
+
1112
+ def _yaw_to_quat_wxyz(self, yaw: torch.Tensor, like_q: torch.Tensor,
1113
+ out: torch.Tensor = None):
1114
+ q = out
1115
+ if q is None:
1116
+ q = torch.zeros((yaw.shape[0], 4), dtype=like_q.dtype, device=like_q.device)
1117
+ else:
1118
+ q.zero_()
1119
+ q[:, 0] = torch.cos(yaw / 2.0)
1120
+ q[:, 3] = torch.sin(yaw / 2.0)
1121
+ return q
1122
+
1123
+ def _quat_conjugate_wxyz(self, q: torch.Tensor, out: torch.Tensor = None):
1124
+ qi = out
1125
+ if qi is None:
1126
+ qi = torch.empty_like(q)
1127
+ qi[:, :] = q
1128
+ qi[:, 1:] = -qi[:, 1:]
1129
+ return qi
1130
+
1131
+ def _quat_multiply_wxyz(self, q1: torch.Tensor, q2: torch.Tensor,
1132
+ out: torch.Tensor = None):
1133
+ q_out = out
1134
+ if q_out is None:
1135
+ q_out = torch.empty_like(q1)
1136
+ w1, x1, y1, z1 = q1[:, 0], q1[:, 1], q1[:, 2], q1[:, 3]
1137
+ w2, x2, y2, z2 = q2[:, 0], q2[:, 1], q2[:, 2], q2[:, 3]
1138
+ q_out[:, 0] = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2
1139
+ q_out[:, 1] = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
1140
+ q_out[:, 2] = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2
1141
+ q_out[:, 3] = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2
1142
+ return q_out
1143
+
1144
+ def _normalize_quat_wxyz(self, q: torch.Tensor, out: torch.Tensor = None):
1145
+ q_norm = out
1146
+ if q_norm is None:
1147
+ q_norm = torch.empty_like(q)
1148
+ q_norm[:, :] = q
1149
+ q_norm /= torch.clamp(torch.norm(q_norm, dim=1, keepdim=True), min=1e-9)
1150
+ return q_norm
1151
+
1152
+ def root_q_yaw_rel(self,
1153
+ robot_name: str,
1154
+ env_idxs: torch.Tensor = None):
1155
+
1156
+ # Return quaternion with startup yaw removed while preserving current pitch/roll.
1157
+ if env_idxs is None:
1158
+ ws = self._root_q_yaw_rel_ws[robot_name]
1159
+ q_abs = self._root_q[robot_name]
1160
+ yaw_start = self._root_q_offsets_yaw[robot_name]
1161
+
1162
+ self._normalize_quat_wxyz(q=q_abs, out=ws["q_abs_unit"])
1163
+ self._quat_to_yaw_wxyz(q=ws["q_abs_unit"], out=ws["yaw_abs"])
1164
+
1165
+ torch.sub(ws["yaw_abs"], yaw_start, out=ws["yaw_rel"])
1166
+ torch.sin(ws["yaw_rel"], out=ws["yaw_sin"])
1167
+ torch.cos(ws["yaw_rel"], out=ws["yaw_cos"])
1168
+ torch.atan2(ws["yaw_sin"], ws["yaw_cos"], out=ws["yaw_rel"])
1169
+
1170
+ # Build pure-yaw quaternions for:
1171
+ # 1) the current absolute heading and 2) the startup-relative heading.
1172
+ self._yaw_to_quat_wxyz(yaw=ws["yaw_abs"], like_q=ws["q_abs_unit"], out=ws["q_yaw_abs"])
1173
+ self._yaw_to_quat_wxyz(yaw=ws["yaw_rel"], like_q=ws["q_abs_unit"], out=ws["q_yaw_rel"])
1174
+
1175
+ # Isolate pitch/roll by removing the absolute yaw from the current orientation.
1176
+ # For unit quaternions q_pr = q_yaw_abs^{-1} * q_abs.
1177
+ self._quat_conjugate_wxyz(q=ws["q_yaw_abs"], out=ws["q_yaw_abs_conj"])
1178
+ self._quat_multiply_wxyz(q1=ws["q_yaw_abs_conj"], q2=ws["q_abs_unit"], out=ws["q_pr"])
1179
+ # Recompose orientation with relative yaw + current pitch/roll.
1180
+ self._quat_multiply_wxyz(q1=ws["q_yaw_rel"], q2=ws["q_pr"], out=ws["q_rel"])
1181
+
1182
+ return self._normalize_quat_wxyz(q=ws["q_rel"], out=ws["q_rel"])
1183
+
1184
+ q_abs = self.root_q(robot_name=robot_name, env_idxs=env_idxs)
1185
+ q_abs = self._normalize_quat_wxyz(q=q_abs, out=q_abs)
1186
+
1187
+ yaw_abs = self._quat_to_yaw_wxyz(q_abs)
1188
+ yaw_start = self._root_q_offsets_yaw[robot_name][env_idxs]
1189
+ yaw_rel = yaw_abs - yaw_start
1190
+ yaw_rel = torch.atan2(torch.sin(yaw_rel), torch.cos(yaw_rel))
1191
+
1192
+ q_yaw_abs = self._yaw_to_quat_wxyz(yaw_abs, like_q=q_abs)
1193
+ q_yaw_rel = self._yaw_to_quat_wxyz(yaw_rel, like_q=q_abs)
1194
+ q_pr = self._quat_multiply_wxyz(self._quat_conjugate_wxyz(q_yaw_abs), q_abs)
1195
+ q_rel = self._quat_multiply_wxyz(q_yaw_rel, q_pr)
1196
+
1197
+ return self._normalize_quat_wxyz(q_rel)
1198
+
1199
+ def root_v(self,
1200
+ robot_name: str,
1201
+ env_idxs: torch.Tensor = None,
1202
+ base_loc: bool = True):
1203
+
1204
+ root_v=self._root_v[robot_name]
1205
+ if base_loc:
1206
+ root_v=self._root_v_base_loc[robot_name]
1207
+ if env_idxs is None:
1208
+ return root_v
1209
+ else:
1210
+ return root_v[env_idxs, :]
1211
+
1212
+ def root_omega(self,
1213
+ robot_name: str,
1214
+ env_idxs: torch.Tensor = None,
1215
+ base_loc: bool = True):
1216
+
1217
+ root_omega=self._root_omega[robot_name]
1218
+ if base_loc:
1219
+ root_omega=self._root_omega_base_loc[robot_name]
1220
+ if env_idxs is None:
1221
+ return root_omega
1222
+ else:
1223
+ return root_omega[env_idxs, :]
1224
+
1225
+ def root_a(self,
1226
+ robot_name: str,
1227
+ env_idxs: torch.Tensor = None,
1228
+ base_loc: bool = True):
1229
+
1230
+ root_a=self._root_a[robot_name]
1231
+ if base_loc:
1232
+ root_a=self._root_a_base_loc[robot_name]
1233
+ if env_idxs is None:
1234
+ return root_a
1235
+ else:
1236
+ return root_a[env_idxs, :]
1237
+
1238
+ def root_alpha(self,
1239
+ robot_name: str,
1240
+ env_idxs: torch.Tensor = None,
1241
+ base_loc: bool = True):
1242
+
1243
+ root_alpha=self._root_alpha[robot_name]
1244
+ if base_loc:
1245
+ root_alpha=self._root_alpha_base_loc[robot_name]
1246
+ if env_idxs is None:
1247
+ return root_alpha
1248
+ else:
1249
+ return root_alpha[env_idxs, :]
1250
+
1251
+ def gravity(self,
1252
+ robot_name: str,
1253
+ env_idxs: torch.Tensor = None,
1254
+ base_loc: bool = True):
1255
+
1256
+ gravity_loc=self._gravity_normalized[robot_name]
1257
+ if base_loc:
1258
+ gravity_loc=self._gravity_normalized_base_loc[robot_name]
1259
+ if env_idxs is None:
1260
+ return gravity_loc
1261
+ else:
1262
+ return gravity_loc[env_idxs, :]
1263
+
1264
+ def jnts_q(self,
1265
+ robot_name: str,
1266
+ env_idxs: torch.Tensor = None):
1267
+
1268
+ if env_idxs is None:
1269
+ return self._jnts_q[robot_name]
1270
+ else:
1271
+ return self._jnts_q[robot_name][env_idxs, :]
1272
+
1273
+ def jnts_v(self,
1274
+ robot_name: str,
1275
+ env_idxs: torch.Tensor = None):
1276
+
1277
+ if env_idxs is None:
1278
+ return self._jnts_v[robot_name]
1279
+ else:
1280
+ return self._jnts_v[robot_name][env_idxs, :]
1281
+
1282
+ def jnts_eff(self,
1283
+ robot_name: str,
1284
+ env_idxs: torch.Tensor = None): # (measured) efforts
1285
+
1286
+ if env_idxs is None:
1287
+ return self._jnts_eff[robot_name]
1288
+ else:
1289
+ return self._jnts_eff[robot_name][env_idxs, :]
1290
+
1291
+ def _wait_for_remote_step_req(self,
1292
+ robot_name: str):
1293
+ if not self._remote_steppers[robot_name].wait(self._timeout):
1294
+ Journal.log(self.__class__.__name__,
1295
+ "_wait_for_remote_step_req",
1296
+ "Didn't receive any remote step req within timeout!",
1297
+ LogType.EXCEP,
1298
+ throw_when_excep = False)
1299
+ return False
1300
+ return True
1301
+
1302
+ def _process_remote_reset_req(self,
1303
+ robot_name: str):
1304
+
1305
+ if not self._remote_resetters[robot_name].wait(self._timeout):
1306
+ Journal.log(self.__class__.__name__,
1307
+ "_process_remote_reset_req",
1308
+ "Didn't receive any remote reset req within timeout!",
1309
+ LogType.EXCEP,
1310
+ throw_when_excep = False)
1311
+ return False
1312
+
1313
+ reset_requests = self._remote_reset_requests[robot_name]
1314
+ reset_requests.synch_all(read=True, retry=True) # read reset requests from shared mem
1315
+ to_be_reset = reset_requests.to_be_reset(gpu=self._use_gpu)
1316
+ if to_be_reset is not None:
1317
+ reset_ok=self._reset(env_indxs=to_be_reset,
1318
+ robot_name=robot_name,
1319
+ reset_cluster=True,
1320
+ reset_cluster_counter=False,
1321
+ randomize=True)
1322
+ if not reset_ok:
1323
+ return False
1324
+ self._set_startup_jnt_imp_gains(robot_name=robot_name,
1325
+ env_indxs=to_be_reset) # set gains to startup config (usually lower gains)
1326
+
1327
+ control_cluster = self.cluster_servers[robot_name]
1328
+ control_cluster.activate_controllers(idxs=to_be_reset) # activate controllers
1329
+ # (necessary if failed)
1330
+
1331
+ self._remote_resetters[robot_name].ack() # signal reset performed
1332
+
1333
+ return True
1334
+
1335
+ def _update_jnt_imp_cntrl_shared_data(self):
1336
+ if self._debug:
1337
+ for i in range(0, len(self._robot_names)):
1338
+ robot_name = self._robot_names[i]
1339
+ # updating all the jnt impedance data - > this may introduce some overhead
1340
+ imp_data = self._jnt_imp_cntrl_shared_data[robot_name].imp_data_view
1341
+ # set data
1342
+ imp_data.set(data_type="pos_err",
1343
+ data=self._jnt_imp_controllers[robot_name].pos_err(),
1344
+ gpu=self._use_gpu)
1345
+ imp_data.set(data_type="vel_err",
1346
+ data=self._jnt_imp_controllers[robot_name].vel_err(),
1347
+ gpu=self._use_gpu)
1348
+ imp_data.set(data_type="pos_gains",
1349
+ data=self._jnt_imp_controllers[robot_name].pos_gains(),
1350
+ gpu=self._use_gpu)
1351
+ imp_data.set(data_type="vel_gains",
1352
+ data=self._jnt_imp_controllers[robot_name].vel_gains(),
1353
+ gpu=self._use_gpu)
1354
+ imp_data.set(data_type="eff_ff",
1355
+ data=self._jnt_imp_controllers[robot_name].eff_ref(),
1356
+ gpu=self._use_gpu)
1357
+ imp_data.set(data_type="pos",
1358
+ data=self._jnt_imp_controllers[robot_name].pos(),
1359
+ gpu=self._use_gpu)
1360
+ imp_data.set(data_type="pos_ref",
1361
+ data=self._jnt_imp_controllers[robot_name].pos_ref(),
1362
+ gpu=self._use_gpu)
1363
+ imp_data.set(data_type="vel",
1364
+ data=self._jnt_imp_controllers[robot_name].vel(),
1365
+ gpu=self._use_gpu)
1366
+ imp_data.set(data_type="vel_ref",
1367
+ data=self._jnt_imp_controllers[robot_name].vel_ref(),
1368
+ gpu=self._use_gpu)
1369
+ imp_data.set(data_type="eff",
1370
+ data=self._jnt_imp_controllers[robot_name].eff(),
1371
+ gpu=self._use_gpu)
1372
+ imp_data.set(data_type="imp_eff",
1373
+ data=self._jnt_imp_controllers[robot_name].imp_eff(),
1374
+ gpu=self._use_gpu)
1375
+ # copy from GPU to CPU if using gpu
1376
+ if self._use_gpu:
1377
+ imp_data.synch_mirror(from_gpu=True,non_blocking=True)
1378
+ # even if it's from GPU->CPu we can use non-blocking since it's just for db
1379
+ # purposes
1380
+ # write copies to shared memory
1381
+ imp_data.synch_all(read=False, retry=False)
1382
+
1383
+ def _set_startup_jnt_imp_gains(self,
1384
+ robot_name:str,
1385
+ env_indxs: torch.Tensor = None):
1386
+
1387
+ startup_p_gains=self._jnt_imp_controllers[robot_name].startup_p_gains()
1388
+ startup_d_gains=self._jnt_imp_controllers[robot_name].startup_d_gains()
1389
+
1390
+ if env_indxs is not None:
1391
+ self._jnt_imp_controllers[robot_name].set_gains(robot_indxs=env_indxs,
1392
+ pos_gains=startup_p_gains[env_indxs, :],
1393
+ vel_gains=startup_d_gains[env_indxs, :])
1394
+ else:
1395
+ self._jnt_imp_controllers[robot_name].set_gains(robot_indxs=env_indxs,
1396
+ pos_gains=startup_p_gains[:, :],
1397
+ vel_gains=startup_d_gains[:, :])
1398
+
1399
+ def _write_state_to_jnt_imp(self,
1400
+ robot_name: str):
1401
+
1402
+ # always update ,imp. controller internal state (jnt imp control is supposed to be
1403
+ # always running)
1404
+ self._jnt_imp_controllers[robot_name].update_state(pos=self.jnts_q(robot_name=robot_name),
1405
+ vel = self.jnts_v(robot_name=robot_name),
1406
+ eff = self.jnts_eff(robot_name=robot_name))
1407
+
1408
+ def _set_cluster_actions(self,
1409
+ robot_name):
1410
+ control_cluster = self.cluster_servers[robot_name]
1411
+ actions=control_cluster.get_actions()
1412
+ active_controllers=control_cluster.get_active_controllers(gpu=self._use_gpu)
1413
+
1414
+ if active_controllers is not None:
1415
+ self._jnt_imp_controllers[robot_name].set_refs(
1416
+ pos_ref=actions.jnts_state.get(data_type="q", gpu=self._use_gpu)[active_controllers, :],
1417
+ vel_ref=actions.jnts_state.get(data_type="v", gpu=self._use_gpu)[active_controllers, :],
1418
+ eff_ref=actions.jnts_state.get(data_type="eff", gpu=self._use_gpu)[active_controllers, :],
1419
+ robot_indxs=active_controllers)
1420
+
1421
+ def _jnt_imp_reset_overrride(self, robot_name:str):
1422
+ # to be overriden
1423
+ pass
1424
+
1425
+ def _apply_cmds_to_jnt_imp_control(self, robot_name:str):
1426
+
1427
+ self._jnt_imp_controllers[robot_name].apply_cmds()
1428
+
1429
+ def _update_root_offsets(self,
1430
+ robot_name: str,
1431
+ env_indxs: torch.Tensor = None):
1432
+
1433
+ if self._debug:
1434
+ for_robots = ""
1435
+ if env_indxs is not None:
1436
+ if not isinstance(env_indxs, torch.Tensor):
1437
+ msg = "Provided env_indxs should be a torch tensor of indexes!"
1438
+ Journal.log(self.__class__.__name__,
1439
+ "update_root_offsets",
1440
+ msg,
1441
+ LogType.EXCEP,
1442
+ throw_when_excep = True)
1443
+ if self._use_gpu:
1444
+ if not env_indxs.device.type == "cuda":
1445
+ error = "Provided env_indxs should be on GPU!"
1446
+ Journal.log(self.__class__.__name__,
1447
+ "_step_jnt_imp_control",
1448
+ error,
1449
+ LogType.EXCEP,
1450
+ True)
1451
+ else:
1452
+ if not env_indxs.device.type == "cpu":
1453
+ error = "Provided env_indxs should be on CPU!"
1454
+ Journal.log(self.__class__.__name__,
1455
+ "_step_jnt_imp_control",
1456
+ error,
1457
+ LogType.EXCEP,
1458
+ True)
1459
+ for_robots = f"for robot {robot_name}, indexes: " + str(env_indxs.tolist())
1460
+ if self._verbose:
1461
+ Journal.log(self.__class__.__name__,
1462
+ "update_root_offsets",
1463
+ f"updating root offsets " + for_robots,
1464
+ LogType.STAT,
1465
+ throw_when_excep = True)
1466
+
1467
+ # only planar position used
1468
+ if env_indxs is None:
1469
+ self._root_pos_offsets[robot_name][:, 0:2] = self._root_p[robot_name][:, 0:2]
1470
+ self._normalize_quat_wxyz(q=self._root_q[robot_name], out=self._root_q_offsets[robot_name])
1471
+ self._quat_to_yaw_wxyz(q=self._root_q_offsets[robot_name],
1472
+ out=self._root_q_offsets_yaw[robot_name])
1473
+
1474
+ else:
1475
+ self._root_pos_offsets[robot_name][env_indxs, 0:2] = self._root_p[robot_name][env_indxs, 0:2]
1476
+ q_root_norm=self._normalize_quat_wxyz(self._root_q[robot_name][env_indxs, :])
1477
+ self._root_q_offsets[robot_name][env_indxs, :] = q_root_norm
1478
+ self._root_q_offsets_yaw[robot_name][env_indxs] = self._quat_to_yaw_wxyz(q=q_root_norm)
1479
+
1480
+ def _reset_jnt_imp_control(self,
1481
+ robot_name: str,
1482
+ env_indxs: torch.Tensor = None):
1483
+
1484
+ if self._debug:
1485
+ for_robots = ""
1486
+ if env_indxs is not None:
1487
+ if not isinstance(env_indxs, torch.Tensor):
1488
+ Journal.log(self.__class__.__name__,
1489
+ "reset_jnt_imp_control",
1490
+ "Provided env_indxs should be a torch tensor of indexes!",
1491
+ LogType.EXCEP,
1492
+ throw_when_excep = True)
1493
+ if self._use_gpu:
1494
+ if not env_indxs.device.type == "cuda":
1495
+ error = "Provided env_indxs should be on GPU!"
1496
+ Journal.log(self.__class__.__name__,
1497
+ "_step_jnt_imp_control",
1498
+ error,
1499
+ LogType.EXCEP,
1500
+ True)
1501
+ else:
1502
+ if not env_indxs.device.type == "cpu":
1503
+ error = "Provided env_indxs should be on CPU!"
1504
+ Journal.log(self.__class__.__name__,
1505
+ "_step_jnt_imp_control",
1506
+ error,
1507
+ LogType.EXCEP,
1508
+ True)
1509
+ for_robots = f"for robot {robot_name}, indexes: " + str(env_indxs)
1510
+
1511
+ if self._verbose:
1512
+ Journal.log(self.__class__.__name__,
1513
+ "reset_jnt_imp_control",
1514
+ f"resetting joint impedances " + for_robots,
1515
+ LogType.STAT,
1516
+ throw_when_excep = True)
1517
+
1518
+ # resets all internal data, refs to defaults
1519
+ self._jnt_imp_controllers[robot_name].reset(robot_indxs=env_indxs)
1520
+
1521
+ #restore jnt imp refs to homing
1522
+ if env_indxs is None:
1523
+ self._jnt_imp_controllers[robot_name].set_refs(pos_ref=self._homing[:, :],
1524
+ robot_indxs = None)
1525
+ else:
1526
+ self._jnt_imp_controllers[robot_name].set_refs(pos_ref=self._homing[env_indxs, :],
1527
+ robot_indxs = env_indxs)
1528
+
1529
+ # self._write_state_to_jnt_imp(robot_name=robot_name)
1530
+ # actually applies reset commands to the articulation
1531
+ self._write_state_to_jnt_imp(robot_name=robot_name)
1532
+ self._jnt_imp_reset_overrride(robot_name=robot_name)
1533
+ self._apply_cmds_to_jnt_imp_control(robot_name=robot_name)
1534
+
1535
+ def _synch_default_root_states(self,
1536
+ robot_name: str,
1537
+ env_indxs: torch.Tensor = None):
1538
+
1539
+ if self._debug:
1540
+ for_robots = ""
1541
+ if env_indxs is not None:
1542
+ if not isinstance(env_indxs, torch.Tensor):
1543
+ msg = "Provided env_indxs should be a torch tensor of indexes!"
1544
+ Journal.log(self.__class__.__name__,
1545
+ "synch_default_root_states",
1546
+ msg,
1547
+ LogType.EXCEP,
1548
+ throw_when_excep = True)
1549
+ if self._use_gpu:
1550
+ if not env_indxs.device.type == "cuda":
1551
+ error = "Provided env_indxs should be on GPU!"
1552
+ Journal.log(self.__class__.__name__,
1553
+ "_step_jnt_imp_control",
1554
+ error,
1555
+ LogType.EXCEP,
1556
+ True)
1557
+ else:
1558
+ if not env_indxs.device.type == "cpu":
1559
+ error = "Provided env_indxs should be on CPU!"
1560
+ Journal.log(self.__class__.__name__,
1561
+ "_step_jnt_imp_control",
1562
+ error,
1563
+ LogType.EXCEP,
1564
+ True)
1565
+ for_robots = f"for robot {robot_name}, indexes: " + str(env_indxs.tolist())
1566
+ if self._verbose:
1567
+ Journal.log(self.__class__.__name__,
1568
+ "synch_default_root_states",
1569
+ f"updating default root states " + for_robots,
1570
+ LogType.STAT,
1571
+ throw_when_excep = True)
1572
+
1573
+ if env_indxs is None:
1574
+ self._root_p_default[robot_name][:, :] = self._root_p[robot_name]
1575
+ self._root_q_default[robot_name][:, :] = self._root_q[robot_name]
1576
+ else:
1577
+ self._root_p_default[robot_name][env_indxs, :] = self._root_p[robot_name][env_indxs, :]
1578
+ self._root_q_default[robot_name][env_indxs, :] = self._root_q[robot_name][env_indxs, :]
1579
+
1580
+ def _generate_rob_descriptions(self,
1581
+ robot_name: str,
1582
+ urdf_path: str,
1583
+ srdf_path: str):
1584
+
1585
+ custom_xacro_args=extract_custom_xacro_args(self._env_opts)
1586
+ Journal.log(self.__class__.__name__,
1587
+ "_generate_rob_descriptions",
1588
+ "generating URDF for robot "+ f"{robot_name}, from URDF {urdf_path}...",
1589
+ LogType.STAT,
1590
+ throw_when_excep = True)
1591
+ xrdf_cmds=self._xrdf_cmds(robot_name=robot_name)
1592
+ xrdf_cmds=merge_xacro_cmds(prev_cmds=xrdf_cmds,
1593
+ new_cmds=custom_xacro_args)
1594
+ self._urdf_dump_paths[robot_name]=generate_urdf(robot_name=robot_name,
1595
+ xacro_path=urdf_path,
1596
+ dump_path=self._descr_dump_path,
1597
+ xrdf_cmds=xrdf_cmds)
1598
+ Journal.log(self.__class__.__name__,
1599
+ "_generate_rob_descriptions",
1600
+ "generating SRDF for robot "+ f"{robot_name}, from SRDF {srdf_path}...",
1601
+ LogType.STAT,
1602
+ throw_when_excep = True)
1603
+ # we also generate SRDF files, which are useful for control
1604
+ self._srdf_dump_paths[robot_name]=generate_srdf(robot_name=robot_name,
1605
+ xacro_path=srdf_path,
1606
+ dump_path=self._descr_dump_path,
1607
+ xrdf_cmds=xrdf_cmds)
1608
+
1609
+ def _xrdf_cmds(self, robot_name:str):
1610
+ urdfpath=self._robot_urdf_paths[robot_name]
1611
+ # we assume directory tree of the robot package is like
1612
+ # robot-ros-pkg/robot_urdf/urdf/robot.urdf.xacro
1613
+ parts = urdfpath.split('/')
1614
+ urdf_descr_root_path = '/'.join(parts[:-2])
1615
+ cmds = get_xrdf_cmds(urdf_descr_root_path=urdf_descr_root_path)
1616
+ return cmds
1617
+
1618
+ @abstractmethod
1619
+ def current_tstep(self) -> int:
1620
+ pass
1621
+
1622
+ @abstractmethod
1623
+ def world_time(self, robot_name: str) -> float:
1624
+ return self.cluster_sim_step_counters[robot_name]*self.physics_dt()
1625
+
1626
+ @abstractmethod
1627
+ def is_running(self) -> bool:
1628
+ pass
1629
+
1630
+ @abstractmethod
1631
+ def _get_contact_f(self,
1632
+ robot_name: str,
1633
+ contact_link: str,
1634
+ env_indxs: torch.Tensor) -> torch.Tensor:
1635
+ return None
1636
+
1637
+ @abstractmethod
1638
+ def physics_dt(self) -> float:
1639
+ pass
1640
+
1641
+ @abstractmethod
1642
+ def rendering_dt(self) -> float:
1643
+ pass
1644
+
1645
+ @abstractmethod
1646
+ def set_physics_dt(self, physics_dt:float):
1647
+ pass
1648
+
1649
+ @abstractmethod
1650
+ def set_rendering_dt(self, rendering_dt:float):
1651
+ pass
1652
+
1653
+ @abstractmethod
1654
+ def _robot_jnt_names(self, robot_name: str) -> List[str]:
1655
+ pass
1656
+
1657
+ @abstractmethod
1658
+ def _read_root_state_from_robot(self,
1659
+ robot_name: str,
1660
+ env_indxs: torch.Tensor = None):
1661
+ # IMPORTANT: Child interfaces should provide root quaternions in w, x, y, z convention.
1662
+ pass
1663
+
1664
+ @abstractmethod
1665
+ def _read_jnts_state_from_robot(self,
1666
+ robot_name: str,
1667
+ env_indxs: torch.Tensor = None):
1668
+ pass
1669
+
1670
+ @abstractmethod
1671
+ def _init_robots_state(self):
1672
+ pass
1673
+
1674
+ @abstractmethod
1675
+ def _reset_state(self,
1676
+ robot_name: str,
1677
+ env_indxs: torch.Tensor = None,
1678
+ randomize: bool = False):
1679
+ pass
1680
+
1681
+ @abstractmethod
1682
+ def _init_world(self):
1683
+ pass
1684
+
1685
+ @abstractmethod
1686
+ def _reset_sim(self) -> None:
1687
+ pass
1688
+
1689
+ @abstractmethod
1690
+ def _set_jnts_to_homing(self, robot_name: str):
1691
+ pass
1692
+
1693
+ @abstractmethod
1694
+ def _set_root_to_defconfig(self, robot_name: str):
1695
+ pass
1696
+
1697
+ @abstractmethod
1698
+ def _parse_env_opts(self):
1699
+ pass
1700
+
1701
+ @abstractmethod
1702
+ def _pre_setup(self):
1703
+ pass
1704
+
1705
+ @abstractmethod
1706
+ def _generate_jnt_imp_control(self) -> JntImpCntrlChild:
1707
+ pass
1708
+
1709
+ @abstractmethod
1710
+ def _render_sim(self, mode:str="human") -> None:
1711
+ pass
1712
+
1713
+ @abstractmethod
1714
+ def _close(self) -> None:
1715
+ pass
1716
+
1717
+ @abstractmethod
1718
+ def _step_world(self) -> None:
1719
+ pass
xbot2_basic.yaml ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ XBotInterface:
2
+ urdf_path: $PWD/centauro.urdf
3
+ srdf_path: $PWD/centauro_old.srdf
4
+ # urdf_path: $PWD/centauro_dagana_right.urdf
5
+ # srdf_path: $PWD/centauro_dagana_right.srdf
6
+
7
+ ModelInterface:
8
+ model_type: RBDL
9
+ is_model_floating_base: true
10
+
11
+ motor_pd:
12
+ "j_arm*_1": [500, 10]
13
+ "j_arm*_2": [500, 10]
14
+ "j_arm*_3": [500, 10]
15
+ "j_arm*_4": [500, 10]
16
+ "j_arm*_5": [100, 5]
17
+ "j_arm*_6": [100, 5]
18
+ "j_arm*_7": [100, 5]
19
+ "hip_yaw_*": [3000, 30]
20
+ "hip_pitch_*": [3000, 30]
21
+ "knee_pitch_*": [3000, 30]
22
+ "ankle_pitch_*": [1000, 10]
23
+ "ankle_yaw_*": [300, 10]
24
+ "neck_pitch": [10, 1]
25
+ "neck_yaw": [10, 1]
26
+ "torso_yaw": [1000, 30]
27
+ "j_wheel_*": [0, 30]
28
+ "velodyne_*": [10, 1]
29
+ "d435_*": [10, 1]
30
+ "dagana_*": [50, 1]
31
+
32
+ startup_motor_pd:
33
+ "j_arm*_1": [500, 30]
34
+ "j_arm*_2": [500, 30]
35
+ "j_arm*_3": [500, 30]
36
+ "j_arm*_4": [500, 30]
37
+ "j_arm*_5": [100, 5]
38
+ "j_arm*_6": [100, 5]
39
+ "j_arm*_7": [100, 5]
40
+ "hip_yaw_*": [800, 80]
41
+ "hip_pitch_*": [800, 80]
42
+ "knee_pitch_*": [800, 80]
43
+ "ankle_pitch_*": [800, 80]
44
+ "ankle_yaw_*": [480, 50]
45
+ "neck_pitch": [10, 1]
46
+ "neck_yaw": [10, 1]
47
+ "torso_yaw": [800, 80]
48
+ "j_wheel_*": [0, 30]
49
+ "velodyne_*": [10, 1]
50
+ "d435_*": [10, 1]
51
+ "dagana_*": [50, 1]
52
+
53
+ motor_vel:
54
+ j_wheel_*: [1]
55
+ neck_velodyne: [1]
56
+
57
+ # hal
58
+ xbotcore_device_configs:
59
+ sim: $PWD/hal/centauro_gz.yaml
60
+ dummy: $PWD/hal/centauro_dummy.yaml
61
+
62
+ # threads
63
+ xbotcore_threads:
64
+ rt_main: {sched: fifo , prio: 60, period: 0.001}
65
+ nrt_main: {sched: other, prio: 0 , period: 0.005}
66
+
67
+ # plugins
68
+ xbotcore_plugins:
69
+
70
+ homing:
71
+ thread: rt_main
72
+ type: homing
73
+
74
+ ros_io: {thread: nrt_main, type: ros_io}
75
+
76
+ ros_control: {thread: nrt_main, type: ros_control, params: {autostart: {type: bool, value: true}}}
77
+
78
+ # global parameters
79
+ xbotcore_param:
80
+ /xbot/hal/joint_safety/delta_check_dt: 0.02
81
+ /xbot/hal/joint_safety/filter_autostart: {value: true, type: bool}
82
+ # /xbot/hal/joint_safety/filter_cutoff_hz: {value: 1.0, type: double}
83
+ /xbot/hal/joint_safety/filter_safe_cutoff_hz: {value: 5.0, type: double}
84
+ /xbot/hal/joint_safety/filter_medium_cutoff_hz: {value: 15.0, type: double}
85
+ /xbot/hal/joint_safety/filter_fast_cutoff_hz: {value: 25.0, type: double}
86
+ /xbot/hal/enable_safety: {value: true, type: bool}