TaskspaceController.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cmath>
5#include <map>
6#include <set>
7#include <string>
8
9#include <Eigen/Core>
10
11#include <VirtualRobot/IK/DifferentialIK.h>
12#include <VirtualRobot/IK/JacobiProvider.h>
13#include <VirtualRobot/MathTools.h>
14#include <VirtualRobot/Nodes/RobotNode.h>
15#include <VirtualRobot/Robot.h>
16#include <VirtualRobot/RobotNodeSet.h>
17
19
21
23{
24 template <typename ConfigType, typename ConfigDictType>
25 void
30
31 template <typename ConfigType, typename ConfigDictType>
32 bool
34 const std::vector<std::string>& nameList,
35 const std::map<std::string, std::string>& jointControlModeMap)
36 {
37 std::set<std::string> validModes = {"velocity", "torque"};
38
39 for (const auto& pair : jointControlModeMap)
40 {
41 if (std::find(nameList.begin(), nameList.end(), pair.first) == nameList.end())
42 {
43 ARMARX_ERROR << "TaskspaceController: joint name '" << pair.first
44 << "' not found in robot node set.";
45 return false;
46 }
47
48 if (validModes.find(pair.second) == validModes.end())
49 {
50 ARMARX_ERROR << "TaskspaceController: Invalid control mode for joint '"
51 << pair.first << "': " << pair.second << std::endl;
52 return false;
53 }
54 }
55
56 return true;
57 }
58
59 template <typename ConfigType, typename ConfigDictType>
60 void
62 TSCtrlRtStatus& rtStatus)
63 {
64 /// run in rt thread
65 rtStatus.currentPose = rtTCP->getPoseInRootFrame();
67 /// safety guard by FT and target pose
68 rtStatus.poseDiffMatImp.noalias() = c.desiredPose.template block<3, 3>(0, 0) *
69 rtStatus.currentPose.block<3, 3>(0, 0).transpose();
70 rtStatus.oriDiffAngleAxis.fromRotationMatrix(rtStatus.poseDiffMatImp);
72 /// this should not be moved to non rt, just in case non rt thread dies, rt still functions safely
73 rtStatus.forceTorqueSafe = ftsensor.isSafe(c.ftConfig);
74 if (not rtStatus.forceTorqueSafe)
75 {
76 if (c.ftConfig.resetToCurrentPoseOnSafeGuardTrigger)
77 {
78 c.desiredPose = rtStatus.currentPose;
79 }
80 else
81 {
82 c.desiredPose = rtStatus.desiredPose;
83 }
84 }
85
86 // if ((c.desiredPose.block<3, 1>(0, 3) - rtStatus.currentPose.block<3, 1>(0, 3)).norm() >
87 if ((rtStatus.desiredPose.block<3, 1>(0, 3) - rtStatus.currentPose.block<3, 1>(0, 3))
88 .norm() > c.safeDistanceMMToGoal or
89 std::fminf(rtStatus.oriDiffAngleAxis.angle(),
90 2 * M_PI - rtStatus.oriDiffAngleAxis.angle()) >
91 VirtualRobot::MathTools::deg2rad(c.safeRotAngleDegreeToGoal))
92 {
93 rtStatus.desiredPoseUnsafe = c.desiredPose;
94 c.desiredPose = rtStatus.desiredPose;
95 rtStatus.rtTargetSafe = false;
96 }
97 else
98 {
99 rtStatus.rtTargetSafe = true;
100 }
101
102 rtStatus.rtSafe = rtStatus.forceTorqueSafe and rtStatus.rtTargetSafe;
103 if (rtStatus.rtSafe)
104 {
105 rtStatus.desiredPose = c.desiredPose;
106 }
107 }
108
109 template <typename ConfigType, typename ConfigDictType>
110 void
112 TSCtrlRtStatus& rtStatus)
113 {
114 auto nDoF = static_cast<Eigen::Index>(rtStatus.nDoF);
115 /// ------------------------------ compute jacobi ------------------------------------------
116 // rtStatus.jacobi =
117 // ik->getJacobianMatrix(rtTCP, VirtualRobot::IKSolver::CartesianSelection::All);
118 ik->updateJacobianMatrix(
119 rtStatus.jacobi, rtTCP, VirtualRobot::IKSolver::CartesianSelection::All);
120 ARMARX_CHECK_EQUAL(nDoF, rtStatus.jacobi.cols());
121 ARMARX_CHECK_EQUAL(6, rtStatus.jacobi.rows());
122
123
124 rtStatus.jacobi.block(0, 0, 3, rtStatus.nDoF) =
125 0.001 * rtStatus.jacobi.block(0, 0, 3, rtStatus.nDoF);
126
127 // rtStatus.jpinv = ik->computePseudoInverseJacobianMatrix(rtStatus.jacobi, lambda);
128 ik->updatePseudoInverseJacobianMatrix(
129 rtStatus.jpinv, rtStatus.jacobi, 0.f, JacPseudoInvRegularization);
130 ARMARX_CHECK_EQUAL(6, rtStatus.jpinv.cols());
131 ARMARX_CHECK_EQUAL(nDoF, rtStatus.jpinv.rows());
132
133 // rtStatus.jtpinv =
134 // ik->computePseudoInverseJacobianMatrix(rtStatus.jacobi.transpose(), rtStatus.lambda);
135 rtStatus.jtpinv = ik->computePseudoInverseJacobianMatrix(rtStatus.jacobi.transpose(),
136 JacPseudoInvRegularization);
137 ARMARX_CHECK_EQUAL(nDoF, rtStatus.jtpinv.cols());
138 ARMARX_CHECK_EQUAL(6, rtStatus.jtpinv.rows());
139 }
140
141 template <typename ConfigType, typename ConfigDictType>
142 void
144 TSCtrlRtStatus& rtStatus)
145 {
146 auto nDoF = static_cast<Eigen::Index>(rtStatus.nDoF);
147 /// ------------------------------ update velocity/twist -----------------------------------
148 ARMARX_CHECK_EQUAL(nDoF, rtStatus.jointPosition.rows());
149 ARMARX_CHECK_EQUAL(nDoF, rtStatus.jointVelocity.rows());
150 ARMARX_CHECK_EQUAL(nDoF, rtStatus.qvelFiltered.rows());
151
152 rtStatus.qvelFiltered =
153 (1 - c.qvelFilter) * rtStatus.qvelFiltered + c.qvelFilter * rtStatus.jointVelocity;
154 rtStatus.currentTwist = rtStatus.jacobi * rtStatus.qvelFiltered; /// in m/s
155 }
156
157 template <typename ConfigType, typename ConfigDictType>
158 void
160 TSCtrlRtStatus& rtStatus)
161 {
162 /// ------------------------------------- TS Error --------------------------------
163 /// calculate pose error between target pose and current pose
164 /// !!! This is very important: you have to keep position and orientation both
165 /// with UI unit (meter, radian) to calculate impedance force.
166
167 rtStatus.poseDiffMatImp = rtStatus.desiredPose.block<3, 3>(0, 0) *
168 rtStatus.currentPose.block<3, 3>(0, 0).transpose();
169 /// position error in meter
170 rtStatus.poseErrorImp.head<3>() = 0.001 * (rtStatus.desiredPose.block<3, 1>(0, 3) -
171 rtStatus.currentPose.block<3, 1>(0, 3));
172 // rtStatus.poseErrorImp.head<3>() = (rtStatus.desiredPose.block<3, 1>(0, 3) -
173 // rtStatus.currentPose.block<3, 1>(0, 3)); /// in mm
174 rtStatus.poseErrorImp.tail<3>() =
175 VirtualRobot::MathTools::eigen3f2rpy(rtStatus.poseDiffMatImp);
176 }
177
178 template <typename ConfigType, typename ConfigDictType>
179 void
181 TSCtrlRtStatus& rtStatus)
182 {
183 /// --------------------------- Velocity control -------------------------------------------
184 /// Unit in m/s and rad/s
185 rtStatus.cartesianVelTarget = c.kpCartesianVel.cwiseProduct(rtStatus.poseErrorImp) -
186 c.kdCartesianVel.cwiseProduct(rtStatus.currentTwist);
187 }
188
189 template <typename ConfigType, typename ConfigDictType>
190 void
192 TSCtrlRtStatus& rtStatus)
193 {
194 // rtStatus.poseErrorImp.head<3>() *= 0.001;
195 /// Unit in N, and Nm
196 rtStatus.forceImpedance = c.kpImpedance.cwiseProduct(rtStatus.poseErrorImp) -
197 c.kdImpedance.cwiseProduct(rtStatus.currentTwist);
198 }
199
200 template <typename ConfigType, typename ConfigDictType>
201 void
203 ConfigType& c,
204 TSCtrlRtStatus& rtStatus)
205 {
206 auto nDoF = static_cast<Eigen::Index>(rtStatus.nDoF);
207 ARMARX_CHECK_EQUAL(nDoF, c.kpNullspaceTorque.rows());
208 ARMARX_CHECK_EQUAL(nDoF, c.kdNullspaceTorque.rows());
209 ARMARX_CHECK_EQUAL(nDoF, rtStatus.nullspaceTorque.rows());
210 ARMARX_CHECK_EQUAL(nDoF, c.desiredNullspaceJointAngles.value().size());
211 /// Nullspace PD Control
212 rtStatus.nullspaceTorque =
213 c.kpNullspaceTorque.cwiseProduct(c.desiredNullspaceJointAngles.value() -
214 rtStatus.jointPosition) -
215 c.kdNullspaceTorque.cwiseProduct(rtStatus.qvelFiltered);
216 }
217
218 template <typename ConfigType, typename ConfigDictType>
219 void
221 ConfigType& c,
222 TSCtrlRtStatus& rtStatus)
223 {
224 auto nDoF = static_cast<Eigen::Index>(rtStatus.nDoF);
225 ARMARX_CHECK_EQUAL(nDoF, c.kpNullspaceVel.rows());
226 ARMARX_CHECK_EQUAL(nDoF, c.kdNullspaceVel.rows());
227 ARMARX_CHECK_EQUAL(nDoF, rtStatus.nullspaceVelocity.rows());
228 ARMARX_CHECK_EQUAL(nDoF, c.desiredNullspaceJointAngles.value().size());
229 /// Nullspace PD Control
230 rtStatus.nullspaceVelocity =
231 c.kpNullspaceVel.cwiseProduct(c.desiredNullspaceJointAngles.value() -
232 rtStatus.jointPosition) -
233 c.kdNullspaceVel.cwiseProduct(rtStatus.qvelFiltered);
234 }
235
236 template <typename ConfigType, typename ConfigDictType>
237 void
239 TSCtrlRtStatus& rtStatus)
240 {
241 auto nDoF = static_cast<Eigen::Index>(rtStatus.nDoF);
242 /// Map TS target force to JS
243 ARMARX_CHECK_EQUAL(nDoF, rtStatus.desiredJointTorque.rows());
244 rtStatus.desiredJointTorque =
245 rtStatus.jacobi.transpose() * rtStatus.forceImpedance +
246 (rtStatus.I - rtStatus.jacobi.transpose() * rtStatus.jtpinv) * rtStatus.nullspaceTorque;
247 /// write torque target
248 for (int i = 0; i < rtStatus.desiredJointTorque.rows(); ++i)
249 {
250 rtStatus.desiredJointTorque(i) =
251 std::clamp(rtStatus.desiredJointTorque(i), -c.torqueLimit, c.torqueLimit);
252 }
253 }
254
255 template <typename ConfigType, typename ConfigDictType>
256 void
258 ConfigType& c,
259 TSCtrlRtStatus& rtStatus)
260 {
261 auto nDoF = static_cast<Eigen::Index>(rtStatus.nDoF);
262 /// Map TS target velocity to JS
263 ARMARX_CHECK_EQUAL(nDoF, rtStatus.desiredJointVelocity.rows());
264 rtStatus.desiredJointVelocity =
265 rtStatus.jpinv * rtStatus.cartesianVelTarget +
266 (rtStatus.I - rtStatus.jpinv * rtStatus.jacobi) * rtStatus.nullspaceVelocity;
267
268 for (int i = 0; i < rtStatus.desiredJointVelocity.rows(); ++i)
269 {
270 rtStatus.desiredJointVelocity(i) =
271 std::clamp(rtStatus.desiredJointVelocity(i), -c.velocityLimit, c.velocityLimit);
272 }
273 }
274
275 template <typename ConfigType, typename ConfigDictType>
276 void
278 const bool torqueGuard)
279 {
280 ftsensor.enableSafeGuard(forceGuard, torqueGuard);
281 }
282
283 template <typename ConfigType, typename ConfigDictType>
284 void
286 ConfigType& c,
287 TSCtrlRtStatus& rtStatus)
288 {
289 /// ---------------------------- admittance control ---------------------------------------------
290 /// calculate pose error between the virtual pose and the target pose
291 rtStatus.poseDiffMatAdm = rtStatus.virtualPose.block<3, 3>(0, 0) *
292 rtStatus.desiredPose.block<3, 3>(0, 0).transpose();
293 rtStatus.poseErrorAdm.head<3>() =
294 rtStatus.virtualPose.block<3, 1>(0, 3) - rtStatus.desiredPose.block<3, 1>(0, 3);
295 rtStatus.poseErrorAdm.tail<3>() =
296 VirtualRobot::MathTools::eigen3f2rpy(rtStatus.poseDiffMatAdm);
297
298 /// admittance control law and Euler Integration -> virtual pose
299 rtStatus.acc = c.kmAdmittance.cwiseProduct(rtStatus.currentForceTorque) -
300 c.kpAdmittance.cwiseProduct(rtStatus.poseErrorAdm) -
301 c.kdAdmittance.cwiseProduct(rtStatus.virtualVel);
302
303 rtStatus.vel =
304 rtStatus.virtualVel + 0.5 * rtStatus.deltaT * (rtStatus.acc + rtStatus.virtualAcc);
305 rtStatus.pos = 0.5 * rtStatus.deltaT * (rtStatus.vel + rtStatus.virtualVel);
306 rtStatus.virtualAcc = rtStatus.acc;
307 rtStatus.virtualVel = rtStatus.vel;
308
309 rtStatus.virtualPose.block<3, 1>(0, 3) += rtStatus.pos.head<3>();
310 rtStatus.virtualPose.block<3, 3>(0, 0) =
311 VirtualRobot::MathTools::rpy2eigen3f(
312 rtStatus.pos(3), rtStatus.pos(4), rtStatus.pos(5)) *
313 rtStatus.virtualPose.block<3, 3>(0, 0);
314
315 /// ------------------------------------- Impedance control --------------------------------
316 /// calculate pose error between target pose and current pose
317 /// !!! This is very important: you have to keep position and orientation both
318 /// with UI unit (meter, radian) to calculate impedance force.
319
320 rtStatus.poseDiffMatImp = rtStatus.desiredPose.block<3, 3>(0, 0) *
321 rtStatus.currentPose.block<3, 3>(0, 0).transpose();
322 rtStatus.poseErrorImp.head<3>() = 0.001 * (rtStatus.desiredPose.block<3, 1>(0, 3) -
323 rtStatus.currentPose.block<3, 1>(0, 3));
324 rtStatus.poseErrorImp.tail<3>() =
325 VirtualRobot::MathTools::eigen3f2rpy(rtStatus.poseDiffMatImp);
326
327 rtStatus.forceImpedance = c.kpImpedance.cwiseProduct(rtStatus.poseErrorImp) -
328 c.kdImpedance.cwiseProduct(rtStatus.currentTwist);
329 }
330
331 template <typename ConfigType, typename ConfigDictType>
332 void
334 {
335
336 /// Determine the size of the submatrix (M x M)
337 auto& idx = rtStatus.jointIDVelocityMode;
338 size_t m = idx.size();
339
340 /// Extract elements: Loop over rows and columns
341 for (size_t i = 0; i < m; ++i)
342 {
343 for (size_t j = 0; j < m; ++j)
344 {
345 // Map the submatrix (i, j) to the original matrix (indices[i], indices[j])
346 rtStatus.inertiaVelCtrlJoints(i, j) = rtStatus.inertia(idx[i], idx[j]);
347 }
348 }
349 // ARMARX_INFO << VAROUT(rtStatus.inertiaVelCtrlJoints);
350 double epsilon = 1e-5;
351 if (rtStatus.inertiaVelCtrlJoints.isZero(epsilon))
352 {
353 rtStatus.inertiaInvVelCtrlJoints.setZero();
354 return;
355 }
356 /// Invert and cast to float
357 rtStatus.inertiaInvVelCtrlJoints =
358 rtStatus.inertiaVelCtrlJoints.cast<float>().llt().solve(rtStatus.IVelCtrlJoint);
359 if (rtStatus.inertiaVelCtrlJoints.hasNaN())
360 {
361 rtStatus.inertiaInvVelCtrlJoints.setZero();
362 }
363 }
364
365 template <typename ConfigType, typename ConfigDictType>
366 void
368 ConfigType& c,
369 TSCtrlRtStatus& rtStatus)
370 {
371
372 float k_d = 0.5;
373
374 size_t m = rtStatus.nDoFVelocity;
375 for (size_t i = 0; i < m; ++i)
376 {
377 size_t idx = rtStatus.jointIDVelocityMode[i];
378 rtStatus.qTemp(i) = rtStatus.desiredJointTorque(idx) -
379 k_d * (rtStatus.qdVel(i) - rtStatus.qvelFiltered(idx));
380 /* - k_p * (rtStatus.qdPos(i) - rtStatus.jointPosition(idx))*/
381 }
382 rtStatus.qdAcc = rtStatus.inertiaInvVelCtrlJoints * rtStatus.qTemp;
383 rtStatus.qdVel = 0.5 * (rtStatus.deltaT * rtStatus.qdAcc + rtStatus.qdVel);
384 rtStatus.desiredJointVelocity.setZero();
385 for (size_t i = 0; i < m; ++i)
386 {
387 rtStatus.desiredJointVelocity(rtStatus.jointIDVelocityMode[i]) =
388 std::clamp(rtStatus.qdVel(i), -c.velocityLimit, c.velocityLimit);
389 }
390 }
391
392 template <typename ConfigType, typename ConfigDictType>
393 bool
398
399 void
401 {
402 validate(c, rtStatus);
403 updateJacobian(c, rtStatus);
404 updateTwist(c, rtStatus);
405 updateTSError(c, rtStatus);
406
407 if (not c.enableAdmInterface)
408 {
409 updateTSTwistCommand(c, rtStatus);
411 }
412
413 updateTSForceCommand(c, rtStatus);
415
416 applyInverseDynamics(c, rtStatus);
417 if (c.enableAdmInterface)
418 {
419 applyAdmittanceInterface(c, rtStatus);
420 }
421 else
422 {
423 applyInverseKinematics(c, rtStatus);
424 }
425 }
426
427 // void
428 // TSMixImpVelController::run(Config& c, TSCtrlRtStatus& rtStatus)
429 // {
430 // validate(c, rtStatus);
431 // updateJacobian(c, rtStatus);
432 // updateTwist(c, rtStatus);
433 // updateTSError(c, rtStatus);
434
435 // // updateTSTwistCommand(c, rtStatus);
436 // // updateNullspaceTwistCommand(c, rtStatus);
437
438 // updateTSForceCommand(c, rtStatus);
439 // updateNullspaceForceCommand(c, rtStatus);
440
441 // applyInverseDynamics(c, rtStatus);
442 // computeInertiaInverse(rtStatus);
443 // applyAdmittanceInterface(c, rtStatus);
444 // }
445
446 void
448 {
449 validate(c, rtStatus);
450 updateJacobian(c, rtStatus);
451 updateTwist(c, rtStatus);
452 updateTSError(c, rtStatus);
453
454 updateTSForceCommand(c, rtStatus);
456
457 applyInverseDynamics(c, rtStatus);
458 }
459
460 void
462 {
463 validate(c, rtStatus);
464 updateJacobian(c, rtStatus);
465 updateTwist(c, rtStatus);
466 updateTSError(c, rtStatus);
467
468 updateTSTwistCommand(c, rtStatus);
470
471 computeInertiaInverse(rtStatus);
472 applyInverseKinematics(c, rtStatus);
473 }
474
475 void
477 {
478 validate(c, rtStatus);
479 updateJacobian(c, rtStatus);
480 updateTwist(c, rtStatus);
481 updateTSAdmittanceCommand(c, rtStatus);
483
484 applyInverseDynamics(c, rtStatus);
485 }
486
487} // namespace armarx::control::common::control_law
#define M_PI
Definition MathTools.h:17
constexpr T c
void run(Config &c, TSCtrlRtStatus &robotStatus) override
void run(Config &c, TSCtrlRtStatus &robotStatus) override
void run(Config &c, TSCtrlRtStatus &robotStatus) override
void run(Config &c, TSCtrlRtStatus &robotStatus) override
void updateTSError(ConfigType &c, TSCtrlRtStatus &rtStatus)
void updateJacobian(ConfigType &c, TSCtrlRtStatus &rtStatus)
void applyAdmittanceInterface(ConfigType &c, TSCtrlRtStatus &rtStatus)
void applyInverseKinematics(ConfigType &c, TSCtrlRtStatus &rtStatus)
void updateNullspaceForceCommand(ConfigType &c, TSCtrlRtStatus &rtStatus)
void updateTSAdmittanceCommand(ConfigType &c, TSCtrlRtStatus &rtStatus)
Impedance.
void updateTSTwistCommand(ConfigType &c, TSCtrlRtStatus &rtStatus)
void setForceTorqueGuard(bool forceGuard, bool torqueGuard)
void updateTSForceCommand(ConfigType &c, TSCtrlRtStatus &rtStatus)
Velocity.
void validate(ConfigType &c, TSCtrlRtStatus &rtStatus)
void applyInverseDynamics(ConfigType &c, TSCtrlRtStatus &rtStatus)
bool isControlModeValid(const std::vector< std::string > &nameList, const std::map< std::string, std::string > &jointControlModeMap)
void updateNullspaceTwistCommand(ConfigType &c, TSCtrlRtStatus &rtStatus)
Admittance.
void updateTwist(ConfigType &c, TSCtrlRtStatus &rtStatus)
#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_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
std::vector< std::vector< T > > transpose(const std::vector< std::vector< T > > &src, Thrower thrower)
Eigen::Vector6f cartesianVelTarget
for impedance control
Definition common.h:75
Eigen::Matrix3f poseDiffMatAdm
intermediate results for admittance controller
Definition common.h:90
Eigen::MatrixXf jtpinv
pseudo inverse of the jacobi transpose [6, nDoF], for torque control
Definition common.h:118
Eigen::MatrixXf I
for computing nullspace projection
Definition common.h:124
Eigen::MatrixXf jpinv
pseudo inverse of the jacobi [nDoF, 6], for vel control
Definition common.h:121
Eigen::MatrixXf jacobi
others jacobi matrix [6, nDoF]
Definition common.h:116
Eigen::Matrix3f poseDiffMatImp
intermediate results
Definition common.h:85
Eigen::VectorXf qvelFiltered
for velocity control
Definition common.h:78
Eigen::MatrixXd inertia
inertia Note, the inertia variables are only used for collision avoidance controllers,...
Definition common.h:104
Eigen::Vector6f forceImpedance
task space variables (command in operation space)
Definition common.h:74
Eigen::Vector6f currentForceTorque
force torque
Definition common.h:70