GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/** |
||
2 |
* @file |
||
3 |
* @brief Test for processing of robot actions |
||
4 |
* @copyright Copyright (c) 2019, New York University and Max Planck |
||
5 |
* Gesellschaft. |
||
6 |
*/ |
||
7 |
#include <gtest/gtest.h> |
||
8 |
#include "robot_interfaces/n_joint_robot_functions.hpp" |
||
9 |
|||
10 |
/** |
||
11 |
* @brief Fixture for the tests of process_desired_action(). |
||
12 |
*/ |
||
13 |
✗✓✓✗ ✓✗✓✗ ✓✗ |
18 |
class TestProcessDesiredAction : public ::testing::Test |
14 |
{ |
||
15 |
protected: |
||
16 |
using Functions = robot_interfaces::NJointRobotFunctions<2>; |
||
17 |
using Types = Functions::Types; |
||
18 |
using Vector = Types::Vector; |
||
19 |
|||
20 |
Types::Observation observation; |
||
21 |
double max_torque_Nm; |
||
22 |
Vector safety_kd; |
||
23 |
Vector default_position_control_kp; |
||
24 |
Vector default_position_control_kd; |
||
25 |
|||
26 |
9 |
void SetUp() override |
|
27 |
{ |
||
28 |
// set some arbitrary default values. Tests can overwrite specific |
||
29 |
// values if needed. |
||
30 |
✓✗✓✗ |
9 |
observation.position << 12.34, -0.42; |
31 |
✓✗✓✗ |
9 |
observation.velocity << .4, -.2; |
32 |
✓✗✓✗ |
9 |
observation.torque << 0.3, -0.25; |
33 |
|||
34 |
9 |
max_torque_Nm = 0.3; |
|
35 |
✓✗✓✗ |
9 |
safety_kd << 0.1, 0.1; |
36 |
|||
37 |
✓✗✓✗ |
9 |
default_position_control_kp << 3, 4; |
38 |
✓✗✓✗ |
9 |
default_position_control_kd << .3, .4; |
39 |
9 |
} |
|
40 |
}; |
||
41 |
|||
42 |
/** |
||
43 |
* @brief Test if a valid torque command is returned unchanged (no safety). |
||
44 |
*/ |
||
45 |
✗✓ | 5 |
TEST_F(TestProcessDesiredAction, valid_torque_no_safety) |
46 |
{ |
||
47 |
// disable velocity damping for this test |
||
48 |
✓✗✓✗ |
1 |
safety_kd << 0., 0.; |
49 |
|||
50 |
✓✗ | 1 |
Vector desired_torque; |
51 |
✓✗✓✗ |
1 |
desired_torque << 0.1, 0.2; |
52 |
✓✗✓✗ |
1 |
Types::Action action = Types::Action::Torque(desired_torque); |
53 |
|||
54 |
Types::Action resulting_action = |
||
55 |
Functions::process_desired_action(action, |
||
56 |
observation, |
||
57 |
max_torque_Nm, |
||
58 |
safety_kd, |
||
59 |
default_position_control_kp, |
||
60 |
✓✗ | 1 |
default_position_control_kd); |
61 |
|||
62 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✓✗ |
1 |
ASSERT_EQ(desired_torque[0], resulting_action.torque[0]); |
63 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✓✗ |
1 |
ASSERT_EQ(desired_torque[1], resulting_action.torque[1]); |
64 |
} |
||
65 |
|||
66 |
/** |
||
67 |
* @brief Test if clamping to max. torque is working. |
||
68 |
*/ |
||
69 |
✗✓ | 5 |
TEST_F(TestProcessDesiredAction, exceed_max_torque_no_safety) |
70 |
{ |
||
71 |
// disable velocity damping for this test |
||
72 |
✓✗✓✗ |
1 |
safety_kd << 0., 0.; |
73 |
|||
74 |
✓✗ | 1 |
Vector desired_torque; |
75 |
✓✗✓✗ |
1 |
desired_torque << 0.5, -1.2; |
76 |
✓✗✓✗ |
1 |
Types::Action action = Types::Action::Torque(desired_torque); |
77 |
|||
78 |
Types::Action resulting_action = |
||
79 |
Functions::process_desired_action(action, |
||
80 |
observation, |
||
81 |
max_torque_Nm, |
||
82 |
safety_kd, |
||
83 |
default_position_control_kp, |
||
84 |
✓✗ | 1 |
default_position_control_kd); |
85 |
|||
86 |
✓✗✓✗ ✗✓✗✗ ✗✗✗✗ ✗✗✓✗ |
1 |
ASSERT_EQ(max_torque_Nm, resulting_action.torque[0]); |
87 |
✓✗✓✗ ✗✓✗✗ ✗✗✗✗ ✗✗✓✗ |
1 |
ASSERT_EQ(-max_torque_Nm, resulting_action.torque[1]); |
88 |
} |
||
89 |
|||
90 |
/** |
||
91 |
* @brief Test velocity damping with low velocity |
||
92 |
*/ |
||
93 |
✗✓ | 5 |
TEST_F(TestProcessDesiredAction, velocity_damping_low_velocity) |
94 |
{ |
||
95 |
✓✗ | 1 |
Vector desired_torque; |
96 |
✓✗✓✗ |
1 |
desired_torque << 0.1, 0.2; |
97 |
✓✗✓✗ |
1 |
Types::Action action = Types::Action::Torque(desired_torque); |
98 |
|||
99 |
Types::Action resulting_action = |
||
100 |
Functions::process_desired_action(action, |
||
101 |
observation, |
||
102 |
max_torque_Nm, |
||
103 |
safety_kd, |
||
104 |
default_position_control_kp, |
||
105 |
✓✗ | 1 |
default_position_control_kd); |
106 |
|||
107 |
// joint 0 has positive velocity so expecting a reduced torque. |
||
108 |
// joint 1 has negative velocity so expecting an increased torque. |
||
109 |
// both should remain within the max. torque range. |
||
110 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✓✗ |
1 |
ASSERT_LT(resulting_action.torque[0], desired_torque[0]); |
111 |
✓✗✓✗ ✗✓✗✗ ✗✗✗✗ ✗✗✓✗ |
1 |
ASSERT_GE(resulting_action.torque[0], -max_torque_Nm); |
112 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✓✗ |
1 |
ASSERT_GT(resulting_action.torque[1], desired_torque[1]); |
113 |
✓✗✓✗ ✗✓✗✗ ✗✗✗✗ ✗✗✓✗ |
1 |
ASSERT_LE(resulting_action.torque[1], max_torque_Nm); |
114 |
} |
||
115 |
|||
116 |
/** |
||
117 |
* @brief Test velocity damping with very high velocity. |
||
118 |
* |
||
119 |
* This test ensures that torques cannot exceed the allowed max. torque due to |
||
120 |
* the velocity damping. |
||
121 |
*/ |
||
122 |
✗✓ | 5 |
TEST_F(TestProcessDesiredAction, velocity_damping_high_velocity) |
123 |
{ |
||
124 |
// set very high velocity |
||
125 |
✓✗✓✗ |
1 |
observation.velocity << 4000, -2000; |
126 |
|||
127 |
✓✗ | 1 |
Vector desired_torque; |
128 |
✓✗✓✗ |
1 |
desired_torque << 0.1, 0.2; |
129 |
✓✗✓✗ |
1 |
Types::Action action = Types::Action::Torque(desired_torque); |
130 |
|||
131 |
Types::Action resulting_action = |
||
132 |
Functions::process_desired_action(action, |
||
133 |
observation, |
||
134 |
max_torque_Nm, |
||
135 |
safety_kd, |
||
136 |
default_position_control_kp, |
||
137 |
✓✗ | 1 |
default_position_control_kd); |
138 |
|||
139 |
// joint 0 has positive velocity so expecting a reduced torque. |
||
140 |
// joint 1 has negative velocity so expecting an increased torque. |
||
141 |
// both should remain within the max. torque range. |
||
142 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✓✗ |
1 |
ASSERT_LT(resulting_action.torque[0], desired_torque[0]); |
143 |
✓✗✓✗ ✗✓✗✗ ✗✗✗✗ ✗✗✓✗ |
1 |
ASSERT_GE(resulting_action.torque[0], -max_torque_Nm); |
144 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✓✗ |
1 |
ASSERT_GT(resulting_action.torque[1], desired_torque[1]); |
145 |
✓✗✓✗ ✗✓✗✗ ✗✗✗✗ ✗✗✓✗ |
1 |
ASSERT_LE(resulting_action.torque[1], max_torque_Nm); |
146 |
} |
||
147 |
|||
148 |
/** |
||
149 |
* @brief Test basic position controller (default gains, no velocity) |
||
150 |
*/ |
||
151 |
✗✓ | 5 |
TEST_F(TestProcessDesiredAction, position_controller_basic) |
152 |
{ |
||
153 |
// disable velocity damping for this test |
||
154 |
✓✗✓✗ |
1 |
safety_kd << 0., 0.; |
155 |
|||
156 |
✓✗✓✗ |
1 |
observation.position << 0, 0; |
157 |
✓✗✓✗ |
1 |
observation.velocity << 0, 0; |
158 |
|||
159 |
✓✗ | 1 |
Vector desired_position; |
160 |
✓✗✓✗ |
1 |
desired_position << -1, 1; |
161 |
✓✗✓✗ ✓✗✓✗ |
1 |
Types::Action action = Types::Action::Position(desired_position); |
162 |
|||
163 |
Types::Action resulting_action = |
||
164 |
Functions::process_desired_action(action, |
||
165 |
observation, |
||
166 |
max_torque_Nm, |
||
167 |
safety_kd, |
||
168 |
default_position_control_kp, |
||
169 |
✓✗ | 1 |
default_position_control_kd); |
170 |
|||
171 |
// Just testing here if the torque command goes in the right direction |
||
172 |
|||
173 |
✓✗✓✗ ✗✓✗✗ ✗✗✗✗ ✗✗✓✗ |
1 |
ASSERT_LT(resulting_action.torque[0], 0.0); |
174 |
✓✗✓✗ ✗✓✗✗ ✗✗✗✗ ✗✗✓✗ |
1 |
ASSERT_GE(resulting_action.torque[0], -max_torque_Nm); |
175 |
|||
176 |
✓✗✓✗ ✗✓✗✗ ✗✗✗✗ ✗✗✓✗ |
1 |
ASSERT_GT(resulting_action.torque[1], 0.0); |
177 |
✓✗✓✗ ✗✓✗✗ ✗✗✗✗ ✗✗✓✗ |
1 |
ASSERT_LE(resulting_action.torque[1], max_torque_Nm); |
178 |
|||
179 |
// verify that position and gains are set correctly in the returned action |
||
180 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✓✗ |
1 |
ASSERT_EQ(desired_position[0], resulting_action.position[0]); |
181 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✓✗ |
1 |
ASSERT_EQ(desired_position[1], resulting_action.position[1]); |
182 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✓✗ |
1 |
ASSERT_EQ(default_position_control_kp[0], resulting_action.position_kp[0]); |
183 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✓✗ |
1 |
ASSERT_EQ(default_position_control_kp[1], resulting_action.position_kp[1]); |
184 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✓✗ |
1 |
ASSERT_EQ(default_position_control_kd[0], resulting_action.position_kd[0]); |
185 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✓✗ |
1 |
ASSERT_EQ(default_position_control_kd[1], resulting_action.position_kd[1]); |
186 |
} |
||
187 |
|||
188 |
/** |
||
189 |
* @brief Test position controller for only one joint. |
||
190 |
*/ |
||
191 |
✗✓ | 5 |
TEST_F(TestProcessDesiredAction, position_controller_one_joint_only) |
192 |
{ |
||
193 |
// disable velocity damping for this test |
||
194 |
✓✗✓✗ |
1 |
safety_kd << 0., 0.; |
195 |
|||
196 |
✓✗✓✗ |
1 |
observation.position << 0, 0; |
197 |
✓✗✓✗ |
1 |
observation.velocity << 0, 0; |
198 |
|||
199 |
✓✗ | 1 |
Vector desired_position; |
200 |
✓✗✓✗ |
1 |
desired_position << -1, std::numeric_limits<double>::quiet_NaN(); |
201 |
✓✗✓✗ ✓✗✓✗ |
1 |
Types::Action action = Types::Action::Position(desired_position); |
202 |
|||
203 |
Types::Action resulting_action = |
||
204 |
Functions::process_desired_action(action, |
||
205 |
observation, |
||
206 |
max_torque_Nm, |
||
207 |
safety_kd, |
||
208 |
default_position_control_kp, |
||
209 |
✓✗ | 1 |
default_position_control_kd); |
210 |
|||
211 |
// Just testing here if the torque command goes in the right direction |
||
212 |
✓✗✓✗ ✗✓✗✗ ✗✗✗✗ ✗✗✓✗ |
1 |
ASSERT_LT(resulting_action.torque[0], 0.0); |
213 |
✓✗✓✗ ✗✓✗✗ ✗✗✗✗ ✗✗✓✗ |
1 |
ASSERT_GE(resulting_action.torque[0], -max_torque_Nm); |
214 |
|||
215 |
// desired position for joint 1 is nan, so no torque should be generated |
||
216 |
✓✗✓✗ ✗✓✗✗ ✗✗✗✗ ✗✗✓✗ |
1 |
ASSERT_EQ(0.0, resulting_action.torque[1]); |
217 |
} |
||
218 |
|||
219 |
/** |
||
220 |
* @brief Test position control with velocity (i.e. verifying D-gain is working) |
||
221 |
*/ |
||
222 |
✗✓ | 5 |
TEST_F(TestProcessDesiredAction, position_controller_with_velocity) |
223 |
{ |
||
224 |
// disable velocity damping for this test |
||
225 |
✓✗✓✗ |
1 |
safety_kd << 0., 0.; |
226 |
|||
227 |
✓✗✓✗ |
1 |
observation.position << 0, 0; |
228 |
✓✗✓✗ |
1 |
observation.velocity << 0, 0; |
229 |
|||
230 |
✓✗ | 1 |
Vector desired_position; |
231 |
// use low position change to not saturate torques |
||
232 |
✓✗✓✗ |
1 |
desired_position << -.01, .01; |
233 |
|||
234 |
Types::Action result_action_without_velocity = |
||
235 |
Functions::process_desired_action( |
||
236 |
✓✗✓✗ ✓✗✓✗ |
2 |
Types::Action::Position(desired_position), |
237 |
observation, |
||
238 |
max_torque_Nm, |
||
239 |
safety_kd, |
||
240 |
default_position_control_kp, |
||
241 |
✓✗ | 2 |
default_position_control_kd); |
242 |
|||
243 |
✓✗✓✗ |
1 |
observation.velocity << -.01, .01; |
244 |
|||
245 |
Types::Action result_action_with_velocity = |
||
246 |
Functions::process_desired_action( |
||
247 |
✓✗✓✗ ✓✗✓✗ |
2 |
Types::Action::Position(desired_position), |
248 |
observation, |
||
249 |
max_torque_Nm, |
||
250 |
safety_kd, |
||
251 |
default_position_control_kp, |
||
252 |
✓✗ | 2 |
default_position_control_kd); |
253 |
|||
254 |
// Since velocity has same sign as position error, the resulting |
||
255 |
// (absolute) torques should be lower (since it is |
||
256 |
// torque = P * position_error - D * velocity ) |
||
257 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗ |
1 |
EXPECT_LT(result_action_without_velocity.torque[0], |
258 |
result_action_with_velocity.torque[0]); |
||
259 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗ |
1 |
EXPECT_GT(result_action_without_velocity.torque[1], |
260 |
result_action_with_velocity.torque[1]); |
||
261 |
1 |
} |
|
262 |
|||
263 |
/** |
||
264 |
* @brief Test position control with custom gains. |
||
265 |
*/ |
||
266 |
✗✓ | 5 |
TEST_F(TestProcessDesiredAction, position_controller_custom_gains) |
267 |
{ |
||
268 |
// disable velocity damping for this test |
||
269 |
✓✗✓✗ |
1 |
safety_kd << 0., 0.; |
270 |
|||
271 |
✓✗✓✗ |
1 |
observation.position << 0, 0; |
272 |
✓✗✓✗ |
1 |
observation.velocity << 0, 0; |
273 |
|||
274 |
✓✗✓✗ ✓✗ |
1 |
Vector desired_position, kp, kd; |
275 |
// use low position change to not saturate torques with lower gains |
||
276 |
✓✗✓✗ |
1 |
desired_position << -.01, .01; |
277 |
// set gains significantly higher than the default gains above |
||
278 |
✓✗✓✗ |
1 |
kp << 10, 10; |
279 |
✓✗✓✗ |
1 |
kd << 5, 5; |
280 |
|||
281 |
Types::Action resulting_action_default_gains = |
||
282 |
Functions::process_desired_action( |
||
283 |
✓✗✓✗ ✓✗✓✗ |
2 |
Types::Action::Position(desired_position), |
284 |
observation, |
||
285 |
max_torque_Nm, |
||
286 |
safety_kd, |
||
287 |
default_position_control_kp, |
||
288 |
✓✗ | 2 |
default_position_control_kd); |
289 |
|||
290 |
Types::Action resulting_action_custom_gains = |
||
291 |
Functions::process_desired_action( |
||
292 |
✓✗✓✗ ✓✗✓✗ |
2 |
Types::Action::Position(desired_position, kp, kd), |
293 |
observation, |
||
294 |
max_torque_Nm, |
||
295 |
safety_kd, |
||
296 |
default_position_control_kp, |
||
297 |
✓✗ | 2 |
default_position_control_kd); |
298 |
|||
299 |
// verify that custom gains are set in resulting action |
||
300 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ |
1 |
EXPECT_EQ(kp[0], resulting_action_custom_gains.position_kp[0]); |
301 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ |
1 |
EXPECT_EQ(kp[1], resulting_action_custom_gains.position_kp[1]); |
302 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ |
1 |
EXPECT_EQ(kd[0], resulting_action_custom_gains.position_kd[0]); |
303 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ |
1 |
EXPECT_EQ(kd[1], resulting_action_custom_gains.position_kd[1]); |
304 |
|||
305 |
// Since custom gains are higher, the resulting (absolute) torques should be |
||
306 |
// higher |
||
307 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗ |
1 |
EXPECT_GT(resulting_action_default_gains.torque[0], |
308 |
resulting_action_custom_gains.torque[0]); |
||
309 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗ |
1 |
EXPECT_LT(resulting_action_default_gains.torque[1], |
310 |
resulting_action_custom_gains.torque[1]); |
||
311 |
1 |
} |
|
312 |
|||
313 |
/** |
||
314 |
* @brief Test combined torque and position control. |
||
315 |
*/ |
||
316 |
✗✓ | 5 |
TEST_F(TestProcessDesiredAction, position_controller_and_torque) |
317 |
{ |
||
318 |
// disable velocity damping for this test |
||
319 |
✓✗✓✗ |
1 |
safety_kd << 0., 0.; |
320 |
|||
321 |
✓✗✓✗ |
1 |
observation.position << 0, 0; |
322 |
✓✗✓✗ |
1 |
observation.velocity << 0, 0; |
323 |
|||
324 |
✓✗ | 1 |
Vector desired_torque; |
325 |
✓✗✓✗ |
1 |
desired_torque << -0.2, 0.2; |
326 |
✓✗ | 1 |
Vector desired_position; |
327 |
✓✗✓✗ |
1 |
desired_position << -.1, .1; |
328 |
Types::Action action = |
||
329 |
✓✗✓✗ ✓✗✓✗ ✓✗ |
1 |
Types::Action::TorqueAndPosition(desired_torque, desired_position); |
330 |
|||
331 |
Types::Action resulting_action = |
||
332 |
Functions::process_desired_action(action, |
||
333 |
observation, |
||
334 |
max_torque_Nm, |
||
335 |
safety_kd, |
||
336 |
default_position_control_kp, |
||
337 |
✓✗ | 1 |
default_position_control_kd); |
338 |
|||
339 |
// Just testing here if the torque command goes in the right direction |
||
340 |
|||
341 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✓✗ |
1 |
ASSERT_LT(resulting_action.torque[0], desired_torque[0]); |
342 |
✓✗✓✗ ✗✓✗✗ ✗✗✗✗ ✗✗✓✗ |
1 |
ASSERT_GE(resulting_action.torque[0], -max_torque_Nm); |
343 |
|||
344 |
✓✗✓✗ ✓✗✗✓ ✗✗✗✗ ✗✗✗✗ ✓✗ |
1 |
ASSERT_GT(resulting_action.torque[1], desired_torque[1]); |
345 |
✓✗✓✗ ✗✓✗✗ ✗✗✗✗ ✗✗✓✗ |
1 |
ASSERT_LE(resulting_action.torque[1], max_torque_Nm); |
346 |
✓✗✓✗ |
3 |
} |
Generated by: GCOVR (Version 4.2) |