CollisionAvoidanceVel.cpp
Go to the documentation of this file.
2
3#include <SimoxUtility/math/convert/mat4f_to_quat.h>
4#include <VirtualRobot/MathTools.h>
5#include <VirtualRobot/Nodes/RobotNode.h>
6#include <VirtualRobot/RobotNodeSet.h>
7
8#include <simox/control/dynamics/RBDLModel.h>
9#include <simox/control/robot/NodeInterface.h>
10
12
16
18
20{
21 void
23 CollisionData& collDataVec,
24 const Config& c,
26 const CollisionRobotIndices& collisionRobotIndices,
27 const Eigen::VectorXf& qvelFiltered,
28 Eigen::VectorXf& jointVel,
29 const float distanceThreshold) const
30 {
31 // calculate jacobian
32 {
33 const auto* node = collisionRobotIndices.at(collDataVec.node1);
34 auto localTransformation = Eigen::Isometry3d::Identity();
35 localTransformation.translation() =
36 node->getGlobalPose().inverse() * collDataVec.point1.cast<double>();
37 const bool result = nodeSet->computeJacobian(
38 collDataVec.jacobian,
39 *node,
40 simox::control::robot::JacobianParams{
41 .mode = simox::control::utils::CartesianSelection::Position,
42 .localTransformation = localTransformation});
44 }
45
46 float distThrSquare = distanceThreshold * distanceThreshold;
47 float factor = c.maxRepulsiveVel / distThrSquare;
48 float diff = collDataVec.minDistance - distanceThreshold;
49 /// calculate common collision pair values
50 collDataVec.repulsiveVel = factor * diff * diff;
51 collDataVec.localStiffness = -2.0 * factor * diff;
52 // minimum distance is always lower than the threshold distance
53 // (local stiffness is always positive)
54 collDataVec.repulsiveVel = std::clamp(collDataVec.repulsiveVel, 0.0f, c.maxRepulsiveVel);
57
58 ARMARX_CHECK_EQUAL(3, collDataVec.direction.rows());
59 // projected in direction of collision (1xn), is saved transposed (nx1)
60 collDataVec.projectedJacT =
61 (collDataVec.direction.transpose() * collDataVec.jacobian.cast<float>()).transpose();
62 ARMARX_CHECK_EQUAL(1, collDataVec.projectedJacT.cols());
63 ARMARX_CHECK_EQUAL(numNodes(), collDataVec.projectedJacT.rows());
64 ARMARX_CHECK_EQUAL(numNodes(), qvelFiltered.rows());
65
66 collDataVec.distanceVelocity = collDataVec.projectedJacT.transpose() * qvelFiltered;
67
70
71 collDataVec.projectedMass = 1 / (collDataVec.projectedJacT.transpose() *
72 rts.inertiaInverse * collDataVec.projectedJacT);
73
75
76 collDataVec.damping = 2 * std::sqrt(collDataVec.localStiffness) *
77 std::sqrt(collDataVec.projectedMass) * c.dampingFactor;
78
79 if (rts.inRecoveryMode)
80 {
81 collDataVec.damping *= c.recoveryDampingFactor;
82 }
83
85 const float projectedJacNorm =
86 collDataVec.projectedJacT.transpose() * collDataVec.projectedJacT;
87 float scale = 0.0f;
88 if (projectedJacNorm > 1e-3)
89 {
90 scale = 1.0f / projectedJacNorm;
91 }
92 jointVel += collDataVec.projectedJacT * scale *
93 (collDataVec.repulsiveVel - collDataVec.damping * collDataVec.distanceVelocity);
94 }
95
97 const simox::control::robot::NodeSetInterface* nodeSet) :
99 I(nodeSet ? Eigen::MatrixXf::Identity(nodeSet->getSize(), nodeSet->getSize())
100 : Eigen::MatrixXf())
101 {
104 }
105
107 {
108
109 ARMARX_INFO << "Destruction of CollisionAvoidanceVelController";
110 }
111
112 /// methods for self-collision and joint limit avoidance
113 /// avoidance torque methods
114 void
116 const Config& c,
118 RecoveryState& rState,
119 const DistanceResults& collisionPairs,
120 const CollisionRobotIndices& collisionRobotIndices,
121 const Eigen::VectorXf& qvelFiltered,
122 double deltaT) const
123 {
124 /// self-collision avoidance algorithm following the methods in Dietrich et al. (2012):
125 ///
126 /// A. Dietrich, T. Wimbock, A. Albu-Schaffer and G. Hirzinger, "Integration of Reactive,
127 /// Torque-Based Self-Collision Avoidance Into a Task Hierarchy," in IEEE Transactions on
128 /// Robotics, vol. 28, no. 6, pp. 1278-1293, Dec. 2012, doi: 10.1109/TRO.2012.2208667.
129 ///
130 /// see: https://ieeexplore.ieee.org/document/6255795
131 ///
132 /// method:
133 ///
134 /// computes the joint torques to avoid self-collisions -> collisionJointTorque
135
136 ARMARX_CHECK_GREATER(collisionPairs.size(), 0);
137
138 /// clear values before new cycle
139 rts.selfCollisionJointVel.setZero();
140 rts.activeCollPairsNum = 0;
141
142 for (auto& data : rts.collDataVec)
143 {
144 data.clearValues();
145 }
146
147 /// when collDistanceThresholdInit is smaller than recoveryDistanceElapseMeter
148 /// (usually means it is zero), we need to initilize it to the smallest collision distance
149 /// plus a recoveryDistanceElapseMeter.
150 float& thresholdInit = rState.collDistanceThresholdInit;
151 if (c.enableCollisionRecoveryOnStartup and (thresholdInit < c.recoveryDistanceElapseMeter))
152 {
153 thresholdInit = c.selfDistanceThreshold;
154 for (const DistanceResult& collisionPair : collisionPairs)
155 {
156 float minDist = static_cast<float>(collisionPair.minDistance);
157 if (minDist < thresholdInit)
158 {
159 thresholdInit = std::max(minDist, 0.0f);
160 }
161 }
162 thresholdInit =
163 std::min(c.selfDistanceThreshold, thresholdInit + c.recoveryDistanceElapseMeter);
164 }
165
166 rState.update(c, rts, deltaT);
167
168 /// check collision pairs, which have are below the prefilter distance
169 for (const DistanceResult& collisionPair : collisionPairs)
170 {
171 if (static_cast<float>(collisionPair.minDistance) >= rState.collDistanceThreshold)
172 {
173 continue;
174 }
175
176 if (collisionRobotIndices.count(collisionPair.node1) == 0 and
177 collisionRobotIndices.count(collisionPair.node2) == 0)
178 {
179 continue;
180 }
181
182 ARMARX_CHECK(collisionPair.hasPoints());
183
184 auto& selfCollDataVec = rts.collDataVec[rts.activeCollPairsNum++];
185
186 selfCollDataVec.minDistance = static_cast<float>(collisionPair.minDistance);
187
188 bool flipped = false;
189 if (collisionRobotIndices.count(collisionPair.node1) > 0)
190 {
191 // First node on nodeset
192
193 if (collisionRobotIndices.count(collisionPair.node2) > 0)
194 {
195 // Check that second node comes later (equals larger index)
196 ARMARX_CHECK_GREATER(collisionPair.node2, collisionPair.node1);
197 }
198
199 selfCollDataVec.node1 = collisionPair.node1;
200 selfCollDataVec.node2 = collisionPair.node2;
201 selfCollDataVec.point1 = collisionPair.point1->cast<float>();
202 selfCollDataVec.point2 = collisionPair.point2->cast<float>();
203 }
204 else
205 {
206 // Second node on nodeset -> flip collision pair
207
208 flipped = true;
209 selfCollDataVec.node1 = collisionPair.node2;
210 selfCollDataVec.node2 = collisionPair.node1;
211 selfCollDataVec.point1 = collisionPair.point2->cast<float>();
212 selfCollDataVec.point2 = collisionPair.point1->cast<float>();
213 }
214
215 // direction is pointing away from collision
216 selfCollDataVec.direction = selfCollDataVec.point1 - selfCollDataVec.point2;
217
218 if (selfCollDataVec.minDistance < 0.0f)
219 {
220 selfCollDataVec.minDistance = 0.0f;
221
222 // check for overlapping spheres or capsules overlapping spheres
223 // (returned points are exactly the same)
224 if (selfCollDataVec.point1.isApprox(selfCollDataVec.point2, 1e-8) and
225 collisionPair.normalVec != std::nullopt)
226 {
227 /// if the points are the same, normalVec comes with a value
228
229 // todo: how to make sure the normalVec is always pointing away from the
230 // collision, in normally the vector points out from the contact point within
231 // the sphere
232 // issue: if the arm is modeled with a sphere, it will point towards the collision
233 // No faulty behavior has been detected so far, however there is the possibility
234 // to get a direction vector pointing towards the collision
235
236 // solution: check if the node is a sphere, if the repulsive force is
237 // suppose to apply on the sphere, use the negative normal direction
238 selfCollDataVec.direction = collisionPair.normalVec->cast<float>();
239 if (flipped)
240 {
241 selfCollDataVec.direction *= -1.0f;
242 }
243 }
244 else
245 {
246 selfCollDataVec.direction *= -1.0f;
247 }
248 }
249
250 selfCollDataVec.direction.normalize();
251
252 calculateCollisionJointVel(selfCollDataVec,
253 c,
254 rts,
255 collisionRobotIndices,
256 qvelFiltered,
258 rState.collDistanceThreshold);
259 }
261 }
262
263 void
265 const Config& c,
267 const Eigen::VectorXf& qpos,
268 const Eigen::VectorXf& qvelFiltered) const
269 {
270 /// method:
271 ///
272 /// computes the joint torques to avoid joint limits -> jointLimitJointVel
273
274 rts.jointLimitJointVel.setZero();
275 const size_t n = rts.jointLimitData.size();
277
279 ARMARX_CHECK_EQUAL(numNodes(), rts.inertia.cols());
280 ARMARX_CHECK_EQUAL(numNodes(), rts.inertia.rows());
281
282 /// iterate over all joints and check the joint positions for not limiteless joints
283 for (size_t i = 0; i < n; ++i)
284 {
285 auto& joint = rts.jointLimitData[i];
286 if (joint.isLimitless)
287 {
288 continue;
289 }
290
291 float diff = 0.0f;
292 float sign = 1.0f;
293 // 1. Determine penetration depth and direction
294 if (qpos[i] < joint.qposThresholdLow)
295 {
296 diff = joint.qposThresholdLow - qpos[i];
297 sign = 1.0f;
298 }
299 else if (qpos[i] > joint.qposThresholdHigh)
300 {
301 diff = qpos[i] - joint.qposThresholdHigh;
302 sign = -1.0f;
303 }
304 else
305 {
306 continue; // Within safe limits
307 }
308
309 // 2. Pre-calculate common factors, done in the init process
310 // maxRepulsiveJointVel = 1 / (thresholdRange * thresholdRange)
311 // kRepulsive = c.maxRepulsiveJointVel * joint.invThresholdSq;
312
313 // 3. Compute Repulsive Velocity (Quadratic)
314 joint.repulsiveJointVel =
315 std::min(joint.kRepulsive * (diff * diff), c.maxRepulsiveJointVel);
316
317 // 4. Compute Damping
318 // Stiffness is the derivative of the velocity profile
319 const float localStiffness = 2.0f * joint.kRepulsive * diff;
320 const float jointInertia = rts.inertia(i, i);
321
322 // Critical Damping: 2 * sqrt(K * M) * zeta
323 const float dampingGain =
324 2.0f * std::sqrt(jointInertia * localStiffness) * c.dampingFactor;
325
326 joint.totalDamping = dampingGain * qvelFiltered[i];
327
328 // 5. Apply result
329 // For Low limit: pos_vel - damping
330 // For High limit: -(pos_vel + damping) => -pos_vel - damping
331 rts.jointLimitJointVel(i) = (sign * joint.repulsiveJointVel) - joint.totalDamping;
332 }
333 }
334
335 /// caclulate null spaces
336 void
338 const common::control_law::arondto::CollisionAvoidanceVelConfig& c,
339 RtStatusForSafetyStrategy& rtStatus) const
340 {
341 rtStatus.selfCollNullSpace.setIdentity();
342
345
347 rtStatus.selfCollK1 = rtStatus.selfCollisionNullSpaceWeights(0);
348 rtStatus.selfCollK2 = rtStatus.selfCollisionNullSpaceWeights(1);
349 rtStatus.selfCollK3 = rtStatus.selfCollisionNullSpaceWeights(2);
350 rtStatus.selfCollK4 = rtStatus.selfCollisionNullSpaceWeights(3);
351
353
354 for (unsigned int i = 0; i < rtStatus.objectCollDataIndex; i++)
355 {
356 auto& active = rtStatus.collDataVec[i];
357
358 /// get desired null space value based on proximity to collision
359 if (active.minDistance < c.selfCollActivatorZ1)
360 {
361 /// full locking in direction of collision
362 rtStatus.desiredNullSpace = 0.0f;
363 }
364 else if (c.selfCollActivatorZ1 <= active.minDistance &&
365 active.minDistance <= c.selfCollActivatorZ2)
366 {
367 /// in transition state
368 rtStatus.desiredNullSpace = rtStatus.selfCollK1 * std::pow(active.minDistance, 3) +
369 rtStatus.selfCollK2 * std::pow(active.minDistance, 2) +
370 rtStatus.selfCollK3 * active.minDistance +
371 rtStatus.selfCollK4;
372 if (rtStatus.desiredNullSpace > 1.0f)
373 {
375 "desired null space value should not be higher than 1.0");
376 // ARMARX_WARNING
377 // << "desired null space value should not be higher than 1.0, was "
378 // << rtStatus.desiredNullSpace
379 // << ", in collision null space calulation, weights: " << rtStatus.selfCollK1
380 // << ", " << rtStatus.selfCollK2 << ", " << rtStatus.selfCollK3 << ", "
381 // << rtStatus.selfCollK4;
382 }
383 }
384 else
385 {
386 /// unrestricted movement in direction of collision
387 rtStatus.desiredNullSpace = 1.0f;
388 }
389
390
391 active.desiredNSColl = rtStatus.desiredNullSpace;
392 rtStatus.desiredNullSpace = std::clamp(rtStatus.desiredNullSpace, 0.0f, 1.0f);
394 /// normalize projected Jacobian
395 rtStatus.selfCollNormalizedJacT = active.projectedJacT.normalized();
396
398
400 I - (rtStatus.selfCollNormalizedJacT * (1.0f - rtStatus.desiredNullSpace) *
401 rtStatus.selfCollNormalizedJacT.transpose());
402
403 if (c.onlyLimitCollDirection)
404 {
405 /// check, whether impedance joint torque acts against the collision direction
406 ARMARX_CHECK_EQUAL(active.projectedJacT.rows(), rtStatus.trajFollowJointVel.rows());
407 for (int i = 0; i < rtStatus.trajFollowJointVel.rows(); ++i)
408 {
409 if ((active.projectedJacT(i) < 0.0f and
410 rtStatus.trajFollowJointVel(i) < 0.0f) ||
411 (active.projectedJacT(i) > 0.0f and rtStatus.trajFollowJointVel(i) > 0.0f))
412 {
413 // if both have the same sign (both negative or both positive), do not limit DoF
414 rtStatus.selfCollTempNullSpaceMatrix(i, i) = 1.0f;
415 }
416 }
417 }
418
419 /// project desired null space in corresponding direction via the Norm-Jacobian
421 // todo: clamp the diagonal values between 0 and 1
422 }
423 }
424
425 void
428 const Eigen::VectorXf& qpos) const
429 {
430 /// check fundamentals thesis BA Jan Fenker
431 rts.jointLimNullSpace.setIdentity();
432
435
437
438 for (size_t i = 0; i < rts.jointLimitData.size(); ++i)
439 {
440 auto& joint = rts.jointLimitData[i];
441 ARMARX_CHECK_EQUAL(4, joint.jointLimitNullSpaceWeightsLow.rows());
442 ARMARX_CHECK_EQUAL(4, joint.jointLimitNullSpaceWeightsHigh.rows());
443 rts.k1Lo = joint.jointLimitNullSpaceWeightsLow(0);
444 rts.k2Lo = joint.jointLimitNullSpaceWeightsLow(1);
445 rts.k3Lo = joint.jointLimitNullSpaceWeightsLow(2);
446 rts.k4Lo = joint.jointLimitNullSpaceWeightsLow(3);
447 rts.k1Hi = joint.jointLimitNullSpaceWeightsHigh(0);
448 rts.k2Hi = joint.jointLimitNullSpaceWeightsHigh(1);
449 rts.k3Hi = joint.jointLimitNullSpaceWeightsHigh(2);
450 rts.k4Hi = joint.jointLimitNullSpaceWeightsHigh(3);
451
453
454 if (not joint.isLimitless)
455 {
456 /// get desired null space value based on proximity to joint boundaries
457 if (qpos[i] < joint.qposZ1Low)
458 {
459 /// full locking
460 rts.desiredNullSpace = 0.0f;
461 }
462 else if (joint.qposZ1Low <= qpos[i] && qpos[i] <= joint.qposZ2Low)
463 {
464 /// in transition state
465 rts.desiredNullSpace = rts.k1Lo * std::pow(qpos[i], 3) +
466 rts.k2Lo * std::pow(qpos[i], 2) + rts.k3Lo * qpos[i] +
467 rts.k4Lo;
468 if (rts.desiredNullSpace > 1.0f)
469 {
471 << "desired null space value should not be higher than 1.0, was "
472 << rts.desiredNullSpace
473 << ", approaching lower joint limit, weights: " << rts.k1Lo << ", "
474 << rts.k2Lo << ", " << rts.k3Lo << ", " << rts.k4Lo;
475 }
476 }
477 else if (joint.qposZ2High <= qpos[i] && qpos[i] <= joint.qposZ1High)
478 {
479 /// in transition state
480 rts.desiredNullSpace = rts.k1Hi * std::pow(qpos[i], 3) +
481 rts.k2Hi * std::pow(qpos[i], 2) + rts.k3Hi * qpos[i] +
482 rts.k4Hi;
483 if (rts.desiredNullSpace > 1.0f)
484 {
486 << "desired null space value should not be higher than 1.0, was "
487 << rts.desiredNullSpace
488 << ", approaching upper joint limit, weights: " << rts.k1Hi << ", "
489 << rts.k2Hi << ", " << rts.k3Hi << ", " << rts.k4Hi;
490 }
491 }
492 else if (joint.qposZ1High < qpos[i])
493 {
494 /// full locking
495 rts.desiredNullSpace = 0.0f;
496 }
497 else
498 {
499 /// unrestricted movement
500 rts.desiredNullSpace = 1.0f;
501 }
502
503
504 joint.desiredNSjointLim = rts.desiredNullSpace;
505 rts.desiredNullSpace = std::clamp(rts.desiredNullSpace, 0.0f, 1.0f);
507
508 /// set desired null space value in null space matrix
509 rts.jointLimNullSpace(i, i) = 1.0f - (1.0f - rts.desiredNullSpace);
510 }
511 }
512 }
513
514 /// --------------------------------- main rt-loop ------------------------------------------
515
516 void
519 RecoveryState& rStateSelfColl,
520 const DistanceResults& collisionPairs,
521 const CollisionRobotIndices& collisionRobotIndices,
522 DynamicsModel& dynamicsModel,
523 const Eigen::VectorXf& qpos,
524 const Eigen::VectorXf& qvelFiltered,
525 float velocityLimit,
526 double deltaT)
527 {
528 /// run in rt thread
529 /// ----------------------------- inertia calculations --------------------------------------------------
530 dynamicsModel.getInertiaMatrix(qpos.cast<double>(), rtStatus.inertia);
531
532 // rtStatus.inertiaInverse = rtStatus.inertia.cast<float>().inverse();
533 // inertia is positive definite matrix
534 rtStatus.inertiaInverse = rtStatus.inertia.cast<float>().llt().solve(I);
535
536 /// ----------------------------- safety constraints --------------------------------------------------
537 if (c.enableSelfCollisionAvoidance)
538 {
539 // calculation of self-collision avoidance torque
541 c,
542 rtStatus,
543 rStateSelfColl,
544 collisionPairs,
545 collisionRobotIndices,
546 qvelFiltered,
547 deltaT);
548 }
549 if (c.enableJointLimitAvoidance)
550 {
551 // calculation of joint limit avoidance torque
552 calculateJointLimitVel(c, rtStatus, qpos, qvelFiltered);
553 }
554 if (!c.samePriority)
555 {
556 if (c.enableSelfCollisionAvoidance)
557 {
558 // calculation of null space matrix for self-collision avoidance
560 }
561 if (c.enableJointLimitAvoidance)
562 {
563 // calculation of null space matrix for joint limit avoidance
564 calculateJointLimitNullspace(c, rtStatus, qpos);
565 }
566 }
568
569 /// --------------------------- apply EMA low pass filter -----------------------------------------------
570 if (c.filterSafetyValues)
571 {
572 /// filter safety constraint values using EMA low pass filter
573 for (int i = 0; i < rtStatus.selfCollisionJointVel.size(); ++i)
574 {
575 rtStatus.selfCollisionVelFiltered(i) =
576 (1 - c.safetyValFilter) * rtStatus.selfCollisionVelFiltered(i) +
577 c.safetyValFilter * rtStatus.selfCollisionJointVel(i);
578 }
579 for (int i = 0; i < rtStatus.jointLimitJointVel.size(); ++i)
580 {
581 rtStatus.jointLimitVelFiltered(i) =
582 (1 - c.safetyValFilter) * rtStatus.jointLimitVelFiltered(i) +
583 c.safetyValFilter * rtStatus.jointLimitJointVel(i);
584 }
585
586 for (int i = 0; i < rtStatus.selfCollNullSpace.diagonalSize(); ++i)
587 {
588 // the computed values are only on the diagonal, so only these need to be filtered
589 rtStatus.selfCollNullSpaceFiltered(i, i) =
590 (1 - c.safetyValFilter) * rtStatus.selfCollNullSpaceFiltered(i, i) +
591 c.safetyValFilter * rtStatus.selfCollNullSpace(i, i);
592 }
593 for (int i = 0; i < rtStatus.jointLimNullSpace.diagonalSize(); ++i)
594 {
595 // the computed values are only on the diagonal, so only these need to be filtered
596 rtStatus.jointLimNullSpaceFiltered(i, i) =
597 (1 - c.safetyValFilter) * rtStatus.jointLimNullSpaceFiltered(i, i) +
598 c.safetyValFilter * rtStatus.jointLimNullSpace(i, i);
599 }
600 /// assign filtered values
602 rtStatus.jointLimitJointVel = rtStatus.jointLimitVelFiltered;
605 }
606
608 /// ----------------------------- hierarchical control --------------------------------------------------
609 /// The following control hierarchies are considered:
610 ///
611 /// three tasks in hierarchy: "enableSelfCollisionAvoidance": true, "enableJointLimitAvoidance": true
612 ///
613 /// flag: "samePriority": true
614 /// same priority: torque_coll + torque_jl + torque_imp
615 ///
616 /// flag: "topPrioritySelfCollisionAvoidance": true, "samePriority": false
617 /// self-coll avoidance on top: torque_coll -> torque_jl -> torque_imp
618 ///
619 /// flag: "topPriorityJointLimitAvoidance": true, "samePriority": false
620 /// joint limit avoidance on top: torque_jl -> torque_coll -> torque_imp
621 ///
622 /// flag: "topPrioritySelfCollisionAvoidance": false, "topPriorityJointLimitAvoidance": false,
623 /// "samePriority": false
624 /// (torque_coll + torque_jl) -> torque_imp
625 /// self-coll and joint limit avoidance on same priority level:
626 ///
627 /// two tasks in hierarchy:
628 ///
629 /// "enableSelfCollisionAvoidance": true, "enableJointLimitAvoidance": false
630 /// self-coll avoidance on top: torque_coll -> torque_imp
631 ///
632 /// "enableSelfCollisionAvoidance": false, "enableJointLimitAvoidance": true
633 /// joint limit avoidance on top: torque_jl -> torque_imp
634 ///
635 /// one task in hierarchy (impedance control):
636 ///
637 /// "enableSelfCollisionAvoidance": false, "enableJointLimitAvoidance": false
638 /// only impedance control: torque_imp
639
640 if (c.samePriority)
641 {
645 rtStatus.desiredJointVel = rtStatus.selfCollisionJointVel +
646 rtStatus.jointLimitJointVel +
647 rtStatus.trajFollowJointVel /* +
648 rtStatus.kdImpedanceTorque*/
649 ;
651 }
652 else if (c.enableSelfCollisionAvoidance and c.enableJointLimitAvoidance)
653 {
654 /// three tasks in hierarchy considered
655
656 if (c.topPrioritySelfCollisionAvoidance)
657 {
658
662 rtStatus.selfCollNullSpace.cols());
666 rtStatus.selfCollNullSpace.cols());
667
668 rtStatus.desiredJointVel =
669 rtStatus.selfCollisionJointVel +
670 rtStatus.selfCollNullSpace *
671 (rtStatus.jointLimitJointVel +
672 rtStatus.jointLimNullSpace * rtStatus.trajFollowJointVel) /* +
673 rtStatus.kdImpedanceTorque*/
674 ;
675
676 // // debug values
677 // rtStatus.projJointLimJointTorque =
678 // rtStatus.selfCollNullSpace * rtStatus.jointLimitJointVel;
679 // rtStatus.projtrajFollowJointVel =
680 // rtStatus.selfCollNullSpace *
681 // (rtStatus.jointLimNullSpace * rtStatus.trajFollowJointVel);
682 }
683 else if (c.topPriorityJointLimitAvoidance)
684 {
688 rtStatus.selfCollNullSpace.cols());
692 rtStatus.jointLimNullSpace.cols());
693
694 rtStatus.desiredJointVel =
695 rtStatus.jointLimitJointVel +
696 rtStatus.jointLimNullSpace *
697 (rtStatus.selfCollisionJointVel +
698 rtStatus.selfCollNullSpace * rtStatus.trajFollowJointVel) /* +
699 rtStatus.kdImpedanceTorque*/
700 ;
701
702 // // debug values
703 // rtStatus.projCollJointTorque =
704 // rtStatus.jointLimNullSpace * rtStatus.selfCollisionJointVel;
705 // rtStatus.projtrajFollowJointVel =
706 // rtStatus.jointLimNullSpace *
707 // (rtStatus.selfCollNullSpace * rtStatus.trajFollowJointVel);
708 }
709 else
710 {
714 rtStatus.selfCollNullSpace.cols());
718 rtStatus.jointLimNullSpace.cols());
719 rtStatus.desiredJointVel =
720 rtStatus.jointLimitJointVel + rtStatus.selfCollisionJointVel +
721 (rtStatus.jointLimNullSpace * rtStatus.selfCollNullSpace) *
722 rtStatus.trajFollowJointVel /* +
723 rtStatus.kdImpedanceTorque*/
724 ;
725
726 // // debug values
727 // rtStatus.projtrajFollowJointVel =
728 // (rtStatus.jointLimNullSpace * rtStatus.selfCollNullSpace) *
729 // rtStatus.trajFollowJointVel;
730 }
731 }
732 else if (c.enableSelfCollisionAvoidance)
733 {
734 /// impedance control and self-collision avoidance
735
739 rtStatus.selfCollNullSpace.cols());
740
741 rtStatus.desiredJointVel = rtStatus.selfCollisionJointVel +
742 rtStatus.selfCollNullSpace * rtStatus.trajFollowJointVel /* +
743 rtStatus.kdImpedanceTorque*/
744 ;
745
746 // // debugging
747 // rtStatus.projtrajFollowJointVel =
748 // rtStatus.selfCollNullSpace * rtStatus.trajFollowJointVel;
749 }
750 else if (c.enableJointLimitAvoidance)
751 {
752 /// impedance control and joint limit avoidance
753
757 rtStatus.jointLimNullSpace.cols());
758
759 rtStatus.desiredJointVel = rtStatus.jointLimitJointVel +
760 rtStatus.jointLimNullSpace * rtStatus.trajFollowJointVel /* +
761 rtStatus.kdImpedanceTorque*/
762 ;
763
764 // // debugging
765 // rtStatus.projtrajFollowJointVel =
766 // rtStatus.jointLimNullSpace * rtStatus.trajFollowJointVel;
767 }
768 else
769 {
770 /// plain impedance control
771 rtStatus.desiredJointVel =
772 rtStatus.trajFollowJointVel /* + rtStatus.kdImpedanceTorque*/;
773 }
774
775 // rtStatus.desiredJointVel += rtStatus.jointDampingTorque;
776
778
779 /// ------------------------- write impedance forces to buffer -----------------------------------------
780 // this section is purely for visualization purposes in the viewer
781 // rtStatus.dirErrorImp = rtStatus.poseErrorImp.head<3>().normalized();
782 // rtStatus.totalForceImpedance = rtStatus.forceImpedance.head<3>().norm();
783 // rtStatus.projForceImpedance = rtStatus.jtpinv * rtStatus.projtrajFollowJointVel;
784 // rtStatus.projTotalForceImpedance = rtStatus.projForceImpedance.head<3>().norm();
785 // rtStatus.impForceRatio = rtStatus.projTotalForceImpedance / rtStatus.totalForceImpedance;
786 // rtStatus.impTorqueRatio =
787 // rtStatus.projtrajFollowJointVel.norm() / rtStatus.trajFollowJointVel.norm();
788
789
790 /// ----------------------------- write torque target --------------------------------------------------
791 for (int i = 0; i < rtStatus.desiredJointVel.rows(); ++i)
792 {
793 rtStatus.desiredJointVel(i) =
794 std::clamp(rtStatus.desiredJointVel(i), -velocityLimit, velocityLimit);
795 }
796 }
797
798 void
800 const Config& c,
801 const unsigned int nDoF,
802 const unsigned int maxCollisionPairs,
803 VirtualRobot::RobotNodeSetPtr& rns)
804 {
805 trackingError = 0.0;
806 dirErrorImp.setZero();
808 projForceImpedance.setZero();
810
811 /// intermediate torque results
812 trajFollowJointVel.setZero(nDoF);
813 // kdImpedanceTorque.setZero(nDoF);
814 selfCollisionJointVel.setZero(nDoF);
815 objectCollisionJointVel.setZero(nDoF);
816 jointLimitJointVel.setZero(nDoF);
817
818 selfCollisionVelFiltered.setZero(nDoF);
819 objectCollisionVelFiltered.setZero(nDoF);
820 jointLimitVelFiltered.setZero(nDoF);
821
822 /// intermediate projected torques via null space matrices
823 // projtrajFollowJointVel.setZero(nDoF);
824 // projCollJointTorque.setZero(nDoF);
825 // projJointLimJointTorque.setZero(nDoF);
826
827 impForceRatio = 0.0f;
828 impTorqueRatio = 0.0f;
829
830 /// intermediate null space matrices (collision and joint limit avoidance)
831 selfCollNullSpace.resize(nDoF, nDoF);
832 selfCollNullSpace.setZero();
833 jointLimNullSpace.resize(nDoF, nDoF);
834 jointLimNullSpace.setZero();
835 objectCollNullSpace.resize(nDoF, nDoF);
836 objectCollNullSpace.setZero();
837 impedanceNullSpace.resize(nDoF, nDoF);
838 impedanceNullSpace.setIdentity();
839 desiredNullSpace = 0.0;
840
841 selfCollNullSpaceFiltered.resize(nDoF, nDoF);
843 objectCollNullSpaceFiltered.resize(nDoF, nDoF);
845 jointLimNullSpaceFiltered.resize(nDoF, nDoF);
847
848 /// self-collision avoidance initialization parameters
849 /// calculate weights for self-collision nullspace transition function
850 /// range between z1 and z2
851 /// z1: below this distance [m] the collision direction is fully locked
852 /// z2: above this distance collision direction is unrestricted
853 // collisionNullSpaceWeights.setZero();
855 helper::calculateTransitionWeights(c.selfCollActivatorZ1, c.selfCollActivatorZ2);
856
857 /// distance results
858 Eigen::Vector3d point1(0.0, 0.0, 0.0);
859 Eigen::Vector3d point2(0.0, 0.0, 0.0);
860 Eigen::Vector3d norm(0.0, 0.0, 0.0);
861
862 collDataVec.resize(maxCollisionPairs, CollisionAvoidanceVelController::CollisionData(nDoF));
863
864 /// self-collision avoidance null space intermediate results
865 selfCollK1 = 0.0;
866 selfCollK2 = 0.0;
867 selfCollK3 = 0.0;
868 selfCollK4 = 0.0;
869 selfCollNormalizedJacT.setZero(nDoF);
870 selfCollTempNullSpaceMatrix.resize(nDoF, nDoF);
872
873 /// object-collision avoidance null space intermediate results
874 objectCollK1 = 0.0;
875 objectCollK2 = 0.0;
876 objectCollK3 = 0.0;
877 objectCollK4 = 0.0;
878 objectCollNormalizedJacT.setZero(nDoF);
879 objectCollTempNullSpaceMatrix.resize(nDoF, nDoF);
881
882 /// joint limit avoidance initialization parameters
883 CollisionAvoidanceVelController::RtStatusForSafetyStrategy::initJointLimtDataVec(
884 c, nDoF, rns);
885
886 /// others
887 inertia.resize(nDoF, nDoF);
888 inertia.setZero();
889 inertiaInverse.resize(nDoF, nDoF);
890 inertiaInverse.setZero();
891
892 /// time status
893 collisionPairTime = 0.0;
894 preFilterTime = 0.0;
899
900 /// collision pair info
903
904 /// priority accumulators
905 nullSpaceAcc.resize(nDoF, nDoF);
906 nullSpaceAcc.setIdentity();
907 torqueAcc.resize(nDoF);
908 torqueAcc.setZero();
909 finalTorque.resize(nDoF);
910 finalTorque.setZero();
911 }
912
913 void
915 {
916 timeSinceStart = 0.0;
917 inRecoveryMode = c.enableCollisionRecoveryOnStartup;
918 }
919
920 void
921 CollisionAvoidanceVelController::RtStatusForSafetyStrategy::initJointLimtDataVec(
922 const Config& c,
923 const unsigned int nDoF,
924 VirtualRobot::RobotNodeSetPtr& rns)
925 {
926 jointVel = 0.0;
927 jointInertia = 0.0;
928
929 localStiffnessJointLim = 0.0;
930 dampingJointLim = 0.0;
931
932 k1Lo = 0.0;
933 k2Lo = 0.0;
934 k3Lo = 0.0;
935 k4Lo = 0.0;
936 k1Hi = 0.0;
937 k2Hi = 0.0;
938 k3Hi = 0.0;
939 k4Hi = 0.0;
940
941 jointLimitData.resize(nDoF);
942
943 /// obtain joint information (limits, limitless) from VirtualRobot
944 for (size_t i = 0; i < nDoF; ++i)
945 {
946 jointLimitData[i].jointName = rns->getNode(i)->getName();
947 jointLimitData[i].isLimitless = rns->getNode(i)->isLimitless();
948
949 if (not jointLimitData[i].isLimitless)
950 {
951 jointLimitData[i].qLimLow = rns->getNode(i)->getJointLimitLow();
952 jointLimitData[i].qLimHigh = rns->getNode(i)->getJointLimitHigh();
953 }
954 }
955
956 // std::vector<std::string> jointInfo;
957 for (auto& joint : jointLimitData)
958 {
959 if (not joint.isLimitless)
960 {
961 /// set joint limit avoidance threshold parameters for not limiteless joints
962 float qRange = joint.qLimHigh - joint.qLimLow;
963 joint.thresholdRange = c.jointRangeBufferZone * qRange;
964 joint.invThresholdSq = 1.0f / (joint.thresholdRange * joint.thresholdRange);
965 joint.kRepulsive = c.maxRepulsiveJointVel * joint.invThresholdSq;
966 joint.qposThresholdLow = joint.qLimLow + joint.thresholdRange;
967 joint.qposThresholdHigh = joint.qLimHigh - joint.thresholdRange;
968 float qposZ1 = c.jointRangeBufferZoneZ1 * qRange;
969 float qposZ2 = c.jointRangeBufferZoneZ2 * qRange;
970 joint.qposZ1Low = joint.qLimLow + qposZ1;
971 joint.qposZ2Low = joint.qLimLow + qposZ2;
972 joint.qposZ1High = joint.qLimHigh - qposZ1;
973 joint.qposZ2High = joint.qLimHigh - qposZ2;
974 joint.jointLimitNullSpaceWeightsLow =
975 helper::calculateTransitionWeights(joint.qposZ1Low, joint.qposZ2Low);
976 joint.jointLimitNullSpaceWeightsHigh =
977 helper::calculateTransitionWeights(joint.qposZ1High, joint.qposZ2High);
978
979 /// from here: only the parameter output information is prepared
980 // std::vector<float> params = {joint.qLimLow,
981 // joint.qLimHigh,
982 // joint.qposThresholdLow,
983 // joint.qposThresholdHigh,
984 // joint.qposZ1Low,
985 // joint.qposZ2Low,
986 // joint.qposZ1High,
987 // joint.qposZ2High,
988 // joint.jointLimitNullSpaceWeightsLow(0),
989 // joint.jointLimitNullSpaceWeightsLow(1),
990 // joint.jointLimitNullSpaceWeightsLow(2),
991 // joint.jointLimitNullSpaceWeightsLow(3),
992 // joint.jointLimitNullSpaceWeightsHigh(0),
993 // joint.jointLimitNullSpaceWeightsHigh(1),
994 // joint.jointLimitNullSpaceWeightsHigh(2),
995 // joint.jointLimitNullSpaceWeightsHigh(3)};
996 // std::vector<std::string> stringParams;
997 // // convert float values to string rounded to two digits
998 // for (auto& param : params)
999 // {
1000 // std::stringstream ss;
1001 // ss << std::fixed << std::setprecision(2) << param;
1002 // stringParams.push_back(ss.str());
1003 // }
1004 // std::string info = joint.jointName + ": LimLo: " + stringParams[0] +
1005 // " - LimHi: " + stringParams[1] + " - q0L: " + stringParams[2] +
1006 // " - q0H: " + stringParams[3] + " - z1L: " + stringParams[4] +
1007 // " z2L: " + stringParams[5] + " z1H: " + stringParams[6] +
1008 // " z2H: " + stringParams[7] + " k1L: " + stringParams[8] +
1009 // " k2L: " + stringParams[9] + " k3L: " + stringParams[10] +
1010 // " k4L: " + stringParams[11] + " k1H: " + stringParams[12] +
1011 // " k2H: " + stringParams[13] + " k3H: " + stringParams[14] +
1012 // " k4H: " + stringParams[15];
1013 // jointInfo.push_back(info);
1014 }
1015 }
1016
1017 // std::ostringstream oss;
1018 // for (const auto& str : jointInfo)
1019 // {
1020 // oss << str << "\n";
1021 // }
1022 // std::string initMessage = oss.str();
1023 // ARMARX_INFO << "joint limit avoidance parameters initialized for: \n" << initMessage;
1024 }
1025
1026 void
1027 collision::validateConfigData(const arondto::CollisionAvoidanceVelConfig& cfg)
1028 {
1029 /// collision config parameters
1030 ARMARX_CHECK_GREATER(cfg.dampingFactor, 0.0); // damping can't be exactly zero
1031 ARMARX_CHECK_LESS_EQUAL(cfg.dampingFactor, 1.0); // for scaling of the damping
1032 ARMARX_CHECK_GREATER(cfg.maxRepulsiveVel, 0.0);
1033 ARMARX_CHECK_NONNEGATIVE(cfg.maxRepulsiveVel);
1034 ARMARX_CHECK_NONNEGATIVE(cfg.selfDistanceThreshold);
1035 ARMARX_CHECK_NONNEGATIVE(cfg.selfCollActivatorZ1);
1036 ARMARX_CHECK_NONNEGATIVE(cfg.selfCollActivatorZ2);
1037 ARMARX_CHECK_GREATER(cfg.selfCollActivatorZ2, cfg.selfCollActivatorZ1);
1038
1039 /// joint limit avoidance config parameters
1040 ARMARX_CHECK_NONNEGATIVE(cfg.maxRepulsiveJointVel);
1041 ARMARX_CHECK_NONNEGATIVE(cfg.jointRangeBufferZone);
1042 ARMARX_CHECK_LESS_EQUAL(cfg.jointRangeBufferZone, 1.0); // percentage value
1043 ARMARX_CHECK_NONNEGATIVE(cfg.jointRangeBufferZoneZ1);
1044 ARMARX_CHECK_LESS_EQUAL(cfg.jointRangeBufferZoneZ1, 1.0); // percentage value
1045 ARMARX_CHECK_NONNEGATIVE(cfg.jointRangeBufferZoneZ2);
1046 ARMARX_CHECK_GREATER(cfg.jointRangeBufferZoneZ2, cfg.jointRangeBufferZoneZ1);
1047 ARMARX_CHECK_LESS_EQUAL(cfg.jointRangeBufferZoneZ2, 1.0); // percentage value
1048 ARMARX_CHECK_NONNEGATIVE(cfg.safetyValFilter);
1049 ARMARX_CHECK_LESS_EQUAL(cfg.safetyValFilter, 1.0);
1050
1051 /// check hierarchy flags for valid config
1052 if (cfg.topPrioritySelfCollisionAvoidance)
1053 {
1054 ARMARX_CHECK_EXPRESSION(cfg.enableSelfCollisionAvoidance);
1056 (cfg.topPrioritySelfCollisionAvoidance == !cfg.topPriorityJointLimitAvoidance));
1057 }
1058 if (cfg.topPriorityJointLimitAvoidance)
1059 {
1060 ARMARX_CHECK_EXPRESSION(cfg.enableJointLimitAvoidance);
1062 (cfg.topPrioritySelfCollisionAvoidance == !cfg.topPriorityJointLimitAvoidance));
1063 }
1064 }
1065
1066 void
1068 RtStatusForSafetyStrategy& rtStatus,
1069 double deltaT)
1070 {
1071 if (not c.enableCollisionRecoveryOnStartup or not rtStatus.inRecoveryMode)
1072 {
1073 collDistanceThreshold = c.selfDistanceThreshold;
1074 return;
1075 }
1076
1077 /// 1. determine current time since start
1078 rtStatus.timeSinceStart += deltaT;
1079 if (rtStatus.timeSinceStart > c.recoveryDurationSec)
1080 rtStatus.inRecoveryMode = false;
1081
1082 /// 2. update threshold
1084 std::min(c.selfDistanceThreshold,
1086 (c.selfDistanceThreshold - collDistanceThresholdInit) *
1087 static_cast<float>(rtStatus.timeSinceStart) / c.recoveryDurationSec);
1088 }
1089
1090 void
1096
1097} // namespace armarx::control::common::control_law
#define ARMARX_RT_LOGF_WARNING(...)
constexpr T c
void update(const Config &c, RtStatusForSafetyStrategy &rtStatus, double deltaT)
internal status of the controller, containing intermediate variables, mutable targets
float impTorqueRatio
intermediate projected torques via null space matrices ===== to remove the following
void reset(const Config &c, unsigned int nDoF, unsigned int maxCollisionPairs, VirtualRobot::RobotNodeSetPtr &rns)
Eigen::MatrixXf selfCollNullSpace
intermediate null space matrices ((self-)collision and joint limit avoidance)
std::vector< JointRangeBufferZoneData > jointLimitData
joint limit avoidance initialization parameters
void run(const Config &c, RtStatusForSafetyStrategy &robotStatus, RecoveryState &rStateSelfColl, const DistanceResults &collisionPairs, const CollisionRobotIndices &collisionRobotIndices, DynamicsModel &dynamicsModel, const Eigen::VectorXf &qpos, const Eigen::VectorXf &qvelFiltered, float velocityLimit, double deltaT)
------------------------------— main rt-loop ---------------------------------------—
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
common::control_law::arondto::CollisionAvoidanceVelConfig Config
#define ARMARX_CHECK_GREATER(lhs, rhs)
This macro evaluates whether lhs is greater (>) than rhs and if it turns out to be false it will thro...
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_CHECK_LESS_EQUAL(lhs, rhs)
This macro evaluates whether lhs is less or equal (<=) rhs and if it turns out to be false it will th...
#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_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
void validateConfigData(const arondto::CollisionAvoidanceConfig &cfg)
Eigen::Vector4f calculateTransitionWeights(float z1, float z2)
helper functions
Definition utils.cpp:25
T sign(T t)
Definition algorithm.h:214
double norm(const Point &a)
Definition point.hpp:102
unsigned int node1
the distances and projected jacobians from the generateSelfCollisionTorque() method are needed for th...
#define ARMARX_TRACE_LITE
Definition trace.h:98