TaskspaceAdmittanceController.cpp
Go to the documentation of this file.
2 
3 #include <fstream>
4 #include <boost/algorithm/clamp.hpp>
5 
6 #include <SimoxUtility/json.h>
7 #include <SimoxUtility/math/compare/is_equal.h>
8 #include <SimoxUtility/math/convert/rpy_to_mat3f.h>
9 #include <SimoxUtility/math/convert/mat4f_to_pos.h>
10 #include <SimoxUtility/math/convert/mat4f_to_quat.h>
11 #include <VirtualRobot/MathTools.h>
14 
15 #include "../utils.h"
16 
17 
19 {
20 
21 void TaskspaceAdmittanceController::initialize(const VirtualRobot::RobotNodeSetPtr& rns)
22  {
24  tcp = rns->getTCP();
26  ik.reset(new VirtualRobot::DifferentialIK(
27  rns, rns->getRobot()->getRootNode(), VirtualRobot::JacobiProvider::eSVDDamped));
28 
29  numOfJoints = rns->getSize();
30  I = Eigen::MatrixXf::Identity(numOfJoints, numOfJoints);
31  // s.qpos.resize(numOfJoints);
32  // s.qvel.resize(numOfJoints);
33  // s.desiredJointTorques.resize(numOfJoints);
34 
35  // s.currentTwist.setZero();
36  // s.forceImpedance.setZero();
37  // s.virtualVel.setZero();
38  // s.virtualAcc.setZero();
39 
40  // /// parse configuration files
41  // nlohmann::json userConfig;
42  // nlohmann::json defaultConfig;
43  // std::string filename;
44  // {
45  // ArmarXDataPath::getAbsolutePath(
46  // "armarx_control/controller_config/default/njoint_controller/impedance_controller_config.json",
47  // filename);
48  // ARMARX_INFO << "ctrl law: reading default config from: " << VAROUT(filename);
49  // std::ifstream ifs{filename};
50  // ifs >> defaultConfig;
51  // if (defaultConfig.empty())
52  // {
53  // ARMARX_ERROR << "ctrl law: default config is empty!";
54  // }
55  // }
56  // if (configFileName.empty())
57  // {
58  // ARMARX_IMPORTANT << "ctrl law: No control parameter specified by user, use default parameter in \n" << filename;
59  // userConfig = defaultConfig;
60  // }
61  // else
62  // {
63  // std::ifstream ifs{configFileName};
64  // ifs >> userConfig;
65  // }
66  // if (userConfig.empty())
67  // {
68  // ARMARX_ERROR << "ctrl law: userConfig is empty!";
69  // }
70 
71  // if (userConfig.find("control") != userConfig.end())
72  // {
73  // auto c = userConfig["control"];
74  // auto dc = defaultConfig["control"];
75  // s.kpImpedance = getEigenVec(c, dc, "impedance_stiffness");
76  // s.kdImpedance = getEigenVec(c, dc, "impedance_damping");
77  // s.kpAdmittance = getEigenVec(c, dc, "admittance_stiffness");
78  // s.kdAdmittance = getEigenVec(c, dc, "admittance_damping");
79  // s.kmAdmittance = getEigenVec(c, dc, "admittance_inertia");
80  // s.kpNullspace = getEigenVec(c, dc, "nullspace_stiffness", numOfJoints, 10.0f);
81  // s.kdNullspace = getEigenVec(c, dc, "nullspace_damping", numOfJoints, 2.0f);
82  // s.desiredNullspaceJointAngles = getEigenVec(c, dc, "desired_nullspace_joint_angles");
83  // if (s.desiredNullspaceJointAngles.size() != numOfJoints)
84  // {
85  // ARMARX_IMPORTANT << "ctrl law: default value is not consistent with requested kinematic chain, reinit in preactivation";
86  // enablePreactivateInit.store(true);
87  // }
88  // s.torqueLimit = getValue<float>(c, dc, "torque_limit");
89  // s.qvelFilter = getValue<float>(c, dc, "qvel_filter");
90  // }
91  // else
92  // {
93  // ARMARX_WARNING << "ctrl law: controller parameters not found, they should be placed in 'control' tag in your json file";
94  // }
95 
96  }
97 
98 
99  //TaskspaceAdmittanceController::Config TaskspaceAdmittanceController::reconfigure(const std::string &configFileName)
100  //{
101  // nlohmann::json userConfig;
102  // std::ifstream ifs{configFileName};
103  // ifs >> userConfig;
104  // auto c = userConfig["control"];
105  // Config config = Config();
106  // config.kpImpedance = getEigenVecWithDefault (c, s.kpImpedance, "impedance_stiffness");
107  // config.kdImpedance = getEigenVecWithDefault (c, s.kdImpedance, "impedance_damping");
108  // config.kpAdmittance = getEigenVecWithDefault (c, s.kpAdmittance, "admittance_stiffness");
109  // config.kdAdmittance = getEigenVecWithDefault (c, s.kdAdmittance, "admittance_damping");
110  // config.kmAdmittance = getEigenVecWithDefault (c, s.kmAdmittance, "admittance_inertia");
111  // config.kpNullspace = getEigenVecWithDefault (c, s.kpNullspace, "nullspace_stiffness");
112  // config.kdNullspace = getEigenVecWithDefault (c, s.kdNullspace, "nullspace_damping");
113 
114  // config.currentForceTorque.setZero();
115  // config.desiredPose = s.currentPose;
116  // config.desiredTwist.setZero();
117  // config.desiredNullspaceJointAngles = getEigenVecWithDefault (c, s.desiredNullspaceJointAngles, "desired_nullspace_joint_angles");
118  // config.torqueLimit = getValueWithDefault<float>(c, s.torqueLimit, "torque_limit");
119  // config.qvelFilter = getValueWithDefault<float>(c, s.qvelFilter, "qvel_filter");
120  // return config;
121  //}
122 
123  void TaskspaceAdmittanceController::preactivateInit(const VirtualRobot::RobotNodeSetPtr& rns)
124  {
125  ARMARX_INFO << "preactivate control law TaskspaceImpedanceController";
126  Eigen::Matrix4f currentPose = rns->getTCP()->getPoseInRootFrame();
127  {
128  NonRtStatus s;
129 
130  s.qpos = rns->getJointValuesEigen();
131  s.qvel.setZero(numOfJoints);
132 
133  s.currentPose = currentPose;
134  s.currentTwist.setZero();
135 
136  s.jacobi.setZero(6, numOfJoints);
137  s.jtpinv.setZero(6, numOfJoints);
138 
139  bufferNonRtToRt.reinitAllBuffers(s);
140  bufferNonRtToOnPublish.reinitAllBuffers(s);
141  }
142 
143  {
144  RtStatus s;
145  s.desiredJointTorques.setZero(numOfJoints);
146  s.nullspaceTorque.setZero(numOfJoints);
147 
148  s.virtualPose = currentPose;
149  s.virtualVel.setZero();
150  s.virtualAcc.setZero();
151 
152  s.desiredPose = currentPose;
153  s.desiredTwist.setZero();
154  s.kmAdmittance.setZero();
155 
156  s.currentForceTorque.setZero();
157 
158  s.forceImpedance.setZero();
159  bufferRtToOnPublish.reinitAllBuffers(s);
160  }
161  ARMARX_INFO << "preactivation done";
162  }
163 
165  {
166  auto& sRt = bufferRtToOnPublish.getWriteBuffer();
167  sRt.desiredPose = bufferNonRtToRt.getUpToDateReadBuffer().currentPose;
168  sRt.currentForceTorque.setZero();
169 
170  bufferRtToOnPublish.commitWrite();
171  ftsensor.reset();
172 // isCalibrated.store(false);
173  // s.previousDesiredPose = s.currentPose;
174  // s.desiredPose = s.currentPose;
175  // s.desiredTwist.setZero();
176  // s.virtualPose = s.currentPose;
177  // s.virtualVel.setZero();
178  // s.virtualAcc.setZero();
179  // ARMARX_INFO << VAROUT(s.desiredTCPPose) << VAROUT(s.previousTargetPose);
180  }
181 
182 
183  bool
185  const Config& cfg,
186  const RobotStatus& robotStatus)
187  {
188  auto& s = bufferNonRtToRt.getWriteBuffer();
189 
190  /// check size and value
191  {
192  ARMARX_CHECK_EQUAL((unsigned)numOfJoints, robotStatus.jointPosition.size());
193  ARMARX_CHECK_EQUAL((unsigned)numOfJoints, robotStatus.jointVelocity.size());
194  ARMARX_CHECK_EQUAL((unsigned)numOfJoints, robotStatus.jointTorque.size());
195  }
196 
197  /// ----------------------------- get current status of robot ---------------------------------------------
198  /// original data in ArmarX use the following units:
199  /// position in mm,
200  /// joint angles and orientation in radian,
201  /// velocity in mm/s and radian/s, etc.
202  /// here we convert mm to m, if you use MP from outside, make sure to convert it back to mm
203 
204  s.currentPose = tcp->getPoseInRootFrame();
205  s.jacobi = ik->getJacobianMatrix(tcp, VirtualRobot::IKSolver::CartesianSelection::All);
206  s.jacobi.block(0, 0, 3, numOfJoints) = 0.001 * s.jacobi.block(0, 0, 3, numOfJoints);
207  ARMARX_CHECK_EQUAL(numOfJoints, s.jacobi.cols());
208  ARMARX_CHECK_EQUAL(6, s.jacobi.rows());
209 
210  s.jtpinv = ik->computePseudoInverseJacobianMatrix(s.jacobi.transpose(), lambda);
211  ARMARX_CHECK_EQUAL(numOfJoints, s.jtpinv.cols());
212  ARMARX_CHECK_EQUAL(6, s.jtpinv.rows());
213 
214  Eigen::VectorXf qvelRaw(numOfJoints);
215  for (size_t i = 0; i < robotStatus.jointVelocity.size(); ++i)
216  {
217  s.qpos(i) = robotStatus.jointPosition[i];
218  qvelRaw(i) = robotStatus.jointVelocity[i];
219  }
220  ARMARX_CHECK_EQUAL(s.qvel.rows(), qvelRaw.rows());
221 
222  s.qvel = (1 - cfg.qvelFilter) * s.qvel + cfg.qvelFilter * qvelRaw;
223  s.currentTwist = s.jacobi * s.qvel;
224 
225  bufferNonRtToOnPublish.getWriteBuffer() = s;
226 
227  bufferNonRtToOnPublish.commitWrite();
228  bufferNonRtToRt.commitWrite();
229 
230  isInitialized.store(true);
231  return true;
232  }
233 
234  void
236  {
237  /// run in rt thread
238 
239  if (c.reCalibrate)
240  {
241  ftsensor.calibrated.store(false);
242  /// TODO how to toggle recalibration?
243  }
245  if (!ftsensor.calibrated.load())
246  {
247  if(ftsensor.calibrate(c, deltaT))
248  {
249  ftsensor.calibrated.store(true);
250  }
251  }
252  else
253  {
254  bufferRtToOnPublish.getWriteBuffer().currentForceTorque = ftsensor.getFilteredForceTorque(c);
255  bufferRtToOnPublish.commitWrite();
256  }
257  }
258 
259  void
261  const Config& c,
262  ControlTarget& targets,
263  double deltaT)
264  {
265  /// run in rt thread
266 
267  auto& sRt = bufferRtToOnPublish.getWriteBuffer();
268  const auto& s = bufferNonRtToRt.getUpToDateReadBuffer();
269 
270  /// only when the ft sensor is calibrated can we allow the admittance controller
271  /// to have virtual pose modulation out of force/torque information.
272  if (not ftsensor.calibrated.load() or not isInitialized.load())
273  {
274  sRt.desiredTwist.setZero();
275  sRt.kmAdmittance.setZero();
276  sRt.currentForceTorque.setZero();
277  }
278  else
279  {
280  if ((sRt.desiredPose.block<3, 1>(0, 3) - c.desiredPose.block<3, 1>(0, 3)).norm() > 100.0f)
281  {
282  sRt.currentForceTorque.setZero();
283  ARMARX_RT_LOGF_WARN("new target \n\n %s\n\nis too far away from\n\n %s", VAROUT(c.desiredPose), VAROUT(sRt.desiredPose));
284  }
285  else
286  {
287  sRt.desiredPose = c.desiredPose;
288  }
289  sRt.desiredTwist = c.desiredTwist;
290  sRt.kmAdmittance = c.kmAdmittance;
291  }
292  /// ---------------------------- admittance control ---------------------------------------------
293  /// calculate pose error between the virtual pose and the target pose
294  Eigen::Matrix3f objDiffMat = sRt.virtualPose.block<3, 3>(0, 0) * sRt.desiredPose.block<3, 3>(0, 0).transpose();
295  Eigen::Vector6f poseError;
296  poseError.head(3) = sRt.virtualPose.block<3, 1>(0, 3) - sRt.desiredPose.block<3, 1>(0, 3);
297  poseError.tail(3) = VirtualRobot::MathTools::eigen3f2rpy(objDiffMat);
298 
299  /// admittance control law and Euler Integration -> virtual pose
300  Eigen::Vector6f acc =
301  sRt.kmAdmittance.cwiseProduct(sRt.currentForceTorque) -
302  c.kpAdmittance.cwiseProduct(poseError) -
303  c.kdAdmittance.cwiseProduct(sRt.virtualVel);
304 
305  Eigen::Vector6f vel = sRt.virtualVel + 0.5 * deltaT * (acc + sRt.virtualAcc);
306  Eigen::Vector6f deltaPose = 0.5 * deltaT * (vel + sRt.virtualVel);
307  sRt.virtualAcc = acc;
308  sRt.virtualVel = vel;
309 
310  Eigen::Matrix3f deltaPoseMat = VirtualRobot::MathTools::rpy2eigen3f(deltaPose(3), deltaPose(4), deltaPose(5));
311  sRt.virtualPose.block<3, 1>(0, 3) += deltaPose.head(3);
312  sRt.virtualPose.block<3, 3>(0, 0) = deltaPoseMat * sRt.virtualPose.block<3, 3>(0, 0);
313 
314  /// ----------------------------- Impedance control ---------------------------------------------
315  /// calculate pose error between target pose and current pose
316  /// !!! This is very important: you have to keep postion and orientation both
317  /// with UI unit (meter, radian) to calculate impedance force.
318 
319  Eigen::Matrix3f diffMat =
320  sRt.virtualPose.block<3, 3>(0, 0) * s.currentPose.block<3, 3>(0, 0).transpose();
321  Eigen::Vector6f poseErrorImp;
322  poseErrorImp.head(3) = 0.001 * (sRt.virtualPose.block<3, 1>(0, 3) - s.currentPose.block<3, 1>(0, 3));
323  poseErrorImp.tail(3) = VirtualRobot::MathTools::eigen3f2rpy(diffMat);
324  sRt.forceImpedance =
325  c.kpImpedance.cwiseProduct(poseErrorImp) -
326  c.kdImpedance.cwiseProduct(s.currentTwist);
327 
328  /// ----------------------------- Nullspace PD Control --------------------------------------------------
329  ARMARX_CHECK_EQUAL((unsigned)numOfJoints, c.kpNullspace.rows());
330  ARMARX_CHECK_EQUAL((unsigned)numOfJoints, s.qpos.rows());
331  ARMARX_CHECK_EQUAL((unsigned)numOfJoints, s.qvel.rows());
332 
333  Eigen::VectorXf nullspaceTorque =
334  c.kpNullspace.cwiseProduct(c.desiredNullspaceJointAngles.value() - s.qpos) -
335  c.kdNullspace.cwiseProduct(s.qvel);
336 
337  /// ----------------------------- Map TS target force to JS --------------------------------------------------
338  ARMARX_CHECK_EQUAL((unsigned)6, s.jacobi.rows());
339  ARMARX_CHECK_EQUAL((unsigned)numOfJoints, s.jacobi.cols());
340 
341  ARMARX_CHECK_EQUAL((unsigned)6, s.jtpinv.rows());
342  ARMARX_CHECK_EQUAL((unsigned)numOfJoints, s.jtpinv.cols());
343 
344  sRt.desiredJointTorques =
345  s.jacobi.transpose() * sRt.forceImpedance +
346  (I - s.jacobi.transpose() * s.jtpinv) * nullspaceTorque;
347 
348  /// ----------------------------- write torque target --------------------------------------------------
349  for (size_t i = 0; i < (unsigned)sRt.desiredJointTorques.rows(); ++i)
350  {
351  sRt.desiredJointTorques(i) = std::clamp(sRt.desiredJointTorques(i), -c.torqueLimit, c.torqueLimit);
352  targets.targets.at(i) = sRt.desiredJointTorques(i);
353  }
354 
355  bufferRtToOnPublish.commitWrite();
356  }
357 } /// namespace armarx::control::common::control_law
armarx::control::common::control_law::TaskspaceAdmittanceController::RtStatus
internal status of the controller, containing intermediate variables, mutable targets
Definition: TaskspaceAdmittanceController.h:67
armarx::control::common::control_law::TaskspaceAdmittanceController::firstRun
void firstRun()
Definition: TaskspaceAdmittanceController.cpp:164
armarx::control::common::control_law::TaskspaceAdmittanceController::NonRtStatus
Definition: TaskspaceAdmittanceController.h:88
armarx::control::common::control_law::ControlTarget::targets
std::vector< float > targets
Definition: common.h:46
armarx::control::common::control_law::RobotStatus::jointTorque
std::vector< float > jointTorque
Definition: common.h:34
ARMARX_CHECK_NOT_NULL
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
Definition: ExpressionException.h:206
armarx::control::common::control_law::TaskspaceAdmittanceController::bufferRtToOnPublish
TripleBuffer< RtStatus > bufferRtToOnPublish
Definition: TaskspaceAdmittanceController.h:118
c
constexpr T c
Definition: UnscentedKalmanFilterTest.cpp:43
armarx::control::common::control_law::RobotStatus::jointVelocity
std::vector< float > jointVelocity
Definition: common.h:33
armarx::control::common::control_law::TaskspaceAdmittanceController::ftsensor
common::ft::FTSensor ftsensor
Definition: TaskspaceAdmittanceController.h:124
armarx::control::common::control_law::TaskspaceAdmittanceController::Config::qvelFilter
float qvelFilter
Definition: TaskspaceAdmittanceController.h:60
clamp
double clamp(double x, double a, double b)
Definition: point.hpp:125
GfxTL::Identity
void Identity(MatrixXX< N, N, T > *a)
Definition: MatrixXX.h:523
armarx::control::common::control_law::TaskspaceAdmittanceController::preactivateInit
void preactivateInit(const VirtualRobot::RobotNodeSetPtr &rns)
Definition: TaskspaceAdmittanceController.cpp:123
armarx::control::common::control_law::RobotStatus
Definition: common.h:30
armarx::control::common::ft::FTSensor::calibrated
std::atomic_bool calibrated
Definition: FTSensor.h:77
armarx::control::common::control_law
namespace armarx::control::common::ft
Definition: aron_conversions.cpp:62
armarx::control::common::control_law::TaskspaceAdmittanceController::bufferNonRtToRt
TripleBuffer< NonRtStatus > bufferNonRtToRt
Definition: TaskspaceAdmittanceController.h:120
armarx::control::common::ft::FTSensor::reset
void reset()
Definition: FTSensor.cpp:80
armarx::control::common::control_law::TaskspaceAdmittanceController::Config
you can set the following values from outside of the rt controller via Ice interfaces
Definition: TaskspaceAdmittanceController.h:43
TaskspaceAdmittanceController.h
armarx::control::common::control_law::RobotStatus::jointPosition
std::vector< float > jointPosition
Definition: common.h:32
armarx::control::common::control_law::TaskspaceAdmittanceController::isInitialized
std::atomic< bool > isInitialized
Definition: TaskspaceAdmittanceController.h:136
armarx::control::common::control_law::TaskspaceAdmittanceController::tcp
VirtualRobot::RobotNodePtr tcp
Definition: TaskspaceAdmittanceController.h:123
armarx::control::common::ft::FTSensor::getFilteredForceTorque
Eigen::Vector6f & getFilteredForceTorque(const FTConfig &data)
Definition: FTSensor.cpp:158
armarx::control::common::control_law::TaskspaceAdmittanceController::bufferNonRtToOnPublish
TripleBuffer< NonRtStatus > bufferNonRtToOnPublish
Definition: TaskspaceAdmittanceController.h:121
GfxTL::Matrix3f
MatrixXX< 3, 3, float > Matrix3f
Definition: MatrixXX.h:600
GfxTL::Matrix4f
MatrixXX< 4, 4, float > Matrix4f
Definition: MatrixXX.h:601
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
armarx::control::common::control_law::TaskspaceAdmittanceController::run
void run(const Config &cfg, ControlTarget &targets, double deltaT)
Definition: TaskspaceAdmittanceController.cpp:260
VAROUT
#define VAROUT(x)
Definition: StringHelpers.h:182
armarx::control::common::ft::FTSensor::calibrate
bool calibrate(const FTConfig &c, double deltaT)
Definition: FTSensor.cpp:98
Eigen::Matrix< float, 6, 1 >
ControlThreadOutputBuffer.h
ARMARX_CHECK_EQUAL
#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...
Definition: ExpressionException.h:130
armarx::control::common::control_law::TaskspaceAdmittanceController::updateControlStatus
bool updateControlStatus(const Config &cfg, const RobotStatus &robotStatus)
Definition: TaskspaceAdmittanceController.cpp:184
ARMARX_RT_LOGF_WARN
#define ARMARX_RT_LOGF_WARN(...)
Definition: ControlThreadOutputBuffer.h:345
ArmarXDataPath.h
armarx::control::common::control_law::ControlTarget
Definition: common.h:44
armarx::control::common::control_law::TaskspaceAdmittanceController::initialize
void initialize(const VirtualRobot::RobotNodeSetPtr &rns)
Definition: TaskspaceAdmittanceController.cpp:21
armarx::ctrlutil::s
double s(double t, double s0, double v0, double a0, double j)
Definition: CtrlUtil.h:33
armarx::control::common::ft::FTSensor::FTConfig
Definition: FTSensor.h:47
armarx::control::common::ft::FTSensor::compensateTCPGravity
Eigen::Vector6f & compensateTCPGravity(const FTConfig &data)
Definition: FTSensor.cpp:131
armarx::control::common::control_law::TaskspaceAdmittanceController::updateFT
void updateFT(const common::ft::FTSensor::FTConfig &c, double deltaT)
Definition: TaskspaceAdmittanceController.cpp:235