KeypointsAdmittanceController.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <fstream>
5
6#include <SimoxUtility/json.h>
7#include <SimoxUtility/math/compare/is_equal.h>
8#include <SimoxUtility/math/convert/mat4f_to_pos.h>
9#include <SimoxUtility/math/convert/mat4f_to_quat.h>
10#include <SimoxUtility/math/convert/rpy_to_mat3f.h>
11#include <VirtualRobot/IK/DifferentialIK.h>
12#include <VirtualRobot/IK/JacobiProvider.h>
13#include <VirtualRobot/MathTools.h>
14#include <VirtualRobot/Robot.h>
15#include <VirtualRobot/RobotNodeSet.h>
16
18
22
23#include "../utils.h"
24
26{
27
28 void
29 KeypointsAdmittanceController::initialize(const VirtualRobot::RobotNodeSetPtr& rns,
30 const std::string& configFileName)
31 {
33 tcp = rns->getTCP();
35 ik.reset(new VirtualRobot::DifferentialIK(
36 rns, rns->getRobot()->getRootNode(), VirtualRobot::JacobiProvider::eSVDDamped));
37
38 /// joint space variables
39 numOfJoints = rns->getSize();
40 s.qpos.resize(numOfJoints);
41 s.qvel.resize(numOfJoints);
42 s.desiredJointTorques.resize(numOfJoints);
43
44 s.currentForceTorque.setZero();
45 s.currentTwist.setZero();
46 s.forceImpedance.setZero();
47 s.virtualVel.setZero();
48 s.virtualAcc.setZero();
49 s.pointTrackingForce.setZero();
50
51 /// parse configuration files
52 nlohmann::json userConfig;
53 nlohmann::json defaultConfig;
54 std::string filename;
55 {
56 ArmarXDataPath::getAbsolutePath("armarx_control/controller_config/default/"
57 "njoint_controller/impedance_controller_config.json",
58 filename);
59 ARMARX_INFO << "reading default config from: " << VAROUT(filename);
60 std::ifstream ifs{filename};
61 ifs >> defaultConfig;
62 if (defaultConfig.empty())
63 {
64 ARMARX_ERROR << "default config is empty!";
65 }
66 }
67 if (configFileName.empty())
68 {
70 << "No control parameter specified by user, use default parameter in \n"
71 << filename;
72 userConfig = defaultConfig;
73 }
74 else
75 {
76 std::ifstream ifs{configFileName};
77 ifs >> userConfig;
78 }
79 if (userConfig.empty())
80 {
81 ARMARX_ERROR << "userConfig is empty!";
82 }
83
84 if (userConfig.find("control") != userConfig.end())
85 {
86 auto c = userConfig["control"];
87 auto dc = defaultConfig["control"];
88 s.kpImpedance = getEigenVec(c, dc, "impedance_stiffness");
89 s.kdImpedance = getEigenVec(c, dc, "impedance_damping");
90 s.kpAdmittance = getEigenVec(c, dc, "admittance_stiffness");
91 s.kdAdmittance = getEigenVec(c, dc, "admittance_damping");
92 s.kmAdmittance = getEigenVec(c, dc, "admittance_inertia");
93 s.kpNullspaceTorque = getEigenVec(c, dc, "nullspace_stiffness", numOfJoints, 10.0f);
94 s.kdNullspaceTorque = getEigenVec(c, dc, "nullspace_damping", numOfJoints, 2.0f);
95 s.desiredNullspaceJointAngles = getEigenVec(c, dc, "desired_nullspace_joint_angles");
96 if (s.desiredNullspaceJointAngles.size() != numOfJoints)
97 {
98 ARMARX_IMPORTANT << "default value is not consistent with requested kinematic "
99 "chain, reinit in preactivation";
100 enablePreactivateInit.store(true);
101 }
102 s.torqueLimit = getValue<float>(c, dc, "torque_limit");
103 s.qvelFilter = getValue<float>(c, dc, "qvel_filter");
104 /// keypoints related
105 s.numPoints = getValue<int>(c, dc, "num_points");
106 s.keypointKp = getEigenVec(c, dc, "keypoint_stiffness");
107 s.keypointKd = getEigenVec(c, dc, "keypoint_damping");
108 s.currentKeypointPosition = getEigenVec(c, dc, "current_keypoint_position");
109 s.desiredKeypointPosition = s.currentKeypointPosition;
110 s.filteredKeypointPosition = s.currentKeypointPosition;
111 s.previousKeypointPosition = s.currentKeypointPosition;
112 s.keypointPositionFilter = getValue<float>(c, dc, "keypoint_position_filter");
113 s.keypointVelocityFilter = getValue<float>(c, dc, "keypoint_velocity_filter");
114
115 s.isRigid = getValue<bool>(c, dc, "is_rigid");
116 s.fixedTranslation = getEigenVec(c, dc, "fixed_translation");
117 s.desiredKeypointVelocity.setZero(3 * s.numPoints);
118 s.currentKeypointVelocity.setZero(3 * s.numPoints);
119 }
120 else
121 {
122 ARMARX_WARNING << "controller parameters not found, they should be placed in 'control' "
123 "tag in your json file";
124 }
125
126 I = Eigen::MatrixXf::Identity(numOfJoints, numOfJoints);
127 }
128
130 KeypointsAdmittanceController::reconfigure(const std::string& configFileName)
131 {
132 nlohmann::json userConfig;
133 std::ifstream ifs{configFileName};
134 ifs >> userConfig;
135 auto c = userConfig["control"];
136 Config config = Config();
137 config.kpImpedance = getEigenVecWithDefault(c, s.kpImpedance, "impedance_stiffness");
138 config.kdImpedance = getEigenVecWithDefault(c, s.kdImpedance, "impedance_damping");
139 config.kpAdmittance = getEigenVecWithDefault(c, s.kpAdmittance, "admittance_stiffness");
140 config.kdAdmittance = getEigenVecWithDefault(c, s.kdAdmittance, "admittance_damping");
141 config.kmAdmittance = getEigenVecWithDefault(c, s.kmAdmittance, "admittance_inertia");
142 config.kpNullspaceTorque = getEigenVecWithDefault(c, s.kpNullspaceTorque, "nullspace_stiffness");
143 config.kdNullspaceTorque = getEigenVecWithDefault(c, s.kdNullspaceTorque, "nullspace_damping");
144 config.currentForceTorque.setZero();
146 c, s.desiredNullspaceJointAngles, "desired_nullspace_joint_angles");
147 config.torqueLimit = getValueWithDefault<float>(c, s.torqueLimit, "torque_limit");
148 config.qvelFilter = getValueWithDefault<float>(c, s.qvelFilter, "qvel_filter");
149 /// keypoint related
150 config.numPoints = getValueWithDefault<int>(c, s.numPoints, "num_points");
151 config.keypointKp = getEigenVecWithDefault(c, s.keypointKp, "keypoint_stiffness");
152 config.keypointKd = getEigenVecWithDefault(c, s.keypointKd, "keypoint_damping");
154 getEigenVecWithDefault(c, s.currentKeypointPosition, "current_keypoint_position");
156 getEigenVecWithDefault(c, config.currentKeypointPosition, "desired_keypoint_position");
157 config.desiredKeypointVelocity.setZero(config.numPoints * 3);
159 getEigenVecWithDefault(c, config.desiredKeypointVelocity, "desired_keypoint_velocity");
161 getValueWithDefault<float>(c, s.keypointPositionFilter, "keypoint_position_filter");
163 getValueWithDefault<float>(c, s.keypointVelocityFilter, "keypoint_velocity_filter");
164 config.isRigid = getValueWithDefault<bool>(c, s.isRigid, "is_rigid");
165 config.fixedTranslation =
166 getEigenVecWithDefault(c, s.fixedTranslation, "fixed_translation");
167 return config;
168 }
169
170 void
171 KeypointsAdmittanceController::preactivateInit(const VirtualRobot::RobotNodeSetPtr& rns)
172 {
173 Eigen::Matrix4f currentPose = rns->getTCP()->getPoseInRootFrame();
174 s.qpos = rns->getJointValuesEigen();
175 s.qvel.setZero(numOfJoints);
176 s.nullspaceTorque.setZero(numOfJoints);
177 s.desiredJointTorques.setZero(numOfJoints);
178
179 if (s.isRigid)
180 {
181 for (int i = 0; i < s.numPoints; i++)
182 {
183 s.currentKeypointPosition.segment(3 * i, 3) = currentPose.block(0, 3, 3, 1);
184 }
185 s.currentKeypointPosition = s.currentKeypointPosition + s.fixedTranslation;
186 }
187
188 s.previousDesiredPose = currentPose;
189 s.desiredPose = currentPose;
190 s.desiredVel.setZero();
191 s.desiredAcc.setZero();
192
193 s.currentPose = currentPose;
194 s.currentTwist.setZero();
195 s.forceImpedance.setZero();
196
197 s.virtualPose = currentPose;
198 s.virtualVel.setZero();
199 s.virtualAcc.setZero();
200
201 s.pointTrackingForce.setZero();
202 s.filteredKeypointPosition = s.currentKeypointPosition;
203 s.previousKeypointPosition = s.currentKeypointPosition;
204 s.currentKeypointVelocity.setZero(s.numPoints * 3);
205 s.desiredKeypointVelocity.setZero(s.numPoints * 3);
206
207 if (enablePreactivateInit.load())
208 {
209 s.desiredNullspaceJointAngles = rns->getJointValuesEigen();
210 }
211 }
212
213 void
215 {
216 s.previousDesiredPose = s.currentPose;
217 s.desiredPose = s.currentPose;
218 s.desiredVel.setZero();
219 s.desiredAcc.setZero();
220
221 s.virtualPose = s.currentPose;
222 s.virtualVel.setZero();
223 s.virtualAcc.setZero();
224 }
225
226 bool
228 {
229 bool valid = false;
230 if ((s.previousDesiredPose.block<3, 1>(0, 3) - targetPose.block<3, 1>(0, 3)).norm() >
231 100.0f)
232 {
233 ARMARX_RT_LOGF_WARN("new target \n\n %s\n\nis too far away from\n\n %s",
234 VAROUT(targetPose),
235 VAROUT(s.previousDesiredPose));
236 // ARMARX_WARNING << "new target \n\n" << targetPose << "\n\nis too far away from\n\n" << VAROUT(s.previousDesiredPose);
237 targetPose = s.previousDesiredPose;
238 s.currentForceTorque.setZero();
239 }
240 else
241 {
242 s.previousDesiredPose = targetPose;
243 valid = true;
244 }
245 return valid;
246 }
247
248 bool
250 const Config& cfg,
251 const IceUtil::Time& timeSinceLastIteration,
252 std::vector<const SensorValue1DoFActuatorTorque*> torqueSensors,
253 std::vector<const SensorValue1DoFActuatorVelocity*> velocitySensors,
254 std::vector<const SensorValue1DoFActuatorPosition*> positionSensors)
255 {
256 /// check size and value
257 {
258 ARMARX_CHECK_EQUAL(numOfJoints, torqueSensors.size());
259 ARMARX_CHECK_EQUAL(numOfJoints, velocitySensors.size());
260 ARMARX_CHECK_EQUAL(numOfJoints, positionSensors.size());
261
262 ARMARX_CHECK_EQUAL(numOfJoints, cfg.kpNullspaceTorque.size());
263 ARMARX_CHECK_EQUAL(numOfJoints, cfg.kdNullspaceTorque.size());
264 ARMARX_CHECK_EQUAL(numOfJoints, cfg.desiredNullspaceJointAngles.size());
266
267 ARMARX_CHECK_EQUAL(cfg.numPoints * 3, cfg.keypointKp.size());
268 ARMARX_CHECK_EQUAL(cfg.numPoints * 3, cfg.keypointKd.size());
272 ARMARX_CHECK_EQUAL(cfg.numPoints * 3, cfg.fixedTranslation.size());
273 }
274
275 /// ----------------------------- get target status from config ---------------------------------------------
276 s.kpImpedance = cfg.kpImpedance;
277 s.kdImpedance = cfg.kdImpedance;
278 s.kpAdmittance = cfg.kpAdmittance;
279 s.kdAdmittance = cfg.kdAdmittance;
280 s.kmAdmittance = cfg.kmAdmittance;
281 s.kpNullspaceTorque = cfg.kpNullspaceTorque;
282 s.kdNullspaceTorque = cfg.kdNullspaceTorque;
283
284 s.torqueLimit = cfg.torqueLimit;
285 s.qvelFilter = cfg.qvelFilter;
286
287 s.currentForceTorque = cfg.currentForceTorque;
288 s.desiredNullspaceJointAngles = cfg.desiredNullspaceJointAngles;
289
290 /// update keypoints related variables
291 s.numPoints = cfg.numPoints;
292 s.keypointKp = cfg.keypointKp;
293 s.keypointKd = cfg.keypointKd;
294
295 s.currentKeypointPosition = cfg.currentKeypointPosition;
296 s.desiredKeypointPosition = cfg.desiredKeypointPosition;
297 s.desiredKeypointVelocity = cfg.desiredKeypointVelocity;
298 s.keypointPositionFilter = cfg.keypointPositionFilter;
299 s.keypointVelocityFilter = cfg.keypointVelocityFilter;
300
301 s.isRigid = cfg.isRigid;
302 s.fixedTranslation = cfg.fixedTranslation;
303
304 /// ----------------------------- get current status of robot ---------------------------------------------
305 /// original data in ArmarX use the following units:
306 /// position in mm,
307 /// joint angles and orientation in radian,
308 /// velocity in mm/s and radian/s, etc.
309 /// here we convert mm to m, if you use MP from outside, make sure to convert it back to mm
310
311 s.currentPose = tcp->getPoseInRootFrame();
312 Eigen::MatrixXf jacobi =
313 ik->getJacobianMatrix(tcp, VirtualRobot::IKSolver::CartesianSelection::All);
314 jacobi.block(0, 0, 3, numOfJoints) = 0.001 * jacobi.block(0, 0, 3, numOfJoints);
315 ARMARX_CHECK_EQUAL(numOfJoints, jacobi.cols());
316 ARMARX_CHECK_EQUAL(6, jacobi.rows());
317 s.jacobi = jacobi;
318
319 Eigen::VectorXf qvelRaw(numOfJoints);
320 for (size_t i = 0; i < velocitySensors.size(); ++i)
321 {
322 s.qpos(i) = positionSensors[i]->position;
323 qvelRaw(i) = velocitySensors[i]->velocity;
324 }
325
326 s.qvel = (1 - cfg.qvelFilter) * s.qvel + cfg.qvelFilter * qvelRaw;
327 s.currentTwist = jacobi * s.qvel;
328 s.deltaT = timeSinceLastIteration.toSecondsDouble();
329
330 /// ----------------------------- keypoint related ---------------------------------------------
331 /// compute (filtered) keypoint position
332 if (s.isRigid)
333 {
334 for (int i = 0; i < s.numPoints; i++)
335 {
336 s.currentKeypointPosition.segment(3 * i, 3) =
337 s.currentPose.block(0, 0, 3, 3) * s.fixedTranslation.segment(3 * i, 3) +
338 s.currentPose.block<3, 1>(0, 3);
339 }
340 }
341 ARMARX_CHECK_EQUAL(s.filteredKeypointPosition.size(), s.currentKeypointPosition.size());
342 s.filteredKeypointPosition =
343 (1.0f - s.keypointPositionFilter) * s.filteredKeypointPosition +
344 s.keypointPositionFilter * s.currentKeypointPosition;
345 /// compute filtered keypoint velocity
346 if (s.isRigid)
347 {
348 Eigen::VectorXf currentKeypointVelocity;
349 currentKeypointVelocity.setZero(s.numPoints * 3);
350 for (int i = 0; i < s.numPoints; i++)
351 {
352 Eigen::Vector3f angular_vel = s.currentTwist.tail<3>();
353 Eigen::Vector3f dist = s.fixedTranslation.segment(3 * i, 3);
354 currentKeypointVelocity.segment(3 * i, 3) =
355 angular_vel.cross(dist) + s.currentTwist.head<3>();
356 }
357 s.currentKeypointVelocity =
358 (1.0f - s.keypointVelocityFilter) * s.currentKeypointVelocity +
359 s.keypointVelocityFilter * currentKeypointVelocity;
360 }
361 else
362 {
363 s.currentKeypointVelocity =
364 (1.0f - s.keypointVelocityFilter) * s.currentKeypointVelocity +
365 s.keypointVelocityFilter *
366 (s.filteredKeypointPosition - s.previousKeypointPosition) / s.deltaT;
367 }
368 s.previousKeypointPosition = s.filteredKeypointPosition;
369
370 /// ----------------------------- compute keypoint tracking force ---------------------------------------------
371 auto difference = s.desiredKeypointPosition - s.filteredKeypointPosition;
372 /// in theory, we can also add s.desiredKeypointVelocity to the damping term
373 Eigen::VectorXf trackingForce = difference.cwiseProduct(s.keypointKp) -
374 s.currentKeypointVelocity.cwiseProduct(s.keypointKd);
375 if (trackingForce.size() != 3 * s.numPoints)
376 {
377 trackingForce.setZero(3 * s.numPoints);
378 }
379
380 s.pointTrackingForce.setZero();
381 for (int i = 0; i < s.numPoints; i++)
382 {
383 Eigen::Vector3f dist =
384 s.filteredKeypointPosition.segment(3 * i, 3) - s.currentPose.block<3, 1>(0, 3);
385 dist = dist * 0.001;
386 Eigen::Vector3f force = trackingForce.segment(3 * i, 3);
387 if (force.norm() > 200)
388 {
389 ARMARX_RT_LOGF_WARN("force too large, set to zero");
390 force.setZero();
391 }
392 s.pointTrackingForce.head<3>() += force;
393 s.pointTrackingForce.tail<3>() += dist.cross(force);
394 }
395 ARMARX_CHECK_EQUAL(s.currentForceTorque.size(), s.pointTrackingForce.size());
396 Eigen::Vector6f acc = s.kmAdmittance.cwiseProduct(s.pointTrackingForce) -
397 s.kdAdmittance.cwiseProduct(s.desiredVel);
398
399 Eigen::Vector6f vel = s.desiredVel + 0.5 * s.deltaT * (acc + s.desiredAcc);
400 Eigen::VectorXf deltaPose = 0.5 * s.deltaT * (vel + s.desiredVel);
401 s.desiredAcc = acc;
402 s.desiredVel = vel;
403
404 Eigen::Matrix3f deltaPoseMat =
405 VirtualRobot::MathTools::rpy2eigen3f(deltaPose(3), deltaPose(4), deltaPose(5));
406 s.desiredPose.block<3, 1>(0, 3) += deltaPose.head<3>();
407 s.desiredPose.block<3, 3>(0, 0) = deltaPoseMat * s.desiredPose.block<3, 3>(0, 0);
408
409 return validateTargetPose(s.desiredPose);
410 }
411
412 void
414 std::vector<ControlTarget1DoFActuatorTorque*> targets)
415 {
416 /// only when the ft sensor is calibrated can we allow the admittance controller
417 /// to have virtual pose modulation out of force/torque information.
418 if (!rtReady)
419 {
420 s.desiredPose = s.previousDesiredPose;
421 s.desiredVel.setZero();
422 s.kmAdmittance.setZero();
423 }
424 /// ---------------------------- admittance control ---------------------------------------------
425 /// calculate pose error between the virtual pose and the target pose
426 Eigen::Matrix3f objDiffMat =
427 s.virtualPose.block<3, 3>(0, 0) * s.desiredPose.block<3, 3>(0, 0).transpose();
428 Eigen::Vector6f poseError;
429 poseError.head<3>() = s.virtualPose.block<3, 1>(0, 3) - s.desiredPose.block<3, 1>(0, 3);
430 poseError.tail<3>() = VirtualRobot::MathTools::eigen3f2rpy(objDiffMat);
431
432 /// admittance control law and Euler Integration -> virtual pose
433 Eigen::Vector6f acc = s.kmAdmittance.cwiseProduct(s.currentForceTorque) -
434 s.kpAdmittance.cwiseProduct(poseError) -
435 s.kdAdmittance.cwiseProduct(s.virtualVel);
436
437 Eigen::Vector6f vel = s.virtualVel + 0.5 * s.deltaT * (acc + s.virtualAcc);
438 Eigen::VectorXf deltaPose = 0.5 * s.deltaT * (vel + s.virtualVel);
439 s.virtualAcc = acc;
440 s.virtualVel = vel;
441
442 Eigen::Matrix3f deltaPoseMat =
443 VirtualRobot::MathTools::rpy2eigen3f(deltaPose(3), deltaPose(4), deltaPose(5));
444 s.virtualPose.block<3, 1>(0, 3) += deltaPose.head<3>();
445 s.virtualPose.block<3, 3>(0, 0) = deltaPoseMat * s.virtualPose.block<3, 3>(0, 0);
446
447 /// ----------------------------- Impedance control ---------------------------------------------
448 /// calculate pose error between target pose and current pose
449 /// !!! This is very important: you have to keep postion and orientation both
450 /// with UI unit (meter, radian) to calculate impedance force.
451
452 Eigen::Matrix3f diffMat =
453 s.virtualPose.block<3, 3>(0, 0) * s.currentPose.block<3, 3>(0, 0).transpose();
454 Eigen::Vector6f poseErrorImp;
455 poseErrorImp.head<3>() =
456 0.001 * (s.virtualPose.block<3, 1>(0, 3) - s.currentPose.block<3, 1>(0, 3));
457 poseErrorImp.tail<3>() = VirtualRobot::MathTools::eigen3f2rpy(diffMat);
458 s.forceImpedance =
459 s.kpImpedance.cwiseProduct(poseErrorImp) - s.kdImpedance.cwiseProduct(s.currentTwist);
460
461 /// ----------------------------- Nullspace PD Control --------------------------------------------------
462 Eigen::VectorXf nullspaceTorque =
463 s.kpNullspaceTorque.cwiseProduct(s.desiredNullspaceJointAngles - s.qpos) -
464 s.kdNullspaceTorque.cwiseProduct(s.qvel);
465
466 /// ----------------------------- Map TS target force to JS --------------------------------------------------
467 const Eigen::MatrixXf jtpinv =
468 ik->computePseudoInverseJacobianMatrix(s.jacobi.transpose(), lambda);
469 s.desiredJointTorques = s.jacobi.transpose() * s.forceImpedance +
470 (I - s.jacobi.transpose() * jtpinv) * nullspaceTorque;
471
472 ARMARX_CHECK_EQUAL(targets.size(), (unsigned)s.desiredJointTorques.size());
473 for (size_t i = 0; i < targets.size(); ++i)
474 {
475 s.desiredJointTorques(i) =
476 std::clamp(s.desiredJointTorques(i), -s.torqueLimit, s.torqueLimit);
477 targets.at(i)->torque = s.desiredJointTorques(i);
478 if (!targets.at(i)->isValid())
479 {
480 targets.at(i)->torque = 0;
481 }
482 }
483 }
484} // namespace armarx::control::common::control_law
#define ARMARX_RT_LOGF_WARN(...)
#define VAROUT(x)
constexpr T c
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
bool updateControlStatus(const Config &cfg, const IceUtil::Time &timeSinceLastIteration, std::vector< const SensorValue1DoFActuatorTorque * > torqueSensors, std::vector< const SensorValue1DoFActuatorVelocity * > velocitySensors, std::vector< const SensorValue1DoFActuatorPosition * > positionSensors)
void initialize(const VirtualRobot::RobotNodeSetPtr &rns, const std::string &configFileName)
void run(bool rtReady, std::vector< ControlTarget1DoFActuatorTorque * > targets)
Brief description of class targets.
Definition targets.h:39
#define ARMARX_CHECK_GREATER_EQUAL(lhs, rhs)
This macro evaluates whether lhs is greater or equal (>=) rhs and if it turns out to be false it will...
#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...
#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:179
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:188
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:194
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:191
Matrix< float, 6, 1 > Vector6f
Eigen::VectorXf getEigenVec(nlohmann::json &userConfig, nlohmann::json &defaultConfig, const std::string &entryName, int size, int value)
Definition utils.cpp:50
T getValueWithDefault(nlohmann::json &userConfig, T defaultValue, const std::string &entryName)
Definition utils.h:96
T getValue(nlohmann::json &userConfig, nlohmann::json &defaultConfig, const std::string &entryName)
Definition utils.h:80
Eigen::VectorXf getEigenVecWithDefault(nlohmann::json &userConfig, Eigen::VectorXf defaultValue, const std::string &entryName)
Definition utils.cpp:103
you can set the following values from outside of the rt controller via Ice interfaces