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