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