ObjectCollisionAvoidanceVel.cpp
Go to the documentation of this file.
2
3#include <boost/unordered/detail/implementation.hpp>
4
5#include <simox/control/dynamics/RBDLModel.h>
6
8
10{
12 const simox::control::robot::NodeSetInterface* nodeSet) :
14 {
15 controllerHierarchy.reserve(4); // number of torque types to consider
16 }
17
18 void
20 const common::control_law::arondto::ObjectCollisionAvoidanceVelConfig& c,
21 RtStatusForSafetyStrategy& rtStatus) const
22 {
23 rtStatus.objectCollNullSpace.setIdentity();
24
27
29 rtStatus.objectCollK1 = rtStatus.objectCollisionNullSpaceWeights(0);
30 rtStatus.objectCollK2 = rtStatus.objectCollisionNullSpaceWeights(1);
31 rtStatus.objectCollK3 = rtStatus.objectCollisionNullSpaceWeights(2);
32 rtStatus.objectCollK4 = rtStatus.objectCollisionNullSpaceWeights(3);
33
35
36 for (unsigned int i = rtStatus.objectCollDataIndex; i < rtStatus.activeCollPairsNum; i++)
37 {
38 auto& active = rtStatus.collDataVec[i];
39
40 /// get desired null space value based on proximity to collision
41 if (active.minDistance < c.objectCollActivatorZ1)
42 {
43 /// full locking in direction of collision
44 rtStatus.desiredNullSpace = 0.0f;
45 }
46 else if (c.objectCollActivatorZ1 <= active.minDistance &&
47 active.minDistance <= c.objectCollActivatorZ2)
48 {
49 /// in transition state
50 rtStatus.desiredNullSpace =
51 rtStatus.objectCollK1 * std::pow(active.minDistance, 3) +
52 rtStatus.objectCollK2 * std::pow(active.minDistance, 2) +
53 rtStatus.objectCollK3 * active.minDistance + rtStatus.objectCollK4;
54 if (rtStatus.desiredNullSpace > 1.0f)
55 {
56 ARMARX_WARNING << "desired null space value should not be higher than 1.0, was "
57 << rtStatus.desiredNullSpace
58 << ", in collision null space calulation, weights: "
59 << rtStatus.objectCollK1 << ", " << rtStatus.objectCollK2 << ", "
60 << rtStatus.objectCollK3 << ", " << rtStatus.objectCollK4;
61 }
62 }
63 else
64 {
65 /// unrestricted movement in direction of collision
66 rtStatus.desiredNullSpace = 1.0f;
67 }
68
69 active.desiredNSColl = rtStatus.desiredNullSpace;
70 rtStatus.desiredNullSpace = std::clamp(rtStatus.desiredNullSpace, 0.0f, 1.0f);
72 /// normalize projected Jacobian
73 rtStatus.objectCollNormalizedJacT = active.projectedJacT.normalized();
74
76
78 I - (rtStatus.objectCollNormalizedJacT * (1.0f - rtStatus.desiredNullSpace) *
79 rtStatus.objectCollNormalizedJacT.transpose());
80
81 if (c.onlyLimitCollDirection)
82 {
83 /// check, whether impedance joint torque acts against the collision direction
84 ARMARX_CHECK_EQUAL(active.projectedJacT.rows(), rtStatus.trajFollowJointVel.rows());
85 for (int i = 0; i < rtStatus.trajFollowJointVel.rows(); ++i)
86 {
87 if ((active.projectedJacT(i) < 0.0f and
88 rtStatus.trajFollowJointVel(i) < 0.0f) ||
89 (active.projectedJacT(i) > 0.0f and rtStatus.trajFollowJointVel(i) > 0.0f))
90 {
91 // if both have the same sign (both negative or both positive), do not limit DoF
92 rtStatus.objectCollTempNullSpaceMatrix(i, i) = 1.0f;
93 }
94 }
95 }
96 /// project desired null space in corresponding direction via the Norm-Jacobian
98 // todo: clamp the diagonal values between 0 and 1
99 }
100 }
101
102 void
104 const Config& c,
106 RecoveryState& rState,
107 const DistanceResults& externalCollisionPairs,
108 const CollisionRobotIndices& collisionRobotIndices,
109 const Eigen::VectorXf& qvelFiltered,
110 double deltaT) const
111 {
112
113 /// collision avoidance algorithm following the methods in Dietrich et al. (2012):
114 ///
115 /// A. Dietrich, T. Wimbock, A. Albu-Schaffer and G. Hirzinger, "Integration of Reactive,
116 /// Torque-Based Self-Collision Avoidance Into a Task Hierarchy," in IEEE Transactions on
117 /// Robotics, vol. 28, no. 6, pp. 1278-1293, Dec. 2012, doi: 10.1109/TRO.2012.2208667.
118 ///
119 /// see: https://ieeexplore.ieee.org/document/6255795
120 ///
121 /// method:
122 ///
123 /// computes the joint torques to avoid collisions -> collisionJointTorque
124
125 if (externalCollisionPairs.empty())
126 {
127 return;
128 }
129
130 /// clear values before new cycle
131 rts.objectCollisionJointVel.setZero();
132
133 if (!c.enableSelfCollisionAvoidance)
134 {
135 rts.objectCollDataIndex = 0;
136 rts.activeCollPairsNum = 0;
137 }
138
139 // clear values before new cycle, starting from object collision data index
140 for (size_t i = rts.objectCollDataIndex; i < rts.collDataVec.size(); ++i)
141 {
142 rts.collDataVec[i].clearValues();
143 }
144
145 /// when collDistanceThresholdInit is smaller than recoveryDistanceElapseMeter
146 /// (usually means it is zero), we need to initilize it to the smallest collision distance
147 /// plus a recoveryDistanceElapseMeter.
148 float& thresholdInit = rState.collDistanceThresholdInit;
149 if (c.enableCollisionRecoveryOnStartup and (thresholdInit < c.recoveryDistanceElapseMeter))
150 {
151 thresholdInit = c.objectDistanceThreshold;
152 for (const DistanceResult& collisionPair : externalCollisionPairs)
153 {
154 float minDist = static_cast<float>(collisionPair.minDistance);
155 if (minDist < thresholdInit)
156 {
157 thresholdInit = std::max(minDist, 0.0f);
158 }
159 }
160 thresholdInit =
161 std::min(c.objectDistanceThreshold, thresholdInit + c.recoveryDistanceElapseMeter);
162 }
163
164 rState.update(c, rts, deltaT);
165
166
167 /// handling of external collision pairs
168 for (const DistanceResult& collisionPair : externalCollisionPairs)
169 {
170
171 if (rts.activeCollPairsNum >= rts.collDataVec.size())
172 {
174 "CollisionAvoidanceVelController",
175 "Number of active collision pairs exceeds the allocated memory");
176 break;
177 }
178
179 if (static_cast<float>(collisionPair.minDistance) >= rState.collDistanceThreshold)
180 {
181 continue;
182 }
183
184 /// only node1 is on the robot
185 if (collisionRobotIndices.count(collisionPair.node1) == 0)
186 {
187 continue;
188 }
189
190 ARMARX_CHECK(collisionPair.hasPoints());
191
192 auto& externalCollDataVec = rts.collDataVec[rts.activeCollPairsNum++];
193
194 externalCollDataVec.minDistance = static_cast<float>(collisionPair.minDistance);
195
196 externalCollDataVec.node1 = collisionPair.node1;
197 externalCollDataVec.node2 = collisionPair.node2;
198 externalCollDataVec.point1 = collisionPair.point1->cast<float>();
199 externalCollDataVec.point2 = collisionPair.point2->cast<float>();
200 auto node1Type = collisionPair.node1Type;
201 // auto node2Type = collisionPair.node2Type;
202
203 // direction is pointing away from collision
204 externalCollDataVec.direction = externalCollDataVec.point1 - externalCollDataVec.point2;
205
206 if (externalCollDataVec.minDistance < 0.0f)
207 {
208 externalCollDataVec.minDistance = 0.0f;
209
210 // check for overlapping objects (returned points are exactly the same)
211 if (externalCollDataVec.point1.isApprox(externalCollDataVec.point2, 1e-8) and
212 collisionPair.normalVec != std::nullopt)
213 {
214 /// if the points are the same, normalVec comes with a value
215
216 // todo: how to make sure the normalVec is always pointing away from the
217 // collision, in normally the vector points out from the contact point within
218 // the sphere
219 // issue: if the arm is modeled with a sphere, it will point towards the collision
220 // No faulty behavior has been detected so far, however there is the possibility
221 // to get a direction vector pointing towards the collision
222
223 // solution: check if the node is a sphere, if the repulsive force is
224 // suppose to apply on the sphere, use the negative normal direction
225 if (node1Type == hpp::fcl::NODE_TYPE::GEOM_SPHERE)
226 {
227 externalCollDataVec.direction =
228 -1.0 * collisionPair.normalVec->cast<float>();
229 }
230 else
231 {
232 externalCollDataVec.direction = collisionPair.normalVec->cast<float>();
233 }
234 }
235 else
236 {
237 externalCollDataVec.direction *= -1.0f;
238 }
239 }
240
241 externalCollDataVec.direction.normalize();
242
244 externalCollDataVec,
245 c,
246 rts,
247 collisionRobotIndices,
248 qvelFiltered,
250 rState.collDistanceThreshold);
251 }
252 }
253
254 /// --------------------------------- main rt-loop ------------------------------------------
255
256 void
259 RecoveryState& rStateSelfColl,
260 RecoveryState& rStateObjColl,
261 const DistanceResults& collisionPairs,
262 const DistanceResults& externalCollisionPairs,
263 const CollisionRobotIndices& collisionRobotIndices,
264 DynamicsModel& dynamicsModel,
265 const Eigen::VectorXf& qpos,
266 const Eigen::VectorXf& qvelFiltered,
267 float velocityLimit,
268 double deltaT)
269 {
270 // /// run in rt thread
271
272 /// ----------------------------- inertia calculations --------------------------------------------------
273 dynamicsModel.getInertiaMatrix(qpos.cast<double>(), rtStatus.inertia);
274
275 // rtStatus.inertiaInverse = rtStatus.inertia.cast<float>().inverse();
276 // inertia is positive definite matrix
277 rtStatus.inertiaInverse = rtStatus.inertia.cast<float>().llt().solve(I);
278
279 /// ----------------------------- safety constraints --------------------------------------------------
280 if (c.enableSelfCollisionAvoidance)
281 {
282 // calculation of self-collision avoidance torque
284 c, rtStatus, rStateSelfColl, collisionPairs, collisionRobotIndices, qvelFiltered, deltaT);
285 }
286 if (c.enableObjectCollisionAvoidance)
287 {
288 // calculation of external collision avoidance torque
290 c, rtStatus, rStateObjColl, externalCollisionPairs, collisionRobotIndices, qvelFiltered, deltaT);
291 }
292 if (c.enableJointLimitAvoidance)
293 {
294 // calculation of joint limit avoidance torque
295 calculateJointLimitVel(c, rtStatus, qpos, qvelFiltered);
296 }
297
298 if (!c.samePriority)
299 {
300 if (c.enableSelfCollisionAvoidance)
301 {
302 // calculation of null space matrix for self-collision avoidance
304 }
305 if (c.enableObjectCollisionAvoidance)
306 {
307 // calculation of null space matrix for external collision avoidance
309 }
310 if (c.enableJointLimitAvoidance)
311 {
312 // calculation of null space matrix for joint limit avoidance
313 calculateJointLimitNullspace(c, rtStatus, qpos);
314 }
315 }
317
318 /// --------------------------- apply EMA low pass filter -----------------------------------------------
319 if (c.filterSafetyValues)
320 {
321 /// filter safety constraint values using EMA low pass filter
322 for (int i = 0; i < rtStatus.selfCollisionJointVel.size(); ++i)
323 {
324 rtStatus.selfCollisionVelFiltered(i) =
325 (1 - c.safetyValFilter) * rtStatus.selfCollisionVelFiltered(i) +
326 c.safetyValFilter * rtStatus.selfCollisionJointVel(i);
327 }
328 for (int i = 0; i < rtStatus.objectCollisionJointVel.size(); ++i)
329 {
330 rtStatus.objectCollisionVelFiltered(i) =
331 (1 - c.safetyValFilter) * rtStatus.objectCollisionVelFiltered(i) +
332 c.safetyValFilter * rtStatus.objectCollisionJointVel(i);
333 }
334 for (int i = 0; i < rtStatus.jointLimitJointVel.size(); ++i)
335 {
336 rtStatus.jointLimitVelFiltered(i) =
337 (1 - c.safetyValFilter) * rtStatus.jointLimitVelFiltered(i) +
338 c.safetyValFilter * rtStatus.jointLimitJointVel(i);
339 }
340
341 for (int i = 0; i < rtStatus.selfCollNullSpace.diagonalSize(); ++i)
342 {
343 // the computed values are only on the diagonal, so only these need to be filtered
344 rtStatus.selfCollNullSpaceFiltered(i, i) =
345 (1 - c.safetyValFilter) * rtStatus.selfCollNullSpaceFiltered(i, i) +
346 c.safetyValFilter * rtStatus.selfCollNullSpace(i, i);
347 }
348 for (int i = 0; i < rtStatus.objectCollNullSpace.diagonalSize(); ++i)
349 {
350 // the computed values are only on the diagonal, so only these need to be filtered
351 rtStatus.objectCollNullSpaceFiltered(i, i) =
352 (1 - c.safetyValFilter) * rtStatus.objectCollNullSpaceFiltered(i, i) +
353 c.safetyValFilter * rtStatus.objectCollNullSpace(i, i);
354 }
355 for (int i = 0; i < rtStatus.jointLimNullSpace.diagonalSize(); ++i)
356 {
357 // the computed values are only on the diagonal, so only these need to be filtered
358 rtStatus.jointLimNullSpaceFiltered(i, i) =
359 (1 - c.safetyValFilter) * rtStatus.jointLimNullSpaceFiltered(i, i) +
360 c.safetyValFilter * rtStatus.jointLimNullSpace(i, i);
361 }
362 /// assign filtered values
364 rtStatus.jointLimitJointVel = rtStatus.jointLimitVelFiltered;
367
370 }
371
373 /// ----------------------------- hierarchical control --------------------------------------------------
374 /// The following control hierarchies are considered:
375 ///
376 /// there are 4 components generating torque:
377 /// - SelfCollisionAvoidance
378 /// - ObjectCollisionAvoidance
379 /// - JointLimitAvoidance
380 /// - Impedance
381 ///
382 /// For each of the enabled components a priority is loaded from the config.
383 /// The impedance component is always enabled.
384 ///
385 /// The components are processed in descending order from high priority value to low priority value
386 ///
387 /// If two components a and b have the same priority value their torques are added and their null spaces combined:
388 /// torque_combined = torque_a + torque_b
389 /// null_space_combined = null_space_a * null_space_b
390 ///
391 /// The torques of two component groups a and b (with a representing all components with a higher priority value
392 /// than priority_b and b representing all components with a priority value of priority_b) are calculated like this:
393 /// torque_combined = torque_b + (null_space_b * torque_a)
394 ///
395 /// Iteratively all torques are combined this way.
396
397 // ARMARX_INFO << VAROUT(c.samePriority);
398 controllerHierarchy.clear();
399
400 if (c.enableSelfCollisionAvoidance)
401 {
402 controllerHierarchy.emplace_back(c.selfCollisionAvoidancePriority,
403 std::ref(rtStatus.selfCollisionJointVel),
404 std::ref(rtStatus.selfCollNullSpace));
405 }
406 if (c.enableObjectCollisionAvoidance)
407 {
408 controllerHierarchy.emplace_back(c.objectCollisionAvoidancePriority,
409 std::ref(rtStatus.objectCollisionJointVel),
410 std::ref(rtStatus.objectCollNullSpace));
411 }
412 if (c.enableJointLimitAvoidance)
413 {
414 controllerHierarchy.emplace_back(c.jointLimitAvoidancePriority,
415 std::ref(rtStatus.jointLimitJointVel),
416 std::ref(rtStatus.jointLimNullSpace));
417 }
418 controllerHierarchy.emplace_back(c.impedancePriority,
419 std::ref(rtStatus.trajFollowJointVel),
420 std::ref(rtStatus.impedanceNullSpace));
421
422 // sort inplace according to priority
423 sortHierarchy();
424
425
426 uint32_t index = 0;
427
428 rtStatus.finalTorque.setZero();
429
430 while (index < controllerHierarchy.size())
431 {
432 auto& [priority, torque, nullspace] = controllerHierarchy[index];
433 rtStatus.nullSpaceAcc = nullspace.get();
434 rtStatus.torqueAcc = torque.get();
435
436 index++;
437 while ((index < controllerHierarchy.size()) &&
438 (std::get<0>(controllerHierarchy[index]) == priority))
439 {
440 rtStatus.nullSpaceAcc *= std::get<2>(controllerHierarchy[index]).get();
441 rtStatus.torqueAcc += std::get<1>(controllerHierarchy[index]).get();
442 index++;
443 }
444 rtStatus.finalTorque =
445 (rtStatus.torqueAcc + (rtStatus.nullSpaceAcc * rtStatus.finalTorque));
446 }
447
448 rtStatus.desiredJointVel = rtStatus.finalTorque /* + rtStatus.kdImpedanceTorque*/;
449
451
452 /// ------------------------- write impedance forces to buffer -----------------------------------------
453 // this section is purely for visualization purposes in the viewer
454 // rtStatus.dirErrorImp = rtStatus.poseErrorImp.head<3>().normalized();
455 // rtStatus.totalForceImpedance = rtStatus.forceImpedance.head<3>().norm();
456 // rtStatus.projForceImpedance = rtStatus.jtpinv * rtStatus.projtrajFollowJointVel;
457 // rtStatus.projTotalForceImpedance = rtStatus.projForceImpedance.head<3>().norm();
458 // rtStatus.impForceRatio = rtStatus.projTotalForceImpedance / rtStatus.totalForceImpedance;
459 // rtStatus.impTorqueRatio =
460 // rtStatus.projtrajFollowJointVel.norm() / rtStatus.trajFollowJointVel.norm();
461
462
463 /// ----------------------------- write torque target --------------------------------------------------
464 for (int i = 0; i < rtStatus.desiredJointVel.rows(); ++i)
465 {
466 rtStatus.desiredJointVel(i) =
467 std::clamp(rtStatus.desiredJointVel(i), -velocityLimit, velocityLimit);
468 }
469 }
470
471 void
472 ObjectCollisionAvoidanceVelController::sortHierarchy()
473 {
474 for (size_t i = 1; i < controllerHierarchy.size(); i++)
475 {
476 auto element = controllerHierarchy[i];
477 const unsigned int priority = std::get<0>(element);
478
479 int j = i - 1;
480 while (j >= 0 && std::get<0>(controllerHierarchy[j]) < priority)
481 {
482 controllerHierarchy[j + 1] = controllerHierarchy[j];
483 j--;
484 }
485 controllerHierarchy[j + 1] = element;
486 }
487 }
488
489} // namespace armarx::control::common::control_law
#define ARMARX_RT_LOGF_WARN(...)
uint8_t index
constexpr T c
void update(const Config &c, RtStatusForSafetyStrategy &rtStatus, double deltaT)
internal status of the controller, containing intermediate variables, mutable targets
Eigen::MatrixXf selfCollNullSpace
intermediate null space matrices ((self-)collision and joint limit avoidance)
void calculateSelfCollisionVel(const Config &c, RtStatusForSafetyStrategy &rtStatus, RecoveryState &rState, const DistanceResults &collisionPairs, const CollisionRobotIndices &collisionRobotIndices, const Eigen::VectorXf &qvelFiltered, double deltaT) const
avoidance torque methods
void calculateJointLimitNullspace(const Config &c, RtStatusForSafetyStrategy &rtStatus, const Eigen::VectorXf &qpos) const
void calculateSelfCollisionNullspace(const Config &c, RtStatusForSafetyStrategy &rtStatus) const
caclulate null spaces
void calculateJointLimitVel(const Config &c, RtStatusForSafetyStrategy &rtStatus, const Eigen::VectorXf &qpos, const Eigen::VectorXf &qvelFiltered) const
std::unordered_map< unsigned int, const simox::control::robot::NodeInterface * > CollisionRobotIndices
CollisionAvoidanceVelController(const simox::control::robot::NodeSetInterface *nodeSet)
void calculateCollisionJointVel(CollisionData &collDataVec, const Config &c, RtStatusForSafetyStrategy &rts, const CollisionRobotIndices &collisionRobotIndices, const Eigen::VectorXf &qvelFiltered, Eigen::VectorXf &jointTorque, float distanceThreshold) const
void calculateObjectCollisionNullspace(const Config &c, RtStatusForSafetyStrategy &rtStatus) const
ObjectCollisionAvoidanceVelController(const simox::control::robot::NodeSetInterface *nodeSet)
void run(const Config &c, RtStatusForSafetyStrategy &robotStatus, RecoveryState &rStateSelfColl, RecoveryState &rStateObjColl, const DistanceResults &collisionPairs, const DistanceResults &externalCollisionPairs, const CollisionRobotIndices &collisionRobotIndices, DynamicsModel &dynamicsModel, const Eigen::VectorXf &qpos, const Eigen::VectorXf &qvelFiltered, float velocityLimit, double deltaT)
------------------------------— main rt-loop ---------------------------------------—
common::control_law::arondto::ObjectCollisionAvoidanceVelConfig Config
void calculateExternalCollisionVel(const Config &c, RtStatusForSafetyStrategy &rts, RecoveryState &rState, const DistanceResults &externalCollisionPairs, const CollisionRobotIndices &collisionRobotIndices, const Eigen::VectorXf &qvelFiltered, double deltaT) const
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_CHECK_NONNEGATIVE(number)
Check whether number is nonnegative (>= 0).
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_TRACE_LITE
Definition trace.h:98