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