MixedImpedanceVelocityController.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
24#include <VirtualRobot/MathTools.h>
25#include <VirtualRobot/Nodes/RobotNode.h>
26#include <VirtualRobot/RobotNodeSet.h>
27
29#include <RobotAPI/components/units/RobotUnit/RobotUnit.h> // FIXME avoid this
30
32
34{
35 NJointControllerRegistration<NJointTSMixedImpedanceVelocityMPController>
37 "NJointTSMixedImpedanceVelocityMPController");
38
41 const NJointControllerConfigPtr& config,
42 const VirtualRobot::RobotPtr& robot) :
44 {
45 ARMARX_IMPORTANT << getClassName() + " construction done";
46 }
47
48 std::string
50 {
51 return "NJointTSMixedImpedanceVelocityMPController";
52 }
53
54 void
56 const ::armarx::aron::data::dto::DictPtr& dto,
57 const Ice::Current& iceCurrent)
58 {
60 {
61 for (auto& pair : limb)
62 {
63 pair.second->rtFirstRun.store(true);
64 }
65 }
66 }
67
68 void
70 {
71 const std::lock_guard<std::recursive_mutex> lock(mtx_mps);
72 auto [rtTargetSafe, ftSafe] = additionalTaskUpdateStatus();
73
74 // when ft guard is enabled and rt is not safe due to ft trigger, then we stop the corresponding mp
75 std::map<std::string, bool> flagMPToStop;
76 for (auto& pair : limb)
77 {
78 flagMPToStop[pair.first] = not limb.at(pair.first)->controller.ftsensor.ftSafe.load();
79 pair.second->nonRTDeltaT =
80 pair.second->rtStatusInNonRT.accumulateTime - pair.second->nonRTAccumulateTime;
81 pair.second->nonRTAccumulateTime = pair.second->rtStatusInNonRT.accumulateTime;
82 }
83 if (hands)
84 {
85 for (auto& pair : hands->hands)
86 {
87 pair.second->nonRTDeltaT =
88 pair.second->rtsInNonRT.accumulateTime - pair.second->nonRTAccumulateTime;
89 pair.second->nonRTAccumulateTime = pair.second->rtsInNonRT.accumulateTime;
90 }
91 }
92
93 std::map<std::string, bool> mpRunning;
94 for (auto& mp : mps)
95 {
96 const auto mpNodeSet = mp.second.mp->getNodeSetName();
97 mpRunning[mp.second.mp->getMPName()] = mp.second.mp->isRunning();
98
99 /// TODO add loop the check if MP for the sync mode coordinator need to stop
100 auto search = flagMPToStop.find(mpNodeSet);
101 if (search != flagMPToStop.end() and flagMPToStop.at(mpNodeSet) and
102 mp.second.mp->isRunning() and
103 (limb.at(mpNodeSet)->controller.ftsensor.isForceGuardEnabled() or
104 limb.at(mpNodeSet)->controller.ftsensor.isTorqueGuardEnabled()))
105 {
106 mp.second.mp->stop();
107 ARMARX_INFO << "Due to force/torque safe guard, MP " << mp.second.mp->getMPName()
108 << " is stopped at " << mp.second.mp->getCanonicalValue();
109 }
110
111 if (mp.second.mp->isFinished())
112 {
113 continue;
114 }
115 if (mp.second.mp->getRole() == "taskspace")
116 {
117 auto& arm = limb.at(mp.second.mp->getNodeSetName());
118 if (mp.second.mp->isFirstRun())
119 {
120 ARMARX_INFO << "checking TSMP initial status ...";
121 /// Here we check if the TSMP start pose is too far away from the current pose, if so, we would
122 /// prefer to reset the mp start from the current pose.
123 const std::vector<double> mpStart = mp.second.mp->getStartVec();
124 const auto mpStartPose = common::dVecToMat4(mpStart);
125 if (mp.second.mp->getStartFromPrevTarget() or
127 arm->rtStatusInNonRT.currentPose,
128 mpStartPose,
129 "current pose",
130 "TSMP initial start pose",
131 arm->nonRtConfig.safeDistanceMMToGoal,
132 arm->nonRtConfig.safeRotAngleDegreeToGoal,
133 mp.second.mp->getMPName() + "mp_set_target"))
134 {
135 if (mp.second.mp->getStartFromPrevTarget())
136 {
137 ARMARX_INFO << "User requested to start the current TS MP from the "
138 "previous target pose";
139 }
140 else
141 {
143 << "deviation from current pose too large, reset MP start pose";
144 }
145 mp.second.mp->validateInitialState(
146 common::mat4ToDVec(arm->rtStatusInNonRT.desiredPose));
147 }
148 ARMARX_INFO << "done";
149 }
150 mp::TSMPInputPtr in = std::dynamic_pointer_cast<mp::TSMPInput>(mp.second.input);
151 in->pose = arm->rtStatusInNonRT.currentPose;
152 in->vel = arm->rtStatusInNonRT.currentTwist;
153 // in->deltaT = arm->rtStatusInNonRT.deltaT;
154 in->deltaT = arm->nonRTDeltaT;
155 }
156 else if (mp.second.mp->getRole() == "nullspace")
157 {
158 auto& arm = limb.at(mp.second.mp->getNodeSetName());
159 mp::JSMPInputPtr in = std::dynamic_pointer_cast<mp::JSMPInput>(mp.second.input);
160 ARMARX_CHECK_EQUAL(arm->rtStatusInNonRT.nDoF,
161 arm->rtStatusInNonRT.jointPosition.size());
162 in->angleRadian = arm->rtStatusInNonRT.jointPosition;
163 in->angularVel = arm->rtStatusInNonRT.qvelFiltered;
164 in->deltaT = arm->nonRTDeltaT;
165 }
166 else if (mp.second.mp->getRole() == "hand")
167 {
168 auto& hand = hands->hands.at(mp.second.mp->getNodeSetName());
169 mp::JSMPInputPtr in = std::dynamic_pointer_cast<mp::JSMPInput>(mp.second.input);
170 // in->angleRadian = hand->rtsInNonRT.jointPosition.value();
171 in->deltaT = hand->nonRTDeltaT;
172 }
173 }
174 if (not rtTargetSafe)
175 {
177
178 // return;
179 // Make sure that you do not call additionalTaskSetTarget before return;
180 // since we overwrite the arm->nonRtConfig in additionalTaskUpdateStatus() with user config, which does not
181 // align with the up-to-date mp status. userConfig is only updated to align with MP status everytime a MP
182 // finishes execution. Only then, user can use this controller as if no mp is there.
183 }
184 runMPs(rtTargetSafe and ftSafe);
185
186 /// set mp target to nonRT config data structure
187 for (auto& mp : mps)
188 {
189 if (not mpRunning.at(mp.second.mp->getMPName()))
190 {
191 continue;
192 }
193 for (auto& ftGuard : mpConfig.ftGuard)
194 {
195 if (ftGuard.mpName == mp.second.mp->getMPName())
196 {
197 bool const forceGuard =
198 (ftGuard.force.has_value() and
199 ftGuard.force.value() >= mp.second.mp->getCanonicalValue());
200 bool const torqueGuard =
201 (ftGuard.torque.has_value() and
202 ftGuard.torque.value() >= mp.second.mp->getCanonicalValue());
203
204 auto& arm = limb.at(mp.second.mp->getNodeSetName());
205 bool const resetForce =
206 not arm->controller.ftsensor.isForceGuardEnabled() and forceGuard;
207 bool const resetTorque =
208 not arm->controller.ftsensor.isTorqueGuardEnabled() and torqueGuard;
209 if (resetForce or resetTorque)
210 {
211 ARMARX_INFO << "Triggering force torque safety guard for "
212 << arm->kinematicChainName << " at can value "
213 << mp.second.mp->getCanonicalValue();
214 }
215
216 arm->nonRtConfig.ftConfig.enableSafeGuardForce = forceGuard;
217 arm->nonRtConfig.ftConfig.enableSafeGuardTorque = torqueGuard;
218 arm->controller.ftsensor.enableSafeGuard(resetForce, resetTorque);
219 }
220 }
221
222 if (mp.second.mp->getRole() == "taskspace")
223 {
224 auto& arm = limb.at(mp.second.mp->getNodeSetName());
225 mp::TSMPOutputPtr out = std::dynamic_pointer_cast<mp::TSMPOutput>(mp.second.output);
226
227 /// Note that here we only report warning but still set target to RT, we rely on MP phase stop and
228 /// RT safety mechanism to stop or pause the action if needed
230 arm->rtStatusInNonRT.currentPose,
231 out->pose,
232 "current pose",
233 "TSMP pose",
234 arm->nonRtConfig.safeDistanceMMToGoal,
235 arm->nonRtConfig.safeRotAngleDegreeToGoal,
236 mp.second.mp->getMPName() + "mp_set_target");
237 arm->nonRtConfig.desiredPose = out->pose;
238 arm->nonRtConfig.desiredTwist = out->vel;
239 }
240 else if (mp.second.mp->getRole() == "nullspace")
241 {
242 auto& arm = limb.at(mp.second.mp->getNodeSetName());
243 mp::JSMPOutputPtr out = std::dynamic_pointer_cast<mp::JSMPOutput>(mp.second.output);
244 ARMARX_CHECK_EQUAL(arm->rtStatusInNonRT.nDoF, out->angleRadian.size());
245 arm->nonRtConfig.desiredNullspaceJointAngles.value() = out->angleRadian;
246 }
247 else if (mp.second.mp->getRole() == "hand")
248 {
249 mp::JSMPOutputPtr out = std::dynamic_pointer_cast<mp::JSMPOutput>(mp.second.output);
250 auto& hand = hands->hands.at(mp.second.mp->getNodeSetName());
251 mp::JSMPInputPtr in = std::dynamic_pointer_cast<mp::JSMPInput>(mp.second.input);
252 ARMARX_CHECK_EQUAL(hand->targetNonRT.jointPosition.value().size(),
253 out->angleRadian.size());
254 hand->targetNonRT.jointPosition.value() = out->angleRadian;
255 }
256 if (mpRunning.at(mp.second.mp->getMPName()) and mp.second.mp->isFinished())
257 {
258 ARMARX_INFO << "reset buffer for nonRtConfig of " << mp.second.mp->getMPName();
259 for (auto& pair : limb)
260 {
261 auto& arm = pair.second;
262 arm->bufferConfigUserToNonRt.reinitAllBuffers(arm->nonRtConfig);
263 }
264 for (auto& pair : limb)
265 {
266 userConfig.limbs.at(pair.first) = pair.second->nonRtConfig;
267 }
268 if (hands)
269 {
270 hands->reinitBuffer(userConfig.hands);
271 }
272 }
273 }
275 }
276} // namespace armarx::control::njoint_mp_controller::task_space
bool resetMPs(const ::armarx::aron::data::dto::DictPtr &dto, const std::map< std::string, VirtualRobot::RobotNodeSetPtr > &rnsMap)
Definition MPPool.cpp:565
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
NJointTaskspaceMixedImpedanceVelocityController(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
NJointTSMixedImpedanceVelocityMPController(const RobotUnitPtr &robotUnit, const NJointControllerConfigPtr &config, const VirtualRobot::RobotPtr &)
#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
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< NJointTSMixedImpedanceVelocityMPController > registrationControllerNJointTSMixedImpedanceVelocityMPController("NJointTSMixedImpedanceVelocityMPController")
IceUtil::Handle< class RobotUnit > RobotUnitPtr
Definition FTSensor.h:34