WipingVelController.cpp
Go to the documentation of this file.
1 /**
2  * This file is part of ArmarX.
3  *
4  * ArmarX is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * ArmarX is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * @author Jianfeng Gao ( jianfeng dot gao at kit dot edu )
17  * @date 2024
18  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
19  * GNU General Public License
20  */
21 
22 #include "WipingVelController.h"
23 
25 #include <RobotAPI/components/units/RobotUnit/RobotUnit.h> // FIXME avoid this
26 
28 
30 {
31  NJointControllerRegistration<NJointWipingVelMPController>
32  registrationControllerNJointWipingVelMPController("NJointWipingVelMPController");
33 
35  const RobotUnitPtr& robotUnit,
36  const NJointControllerConfigPtr& config,
37  const VirtualRobot::RobotPtr& robot) :
39  {
40  ARMARX_IMPORTANT << getClassName() + " construction done";
41  }
42 
43  std::string
44  NJointWipingVelMPController::getClassName(const Ice::Current&) const
45  {
46  return "NJointWipingVelMPController";
47  }
48 
49  void
51  const Ice::Current& iceCurrent)
52  {
54  {
55  for (auto& pair : limb)
56  {
57  pair.second->rtFirstRun.store(true);
58  }
59  }
60  }
61 
62  void
64  const Ice::Current& iceCurrent)
65  {
66  adaptionCfg = arondto::WipingAdaptionConfigs::FromAron(dto);
67  for (auto& pair : limb)
68  {
69  auto search = adaptionCfg.cfg.find(pair.first);
70  if (search == adaptionCfg.cfg.end())
71  {
72  ARMARX_ERROR << "Arm " << pair.first << " is not included in the adaption config";
73  }
74  // ARMARX_CHECK_NOT_EQUAL(search, adaptionCfg.cfg.end());
75  }
76  adaptionReady.store(true);
77  }
78 
79  void
81  {
83  // for (auto& pair: limb)
84  // {
85  // adaptionCfg.cfg[pair.first] = arondto::WipingAdaptionConfigs();
86  // }
87  for (auto& pair : limb)
88  {
89  adaptionStatus[pair.first] = AdaptionStatus();
90  adaptionStatus[pair.first].lastPosition =
91  common::getPos(pair.second->rtStatus.currentPose).head<2>();
92  }
93  }
94 
95  void
97  {
98  if (not adaptionReady.load())
99  {
100  return;
101  }
102  std::lock_guard<std::recursive_mutex> lock(mtx_mps);
103  bool rtSafe = additionalTaskUpdateStatus();
104 
105  // when ft guard is enabled and rt is not safe due to ft trigger, then we stop the corresponding mp
106  std::map<std::string, bool> flagMPToStop;
107  // double deltaT = 0.0;
108  for (auto& pair : limb)
109  {
110  flagMPToStop[pair.first] = not limb.at(pair.first)->controller.ftsensor.ftSafe.load();
111  pair.second->nonRTDeltaT =
112  pair.second->rtStatusInNonRT.accumulateTime - pair.second->nonRTAccumulateTime;
113  // deltaT = pair.second->nonRTDeltaT;
114  pair.second->nonRTAccumulateTime = pair.second->rtStatusInNonRT.accumulateTime;
115  }
116 
117  std::map<std::string, bool> mpRunning;
118  for (auto& _mp : mps)
119  {
120  const auto mpNodeSet = _mp.second.mp->getNodeSetName();
121  mpRunning[_mp.second.mp->getMPName()] = _mp.second.mp->isRunning();
122 
123  auto search = flagMPToStop.find(mpNodeSet);
124  if (search != flagMPToStop.end() and flagMPToStop.at(mpNodeSet) and
125  _mp.second.mp->isRunning() and
126  (limb.at(mpNodeSet)->controller.ftsensor.isForceGuardEnabled() or
127  limb.at(mpNodeSet)->controller.ftsensor.isTorqueGuardEnabled()))
128  {
129  _mp.second.mp->stop();
130  ARMARX_INFO << "Due to force/torque safe guard, MP " << _mp.second.mp->getMPName()
131  << " is stopped at " << _mp.second.mp->getCanonicalValue();
132  }
133 
134  if (_mp.second.mp->isFinished())
135  {
136  continue;
137  }
138  if (_mp.second.mp->getRole() == "taskspace")
139  {
140  auto& arm = limb.at(_mp.second.mp->getNodeSetName());
141  if (_mp.second.mp->isFirstRun())
142  {
143  ARMARX_INFO << "checking TSMP initial status ...";
144  /// Here we check if the TSMP start pose is too far away from the current pose, if so, we would
145  /// prefer to reset the mp start from the current pose.
146  const std::vector<double> mpStart = _mp.second.mp->getStartVec();
147  const auto mpStartPose = common::dVecToMat4(mpStart);
148  if (_mp.second.mp->getStartFromPrevTarget() or
150  arm->rtStatusInNonRT.currentPose,
151  mpStartPose,
152  "current pose",
153  "TSMP initial start pose",
154  arm->nonRtConfig.safeDistanceMMToGoal,
155  arm->nonRtConfig.safeRotAngleDegreeToGoal,
156  _mp.second.mp->getMPName() + "mp_set_target"))
157  {
158  if (_mp.second.mp->getStartFromPrevTarget())
159  {
160  ARMARX_INFO << "User requested to start the current TS MP from the "
161  "previous target pose";
162  }
163  else
164  {
166  << "deviation from current pose too large, reset MP start pose";
167  }
168  _mp.second.mp->validateInitialState(
169  common::mat4ToDVec(arm->rtStatusInNonRT.desiredPose));
170  }
171  ARMARX_INFO << "done";
172  }
173  mp::TSMPInputPtr in = std::dynamic_pointer_cast<mp::TSMPInput>(_mp.second.input);
174  in->pose = arm->rtStatusInNonRT.currentPose;
175  in->vel = arm->rtStatusInNonRT.currentTwist;
176  // in->deltaT = arm->rtStatusInNonRT.deltaT;
177  in->deltaT = arm->nonRTDeltaT;
178  }
179  else if (_mp.second.mp->getRole() == "nullspace")
180  {
181  auto& arm = limb.at(_mp.second.mp->getNodeSetName());
182  mp::JSMPInputPtr in = std::dynamic_pointer_cast<mp::JSMPInput>(_mp.second.input);
183  ARMARX_CHECK_EQUAL(arm->rtStatusInNonRT.numJoints,
184  arm->rtStatusInNonRT.jointPos.size());
185  in->angleRadian = arm->rtStatusInNonRT.jointPos;
186  in->angularVel = arm->rtStatusInNonRT.qvelFiltered;
187  // in->deltaT = arm->rtStatusInNonRT.deltaT;
188  in->deltaT = arm->nonRTDeltaT;
189  }
190  else if (_mp.second.mp->getRole() == "hand")
191  {
192  auto& hand = hands->hands.at(_mp.second.mp->getNodeSetName());
193  mp::JSMPInputPtr in = std::dynamic_pointer_cast<mp::JSMPInput>(_mp.second.input);
194  in->angleRadian =
195  hand->bufferRTToNonRT.getUpToDateReadBuffer().jointPosition.value();
196  hand->nonRTDeltaT = hand->bufferRTToNonRT.getReadBuffer().accumulateTime -
197  hand->nonRTAccumulateTime;
198  hand->nonRTAccumulateTime = hand->bufferRTToNonRT.getReadBuffer().accumulateTime;
199  // in->deltaT = hand->bufferRTToNonRT.getReadBuffer().deltaT;
200  in->deltaT = hand->nonRTDeltaT;
201  }
202  }
203  if (not rtSafe)
204  {
206 
207  // return;
208  // Make sure that you do not call additionalTaskSetTarget before return;
209  // since we overwrite the arm->nonRtConfig in additionalTaskUpdateStatus() with user config, which does not
210  // align with the up-to-date mp status. userConfig is only updated to align with MP status everytime a MP
211  // finishes execution. Only then, user can use this controller as if no mp is there.
212  }
213  ARMARX_INFO << VAROUT(rtSafe) << deactivateSpam(0.5);
214  runMPs(rtSafe);
215  std::lock_guard<std::recursive_mutex> adaptLock(adaptionMtx);
216  /// set mp target to nonRT config data structure
217  for (auto& _mp : mps)
218  {
219  if (not mpRunning.at(_mp.second.mp->getMPName()))
220  {
221  continue;
222  }
223  for (auto& ftGuard : mpConfig.ftGuard)
224  {
225  if (ftGuard.mpName == _mp.second.mp->getMPName())
226  {
227  bool const forceGuard =
228  (ftGuard.force.has_value() and
229  ftGuard.force.value() >= _mp.second.mp->getCanonicalValue());
230  bool const torqueGuard =
231  (ftGuard.torque.has_value() and
232  ftGuard.torque.value() >= _mp.second.mp->getCanonicalValue());
233 
234  auto& arm = limb.at(_mp.second.mp->getNodeSetName());
235  bool const resetForce =
236  not arm->controller.ftsensor.isForceGuardEnabled() and forceGuard;
237  bool const resetTorque =
238  not arm->controller.ftsensor.isTorqueGuardEnabled() and torqueGuard;
239  if (resetForce or resetTorque)
240  {
241  ARMARX_INFO << "Triggering force torque safety guard for "
242  << arm->kinematicChainName << " at can value "
243  << _mp.second.mp->getCanonicalValue();
244  }
245 
246  arm->nonRtConfig.ftConfig.enableSafeGuardForce = forceGuard;
247  arm->nonRtConfig.ftConfig.enableSafeGuardTorque = torqueGuard;
248  arm->controller.ftsensor.enableSafeGuard(resetForce, resetTorque);
249  }
250  }
251 
252  if (_mp.second.mp->getRole() == "taskspace")
253  {
254  auto& nodeSetName = _mp.second.mp->getNodeSetName();
255  auto& cfg = adaptionCfg.cfg.at(nodeSetName);
256  auto& s = adaptionStatus.at(nodeSetName);
257  auto& arm = limb.at(nodeSetName);
258  mp::TSMPOutputPtr out =
259  std::dynamic_pointer_cast<mp::TSMPOutput>(_mp.second.output);
260 
261  /// Note that here we only report warning but still set target to RT, we rely on MP phase stop and
262  /// RT safety mechanism to stop or pause the action if needed
264  arm->rtStatusInNonRT.currentPose,
265  arm->nonRtConfig.desiredPose,
266  "current pose",
267  "desiredPose for RT",
268  arm->nonRtConfig.safeDistanceMMToGoal,
269  arm->nonRtConfig.safeRotAngleDegreeToGoal,
270  _mp.second.mp->getMPName() + "mp_set_target");
271  arm->nonRtConfig.desiredPose = out->pose;
272  arm->nonRtConfig.desiredTwist = out->vel;
273 
274  /// Note: use force to generate acceleration target and aggregate to the target velocity
275  float desiredVelZ =
276  cfg.kpf * (cfg.targetForce - arm->rtStatusInNonRT.currentForceTorque(2));
277  arm->nonRtConfig.desiredTwist(2) -= desiredVelZ;
278  arm->nonRtConfig.desiredTwist.tail<3>().setZero();
279 
280  // float dT = static_cast<float>(arm->rtStatusInNonRT.deltaT);
281  float dT = static_cast<float>(arm->nonRTDeltaT);
282 
283  /// check phase stop
284  auto& adaptKVel = arm->nonRtConfig.kpCartesianVel;
285  if (not cfg.originalKpVel.has_value())
286  {
287  cfg.originalKpVel = adaptKVel;
288  }
289  Eigen::Vector6f originalKpVel = cfg.originalKpVel.value();
290  if (tooFar)
291  {
292  Eigen::Vector2f currentForceXY =
293  arm->rtStatusInNonRT.currentForceTorque.head<2>();
294  Eigen::Vector2f dragForce =
295  currentForceXY - cfg.deadZoneForce * currentForceXY / currentForceXY.norm();
296  adaptKVel.head<2>() =
297  (adaptKVel.head<2>().array() - dT * cfg.adaptForceCoeff * dragForce.norm())
298  .max(0.0f);
299  // lastDiff = diff;
300  }
301  else
302  {
303  adaptKVel.head<2>() = (adaptKVel.head<2>().array() + dT * cfg.adaptCoeff)
304  .min(originalKpVel.head<2>().array());
305  }
306  adaptKVel(2) = originalKpVel(2);
307 
308  /// Note: integrate the target pose
309  arm->nonRtConfig.desiredPose.block<3, 1>(0, 3) +=
310  dT * arm->nonRtConfig.desiredTwist.head<3>();
311 
312  /// Note: checking the duration that arm is tooFar to indicate a new location for intended wiping
313  if (tooFar)
314  {
315  Eigen::Vector2f currentXY = arm->rtStatusInNonRT.currentPose.block<2, 1>(0, 3);
316  if ((s.lastPosition - currentXY).norm() < cfg.changePositionTolerance)
317  {
318  s.changeTimer += dT;
319  }
320  else
321  {
322  s.lastPosition = currentXY;
323  s.changeTimer = 0;
324  }
325 
326  if (s.changeTimer > cfg.changeTimerThreshold)
327  {
328  arm->nonRtConfig.desiredPose.block<2, 1>(0, 3) = currentXY;
329  // isPhaseStop = false; /// Check if desiredPose above in report deviation is correct
330  s.changeTimer = 0;
331  }
332  }
333  else
334  {
335  s.changeTimer = 0;
336  }
337  }
338  else if (_mp.second.mp->getRole() == "nullspace")
339  {
340  auto& arm = limb.at(_mp.second.mp->getNodeSetName());
341  mp::JSMPOutputPtr out =
342  std::dynamic_pointer_cast<mp::JSMPOutput>(_mp.second.output);
343  ARMARX_CHECK_EQUAL(arm->rtStatusInNonRT.numJoints, out->angleRadian.size());
344  arm->nonRtConfig.desiredNullspaceJointAngles.value() = out->angleRadian;
345  }
346  else if (_mp.second.mp->getRole() == "hand")
347  {
348  mp::JSMPOutputPtr out =
349  std::dynamic_pointer_cast<mp::JSMPOutput>(_mp.second.output);
350  auto& hand = hands->hands.at(_mp.second.mp->getNodeSetName());
351  mp::JSMPInputPtr in = std::dynamic_pointer_cast<mp::JSMPInput>(_mp.second.input);
352  ARMARX_CHECK_EQUAL(hand->targetNonRT.jointPosition.value().size(),
353  out->angleRadian.size());
354  hand->targetNonRT.jointPosition.value() = out->angleRadian;
355  }
356  if (mpRunning.at(_mp.second.mp->getMPName()) and _mp.second.mp->isFinished())
357  {
358  ARMARX_INFO << "reset buffer for nonRtConfig of " << _mp.second.mp->getMPName();
359  for (auto& pair : limb)
360  {
361  auto& arm = pair.second;
362  arm->bufferConfigUserToNonRt.reinitAllBuffers(arm->nonRtConfig);
363  }
364  for (auto& pair : limb)
365  {
366  userConfig.limbs.at(pair.first) = pair.second->nonRtConfig;
367  }
368  if (hands)
369  {
370  hands->reinitBuffer(userConfig.hands);
371  }
372  }
373  }
375  }
376 } // namespace armarx::control::njoint_mp_controller::task_space
armarx::control::njoint_controller::task_space::NJointTaskspaceVelocityController::handleRTNotSafeInNonRT
void handleRTNotSafeInNonRT()
Definition: VelocityController.cpp:215
armarx::control::njoint_mp_controller::task_space::NJointWipingVelMPController::getClassName
std::string getClassName(const Ice::Current &=Ice::emptyCurrent) const override
Definition: WipingVelController.cpp:44
ARMARX_IMPORTANT
#define ARMARX_IMPORTANT
Definition: Logging.h:190
armarx::control::njoint_controller::task_space::NJointTaskspaceVelocityController::additionalTaskUpdateStatus
bool additionalTaskUpdateStatus()
Definition: VelocityController.cpp:183
armarx::control::common::mp::JSMPInputPtr
std::shared_ptr< JSMPInput > JSMPInputPtr
Definition: JSMP.h:47
RobotUnit.h
armarx::control::common::detectAndReportPoseDeviationWarning
bool detectAndReportPoseDeviationWarning(const Eigen::Matrix4f &pose1, const Eigen::Matrix4f &pose2, const std::string &namePose1, const std::string &namePose2, float positionThrMM, float angleThrDeg, const std::string &who)
Definition: utils.cpp:646
armarx::control::njoint_controller::task_space::NJointTaskspaceVelocityController::additionalTaskSetTarget
void additionalTaskSetTarget()
Definition: VelocityController.cpp:202
armarx::max
std::vector< T > max(const std::vector< T > &v1, const std::vector< T > &v2)
Definition: VectorHelpers.h:297
armarx::control::njoint_mp_controller::task_space::NJointWipingVelMPController::AdaptionStatus
Definition: WipingVelController.h:54
armarx::control::common::mp::MPPool::runMPs
void runMPs(const bool rtSafe)
Definition: MPPool.cpp:87
armarx::control::njoint_controller::task_space::NJointTaskspaceVelocityController::robotUnit
RobotUnitPtr robotUnit
Definition: VelocityController.h:163
armarx::control::njoint_controller::task_space::NJointTaskspaceVelocityController::rtPreActivateController
void rtPreActivateController() override
This function is called before the controller is activated.
Definition: VelocityController.cpp:604
armarx::control::common::mp::JSMPOutputPtr
std::shared_ptr< JSMPOutput > JSMPOutputPtr
Definition: JSMP.h:48
WipingVelController.h
armarx::control::njoint_mp_controller::task_space::registrationControllerNJointWipingVelMPController
NJointControllerRegistration< NJointWipingVelMPController > registrationControllerNJointWipingVelMPController("NJointWipingVelMPController")
armarx::control::common::mp::MPPool::resetMPs
bool resetMPs(const ::armarx::aron::data::dto::DictPtr &dto, const std::map< std::string, VirtualRobot::RobotNodeSetPtr > &rnsMap)
Definition: MPPool.cpp:503
armarx::control::njoint_mp_controller::task_space::NJointWipingVelMPController::updateAdaptiveConfig
void updateAdaptiveConfig(const ::armarx::aron::data::dto::DictPtr &dto, const Ice::Current &iceCurrent=Ice::emptyCurrent) override
Definition: WipingVelController.cpp:63
armarx::control::common::mp::TSMPOutputPtr
std::shared_ptr< TSMPOutput > TSMPOutputPtr
Definition: TSMP.h:47
armarx::control::njoint_controller::task_space::NJointTaskspaceVelocityController::userConfig
ConfigDict userConfig
Definition: VelocityController.h:164
armarx::control::njoint_controller::task_space::NJointTaskspaceVelocityController
Brief description of class NJointTaskspaceVelocityController.
Definition: VelocityController.h:49
armarx::control::common::getPos
Eigen::Block< Eigen::Matrix4f, 3, 1 > getPos(Eigen::Matrix4f &matrix)
Definition: utils.cpp:212
armarx::control::common::mp::MPPool::mtx_mps
std::recursive_mutex mtx_mps
Definition: MPPool.h:128
armarx::control::common::dVecToMat4
Eigen::Matrix4f dVecToMat4(const mplib::core::DVec &dvec)
create Eigen:Matrix4f from 7D double vector.
Definition: utils.cpp:230
ARMARX_ERROR
#define ARMARX_ERROR
Definition: Logging.h:196
utils.h
armarx::control::njoint_mp_controller::task_space
This file is part of ArmarX.
Definition: AdmittanceController.cpp:29
armarx::control::njoint_mp_controller::task_space::NJointWipingVelMPController::additionalTask
void additionalTask() override
Definition: WipingVelController.cpp:96
armarx::control::common::MPType::hand
@ hand
armarx::aron::data::DictPtr
std::shared_ptr< Dict > DictPtr
Definition: Dict.h:41
armarx::control::njoint_controller::task_space::NJointTaskspaceVelocityController::limb
std::map< std::string, ArmPtr > limb
Definition: VelocityController.h:161
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:181
VAROUT
#define VAROUT(x)
Definition: StringHelpers.h:198
IceUtil::Handle< class RobotUnit >
armarx::min
std::vector< T > min(const std::vector< T > &v1, const std::vector< T > &v2)
Definition: VectorHelpers.h:327
armarx::control::njoint_mp_controller::task_space::NJointWipingVelMPController::rtPreActivateController
void rtPreActivateController() override
This function is called before the controller is activated.
Definition: WipingVelController.cpp:80
armarx::control::common::mat4ToDVec
mplib::core::DVec mat4ToDVec(const Eigen::Matrix4f &mat)
convert Eigen:Matrix4f to 7D double vector.
Definition: utils.cpp:252
armarx::Logging::deactivateSpam
SpamFilterDataPtr deactivateSpam(float deactivationDurationSec=10.0f, const std::string &identifier="", bool deactivate=true) const
disables the logging for the current line for the given amount of seconds.
Definition: Logging.cpp:99
Eigen::Matrix< float, 6, 1 >
armarx::control::njoint_controller::task_space::NJointTaskspaceVelocityController::hands
core::HandControlPtr hands
Definition: VelocityController.h:165
armarx::control::common::mp::MPPool::mps
std::map< std::string, MPInputOutput > mps
Definition: MPPool.h:127
armarx::control::common::mp::TSMPInputPtr
std::shared_ptr< TSMPInput > TSMPInputPtr
Definition: TSMP.h:46
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::njoint_controller::task_space::NJointTaskspaceVelocityController::controllableNodeSets
std::map< std::string, VirtualRobot::RobotNodeSetPtr > controllableNodeSets
Definition: VelocityController.h:166
armarx::ctrlutil::s
double s(double t, double s0, double v0, double a0, double j)
Definition: CtrlUtil.h:33
armarx::control::njoint_mp_controller::task_space::NJointWipingVelMPController::updateMPConfig
void updateMPConfig(const ::armarx::aron::data::dto::DictPtr &dto, const Ice::Current &iceCurrent=Ice::emptyCurrent) override
Definition: WipingVelController.cpp:50
armarx::control::njoint_mp_controller::task_space::NJointWipingVelMPController::NJointWipingVelMPController
NJointWipingVelMPController(const RobotUnitPtr &robotUnit, const NJointControllerConfigPtr &config, const VirtualRobot::RobotPtr &)
Definition: WipingVelController.cpp:34
NJointControllerRegistry.h
VirtualRobot::RobotPtr
std::shared_ptr< class Robot > RobotPtr
Definition: Bus.h:19
armarx::control::common::mp::MPPool::mpConfig
MPListConfig mpConfig
this variable is only needed when constructing the MP instances, therefore you don't need to use trip...
Definition: MPPool.h:134