CollisionAvoidanceMixedImpedanceVelocityController.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
23
25#include <RobotAPI/components/units/RobotUnit/RobotUnit.h> // FIXME avoid this
26
28
30{
31 NJointControllerRegistration<NJointTSCollisionAvoidanceMixedImpedanceVelocityMPController>
33 "NJointTSCollisionAvoidanceMixedImpedanceVelocityMPController");
34
45
46 std::string
48 const Ice::Current&) const
49 {
50 return "NJointTSCollisionAvoidanceMixedImpedanceVelocityMPController";
51 }
52
53 void
55 const ::armarx::aron::data::dto::DictPtr& dto,
56 const Ice::Current& iceCurrent)
57 {
59 {
60 for (auto& pair : limb)
61 {
62 pair.second->rtFirstRun.store(true);
63 }
64 }
65 }
66
67 void
69 {
70 const std::lock_guard<std::recursive_mutex> lock(mtx_mps);
71 auto [rtTargetSafe, ftSafe] = additionalTaskUpdateStatus();
72 if (coordinator)
73 {
74 coordinator->updateNonRt();
75 }
76
77 // when ft guard is enabled and rt is not safe due to ft trigger, then we stop the corresponding mp
78 std::map<std::string, bool> flagMPToStop;
79 for (auto& pair : limb)
80 {
81 flagMPToStop[pair.first] = not limb.at(pair.first)->controller.ftsensor.ftSafe.load();
82 if (flagMPToStop[pair.first])
83 {
84 // ARMARX_INFO << " --- force triggered at " << pair.first;
85 }
86 pair.second->nonRTDeltaT =
87 pair.second->rtStatusInNonRT.accumulateTime - pair.second->nonRTAccumulateTime;
88 pair.second->nonRTAccumulateTime = pair.second->rtStatusInNonRT.accumulateTime;
89 }
90 if (hands)
91 {
92 for (auto& pair : hands->hands)
93 {
94 pair.second->nonRTDeltaT =
95 pair.second->rtsInNonRT.accumulateTime - pair.second->nonRTAccumulateTime;
96 pair.second->nonRTAccumulateTime = pair.second->rtsInNonRT.accumulateTime;
97 }
98 }
99
100 std::map<std::string, bool> mpRunning;
101 for (auto& mp : mps)
102 {
103 const auto mpNodeSet = mp.second.mp->getNodeSetName();
104 mpRunning[mp.second.mp->getMPName()] = mp.second.mp->isRunning();
105
106 /// TODO add loop the check if MP for the sync mode coordinator need to stop
107 auto search = flagMPToStop.find(mpNodeSet);
108 if (mp.second.mp->isStopRequested())
109 {
110 mp.second.mp->stop();
111 ARMARX_INFO << "user requested to stop mp execution at canonical value: "
112 << mp.second.mp->getCanonicalValue();
113 }
114 if (search != flagMPToStop.end() and flagMPToStop.at(mpNodeSet) and
115 mp.second.mp->isRunning() and
116 (limb.at(mpNodeSet)->controller.ftsensor.isForceGuardEnabled() or
117 limb.at(mpNodeSet)->controller.ftsensor.isTorqueGuardEnabled()))
118 {
119 mp.second.mp->stop();
120 auto& force = limb.at(mpNodeSet)->rtStatusInNonRT.currentForceTorque;
121 ARMARX_INFO << "Due to force/torque safe guard, MP " << mp.second.mp->getMPName()
122 << " is stopped at " << mp.second.mp->getCanonicalValue()
123 << " with current ft: " << force;
124 }
125
126 if (mp.second.mp->isFinished())
127 {
128 continue;
129 }
130 if (mp.second.mp->getRole() == "taskspace")
131 {
132 auto& arm = limb.at(mp.second.mp->getNodeSetName());
133 if (mp.second.mp->isFirstRun())
134 {
135 ARMARX_INFO << "checking TSMP initial status ...";
136 /// Here we check if the TSMP start pose is too far away from the current pose, if so, we would
137 /// prefer to reset the mp start from the current pose.
138 const std::vector<double> mpStart = mp.second.mp->getStartVec();
139 const auto mpStartPose = common::dVecToMat4(mpStart);
140 if (mp.second.mp->getStartFromPrevTarget() or
142 arm->rtStatusInNonRT.currentPose,
143 mpStartPose,
144 "current pose",
145 "TSMP initial start pose",
146 arm->nonRtConfig.safeDistanceMMToGoal,
147 arm->nonRtConfig.safeRotAngleDegreeToGoal,
148 mp.second.mp->getMPName() + "mp_set_target"))
149 {
150 if (mp.second.mp->getStartFromPrevTarget())
151 {
152 ARMARX_INFO << "User requested to start the current TS MP from the "
153 "previous target pose";
154 }
155 else
156 {
158 << "deviation from current pose too large, reset MP start pose";
159 }
160 mp.second.mp->validateInitialState(
161 common::mat4ToDVec(arm->rtStatusInNonRT.desiredPose));
162 }
163 ARMARX_INFO << "done";
164 }
165 mp::TSMPInputPtr in = std::dynamic_pointer_cast<mp::TSMPInput>(mp.second.input);
166 in->pose = arm->rtStatusInNonRT.currentPose;
167 in->vel = arm->rtStatusInNonRT.currentTwist;
168 // in->deltaT = arm->rtStatusInNonRT.deltaT;
169 in->deltaT = arm->nonRTDeltaT;
170 }
171 else if (mp.second.mp->getRole() == "nullspace")
172 {
173 auto& arm = limb.at(mp.second.mp->getNodeSetName());
174 mp::JSMPInputPtr in = std::dynamic_pointer_cast<mp::JSMPInput>(mp.second.input);
175 ARMARX_CHECK_EQUAL(arm->rtStatusInNonRT.nDoF,
176 arm->rtStatusInNonRT.jointPosition.size());
177 in->angleRadian = arm->rtStatusInNonRT.jointPosition;
178 in->angularVel = arm->rtStatusInNonRT.qvelFiltered;
179 in->deltaT = arm->nonRTDeltaT;
180 }
181 else if (mp.second.mp->getRole() == "hand")
182 {
183 auto& hand = hands->hands.at(mp.second.mp->getNodeSetName());
184 mp::JSMPInputPtr in = std::dynamic_pointer_cast<mp::JSMPInput>(mp.second.input);
185 // in->angleRadian = hand->rtsInNonRT.jointPosition.value();
186 in->deltaT = hand->nonRTDeltaT;
187 }
188 else if (mp.second.mp->getRole() == "object")
189 {
190 if (not coordinator)
191 {
192 ARMARX_WARNING << "when using the object for MP, the coordinator must be first "
193 "instantiated";
194 return;
195 }
196 // auto& nodesetList = coord->getNodesetList();
197 // auto& rtsL = limb.at(coord->getLeftNodesetName())->rtStatusInNonRT;
198 // auto& rtsR = limb.at(coord->getRightNodesetName())->rtStatusInNonRT;
199
200 // setPos(currentPose, 0.5f * (getPos(rtsL.currentPose) + getPos(rtsR.currentPose)));
201 // setOri(currentPose, getOri(rtsL.currentPose));
202 const std::scoped_lock lock_vo_non_rt(coordinator->mtx_data_non_rt);
203 mp::TSMPInputPtr in = std::dynamic_pointer_cast<mp::TSMPInput>(mp.second.input);
204 in->pose = coordinator->dataNonRt.currentPose;
205 in->vel = coordinator->dataNonRt.currentVel;
206 in->deltaT = coordinator->dataNonRt.deltaT;
207 }
208 }
209 if (not rtTargetSafe)
210 {
212
213 // return;
214 // Make sure that you do not call additionalTaskSetTarget before return;
215 // since we overwrite the arm->nonRtConfig in additionalTaskUpdateStatus() with user config, which does not
216 // align with the up-to-date mp status. userConfig is only updated to align with MP status everytime a MP
217 // finishes execution. Only then, user can use this controller as if no mp is there.
218 }
219 runMPs(rtTargetSafe);
220
221 /// set mp target to nonRT config data structure
222 for (auto& mp : mps)
223 {
224 if (not mpRunning.at(mp.second.mp->getMPName()))
225 {
226 continue;
227 }
228 for (auto& ftGuard : mpConfig.ftGuard)
229 {
230 if (ftGuard.mpName == mp.second.mp->getMPName())
231 {
232 bool const forceGuard =
233 (ftGuard.force.has_value() and
234 ftGuard.force.value() >= mp.second.mp->getCanonicalValue());
235 bool const torqueGuard =
236 (ftGuard.torque.has_value() and
237 ftGuard.torque.value() >= mp.second.mp->getCanonicalValue());
238
239 auto& arm = limb.at(mp.second.mp->getNodeSetName());
240 bool const resetForce =
241 not arm->controller.ftsensor.isForceGuardEnabled() and forceGuard;
242 bool const resetTorque =
243 not arm->controller.ftsensor.isTorqueGuardEnabled() and torqueGuard;
244 if (resetForce or resetTorque)
245 {
246 ARMARX_INFO << "Triggering force torque safety guard for "
247 << arm->kinematicChainName << " at can value "
248 << mp.second.mp->getCanonicalValue();
249 }
250
251 arm->nonRtConfig.ftConfig.enableSafeGuardForce = forceGuard;
252 arm->nonRtConfig.ftConfig.enableSafeGuardTorque = torqueGuard;
253 arm->controller.ftsensor.enableSafeGuard(resetForce, resetTorque);
254 }
255 }
256
257 if (mp.second.mp->getRole() == "taskspace")
258 {
259 auto& arm = limb.at(mp.second.mp->getNodeSetName());
260 mp::TSMPOutputPtr out = std::dynamic_pointer_cast<mp::TSMPOutput>(mp.second.output);
261
262 /// Note that here we only report warning but still set target to RT, we rely on MP phase stop and
263 /// RT safety mechanism to stop or pause the action if needed
265 arm->rtStatusInNonRT.currentPose,
266 out->pose,
267 "current pose",
268 "TSMP pose",
269 arm->nonRtConfig.safeDistanceMMToGoal,
270 arm->nonRtConfig.safeRotAngleDegreeToGoal,
271 mp.second.mp->getMPName() + "mp_set_target");
272 arm->nonRtConfig.desiredPose = out->pose;
273 arm->nonRtConfig.desiredTwist = out->vel;
274 }
275 else if (mp.second.mp->getRole() == "object")
276 {
277 if (not coordinator)
278 {
279 ARMARX_WARNING << "when using the object for MP, the coordinator must be first "
280 "instantiated";
281 return;
282 }
283 mp::TSMPOutputPtr out = std::dynamic_pointer_cast<mp::TSMPOutput>(mp.second.output);
284 coordinator->cfgInNonRt.desiredPose = out->pose;
285 }
286 else if (mp.second.mp->getRole() == "nullspace")
287 {
288 auto& arm = limb.at(mp.second.mp->getNodeSetName());
289 mp::JSMPOutputPtr out = std::dynamic_pointer_cast<mp::JSMPOutput>(mp.second.output);
290 ARMARX_CHECK_EQUAL(arm->rtStatusInNonRT.nDoF, out->angleRadian.size());
291 arm->nonRtConfig.desiredNullspaceJointAngles.value() = out->angleRadian;
292 }
293 else if (mp.second.mp->getRole() == "hand")
294 {
295 mp::JSMPOutputPtr out = std::dynamic_pointer_cast<mp::JSMPOutput>(mp.second.output);
296 auto& hand = hands->hands.at(mp.second.mp->getNodeSetName());
297 mp::JSMPInputPtr in = std::dynamic_pointer_cast<mp::JSMPInput>(mp.second.input);
298 ARMARX_CHECK_EQUAL(hand->targetNonRT.jointPosition.value().size(),
299 out->angleRadian.size());
300 hand->targetNonRT.jointPosition.value() = out->angleRadian;
301 }
302 if (mpRunning.at(mp.second.mp->getMPName()) and mp.second.mp->isFinished())
303 {
304 ARMARX_INFO << "reset buffer for nonRtConfig of " << mp.second.mp->getMPName();
305 for (auto& pair : limb)
306 {
307 auto& arm = pair.second;
308 arm->bufferConfigUserToNonRt.reinitAllBuffers(arm->nonRtConfig);
309 arm->controller.ftsensor.enableSafeGuard(true, true);
310 }
311 for (auto& pair : limb)
312 {
313 userConfig.limbs.at(pair.first) = pair.second->nonRtConfig;
314 }
315 if (hands)
316 {
317 hands->reinitBuffer(userConfig.hands);
318 }
319 if (coordinator)
320 {
321 coordinator->resetBufferUserCfgAfterMP();
322 }
323 }
324 }
325 // TODO apply coordinator (for loosely-coupled/tightly-coupled tasks)
326 // sync mode is handled directly by "object" type of mp
327 if (coordinator)
328 {
329 coordinator->commitNonRt();
330 }
331
333 }
334} // namespace armarx::control::njoint_mp_controller::task_space
bool resetMPsV1(const ::armarx::aron::data::dto::DictPtr &dto, const std::map< std::string, VirtualRobot::RobotNodeSetPtr > &rnsMap, const std::shared_ptr< common::coordination::Coordination > &coordinator)
Definition MPPool.cpp:604
std::recursive_mutex mtx_mps
Definition MPPool.h:147
std::map< std::string, MPInputOutput > mps
Definition MPPool.h:146
MPListConfig mpConfig
this variable is only needed when constructing the MP instances, therefore you don't need to use trip...
Definition MPPool.h:153
NJointTaskspaceCollisionAvoidanceMixedImpedanceVelocityController(const RobotUnitPtr &robotUnit, const NJointControllerConfigPtr &config, const VirtualRobot::RobotPtr &robot)
NJointTSCollisionAvoidanceMixedImpedanceVelocityMPController(const RobotUnitPtr &robotUnit, const NJointControllerConfigPtr &config, const VirtualRobot::RobotPtr &)
void updateMPConfig(const ::armarx::aron::data::dto::DictPtr &dto, const Ice::Current &iceCurrent=Ice::emptyCurrent) override
#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_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
std::shared_ptr< class Robot > RobotPtr
Definition Bus.h:19
This file is part of ArmarX.
std::shared_ptr< TSMPOutput > TSMPOutputPtr
Definition TSMP.h:47
std::shared_ptr< TSMPInput > TSMPInputPtr
Definition TSMP.h:46
std::shared_ptr< JSMPOutput > JSMPOutputPtr
Definition JSMP.h:48
std::shared_ptr< JSMPInput > JSMPInputPtr
Definition JSMP.h:47
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:773
Eigen::Matrix4f dVecToMat4(const DVec &dvec)
create Eigen:Matrix4f from 7D double vector.
Definition utils.cpp:325
DVec mat4ToDVec(const Eigen::Matrix4f &mat)
convert Eigen:Matrix4f to 7D double vector.
Definition utils.cpp:359
NJointControllerRegistration< NJointTSCollisionAvoidanceMixedImpedanceVelocityMPController > registrationControllerNJointTSCollisionAvoidanceMixedImpedanceVelocityMPController("NJointTSCollisionAvoidanceMixedImpedanceVelocityMPController")
IceUtil::Handle< class RobotUnit > RobotUnitPtr
Definition FTSensor.h:34