TSMP.cpp
Go to the documentation of this file.
1#include "TSMP.h"
2
3#include <SimoxUtility/math/compare/is_equal.h>
4#include <SimoxUtility/math/convert/mat4f_to_quat.h>
5#include <VirtualRobot/MathTools.h>
6
9
10#include "../utils.h"
11
13{
14
15 TSMP::TSMP(const MPConfig& c) : MP{c}
16 {
17
18 ARMARX_INFO << " -- created TSMP with name: " << VAROUT(cfg.name);
19 // if (cfg.viaPoints.size() > 0)
20 // {
21 // for (const auto& vp : cfg.viaPoints)
22 // {
23 //// std::map<std::string, FramedPose> viapointsMapTemp;
24 //// viapointsMapTemp = userConfig["via_points"].get<decltype(viapointsMapTemp)>();
25 // ARMARX_IMPORTANT << VAROUT(vp.canonicalValue) << " " << VAROUT(common::dVecToString(vp.viaPointValue)) << " " << VAROUT(vmp->getUmin());
26 // ARMARX_CHECK_GREATER_EQUAL(vp.canonicalValue, 0);
27 // ARMARX_CHECK_LESS_EQUAL(vp.canonicalValue, 1);
28 // PosePtr p = new Pose(vp.viaPointValue);
29 // userDefinedViaPoints.push_back({vp.canonicalValue, common::poseToDVec(p)});
30 // }
31 // }
32 }
33
34 void
36 {
37 setStart(starts);
38 vmp->setInitialState(currentState);
39 vmp->prepareExecution();
40 }
41
42 DVec
44 {
45 auto viaPoint = viapoint;
46 if (prevViapoint.size() != viapoint.size())
47 {
48 prevViapoint = viapoint;
49 }
51 return viaPoint;
52 }
53
54 void
56 {
57 if (!cfg.enablePhaseStop)
58 {
59 phase.phaseStop = 0.0;
60 phase.mpcFactor = 1.0;
61 return;
62 }
63
64 TSMPInputPtr in = std::dynamic_pointer_cast<TSMPInput>(input);
65 TSMPOutputPtr out = std::dynamic_pointer_cast<TSMPOutput>(output);
66
67 Eigen::Matrix4f currentPose = in->pose;
68 Eigen::Vector6f currentVel = in->vel; /// Necessary for the D-term in Phase Stop
69
70 auto targetPoseVec = common::mat4ToDVec(out->pose);
71
72 double posiError = 0;
73 double oriError = 0;
74 Eigen::Vector3d targetPosition{targetPoseVec[0], targetPoseVec[1], targetPoseVec[2]};
75 Eigen::Quaterniond targetQuaternion{
76 targetPoseVec[3], targetPoseVec[4], targetPoseVec[5], targetPoseVec[6]}; // w,x,y,z
77 getError(currentPose, targetPosition, targetQuaternion, posiError, oriError);
78
79 // Weighted Total Error
80 double poseError = posiError + (cfg.psMM2Radian * oriError);
81
82 // Hysteresis Threshold Selection
83 double phaseDist = isDisturbance ? cfg.backDist : cfg.goDist;
84
85 // double phaseL = cfg.maxValue;
86 // double phaseK = cfg.slop;
87
88 // Sigmoid Function
89 phase.phaseStop = cfg.maxValue / (1 + exp(-cfg.slop * (poseError - phaseDist)));
90
91 // Mixing Factor (1.0 = Trust MP, 0.0 = Trust Feedback)
92 phase.mpcFactor = 1.0 - (phase.phaseStop / cfg.maxValue);
93
94 // Update Disturbance State (Hysteresis)
95 if (phase.mpcFactor < 0.1)
96 {
97 isDisturbance = true;
98 }
99 if (phase.mpcFactor > 0.9)
100 {
101 isDisturbance = false;
102 }
103 }
104
105 void
106 TSMP::run(MPInputPtr input, MPOutputPtr output, const PhaseStopResult& phase)
107 {
108 if (not running.load())
109 {
110 return;
111 }
112 if (firstRun.load())
113 {
115 }
116
117 TSMPInputPtr in = std::dynamic_pointer_cast<TSMPInput>(input);
118 TSMPOutputPtr out = std::dynamic_pointer_cast<TSMPOutput>(output);
119 ARMARX_CHECK_NOT_NULL(in) << "\nInput pointer for TSMP has wrong type or empty";
120 ARMARX_CHECK_NOT_NULL(out) << "\nOutput pointer for TSMP has wrong type or empty";
121
122 out->vel.setZero();
123 if (paused.load())
124 {
125 return;
126 }
127
128 double phaseStop = phase.phaseStop;
129 double mpcFactor = phase.mpcFactor; // Default to pure MP execution (1.0)
130 double tau = 1.0;
131 double timeDuration = cfg.durationSec;
132
133 if (canonicalValue < 0.01 && cfg.mpStyle == "Periodic")
134 {
135 ARMARX_IMPORTANT << " -- Periodic mode: new iteration.";
136 canonicalValue = 1.0;
137 }
138 if (canonicalValue < 1e-8 && cfg.mpStyle == "Discrete")
139 {
140 auto duration = (armarx::DateTime::Now() - mpStartTimeSec).toSecondsDouble();
141 ARMARX_INFO << " -- Discrete mode: MP " << cfg.name << " finished in " << duration
142 << " sec.";
143 running.store(false);
144 return;
145 }
146
147 LockGuardType guard(mpMutex);
148 Eigen::Matrix4f currentPose = in->pose;
149 Eigen::Vector6f currentVel = in->vel; /// Necessary for the D-term in Phase Stop
150 double deltaT = in->deltaT;
151
152 /// Sync internal state with current robot pose
153 std::vector<double> currentPoseVec = common::mat4ToDVec(currentPose);
154 for (size_t i = 0; i < currentState.size(); i++)
155 {
156 currentState[i].pos = currentPoseVec[i];
157 }
158
159 // /// calculate phase stop based on current pose and target pose
160 // // if (cfg.enablePhaseStop)
161 // // {
162 // double posiError = 0;
163 // double oriError = 0;
164
165 // Eigen::Vector3d targetPosition {targetPoseVec[0], targetPoseVec[1], targetPoseVec[2]};
166 // Eigen::Quaterniond targetQuaternion {targetPoseVec[3], targetPoseVec[4], targetPoseVec[5], targetPoseVec[6]}; // w,x,y,z convention
167
168 // getError(currentPose, targetPosition, targetQuaternion, posiError, oriError);
169
170 // double poseError = posiError + cfg.psMM2Radian * oriError;
171
172 // double phaseDist;
173 // if (isDisturbance)
174 // {
175 // phaseDist = cfg.backDist;
176 // }
177 // else
178 // {
179 // phaseDist = cfg.goDist;
180 // }
181 // double phaseL = cfg.maxValue;
182 // double phaseK = cfg.slop;
183
184 // phaseStop = phaseL / (1 + exp(-phaseK * (poseError - phaseDist)));
185 // double mpcFactor = 1 - (phaseStop / phaseL);
186
187 // if (mpcFactor < 0.1)
188 // {
189 // isDisturbance = true;
190 // }
191
192 // if (mpcFactor > 0.9)
193 // {
194 // isDisturbance = false;
195 // }
196
197 /// --------------------------------------------------------
198 /// Time Evolution (Canonical Value) and Step MP forward
199 /// --------------------------------------------------------
200 canonicalValue -= tau * deltaT / ((1 + phaseStop) * timeDuration);
201
202 std::vector<mplib::representation::MPState> targetState =
203 std::dynamic_pointer_cast<mplib::representation::vmp::TaskSpacePrincipalComponentVMP>(
204 vmp)
205 ->calculateDesiredState(canonicalValue, currentState);
206
207 /// scale translation velocity
208 for (size_t i = 0; i < 3; ++i)
209 {
210 currentState[i].vel = tau * targetState[i].vel * cfg.amplitude / timeDuration;
211 currentState[i].pos += deltaT * currentState[i].vel;
212 }
213
214 /// ----------------------------------------------------
215 /// Velocity Blending (Translation)
216 /// ----------------------------------------------------
217 if (cfg.enablePhaseStop and cfg.enablePhaseStopMPC)
218 {
219 for (size_t i = 0; i < 3; i++)
220 {
221 const double vel0 = currentState[i].vel;
222
223 /// PD Controller pulling towards target
224 const double vel1 = (cfg.psKpPos * (targetPoseVec[i] - currentPose(i, 3))) -
225 (cfg.psKdPos * currentVel(i));
226
227 /// Blend: Trust MP if mpcFactor is 1, Trust PD if mpcFactor is 0
228 out->vel(i) = mpcFactor * vel0 + (1 - mpcFactor) * vel1;
229 }
230 }
231 else
232 {
233 for (size_t i = 0; i < 3; i++)
234 {
235 out->vel(i) = currentState[i].vel;
236 }
237 }
238
239 /// ----------------------------------------------------
240 /// Velocity Blending (Orientation)
241 /// ----------------------------------------------------
242 /// Compute MP angular velocity
243 Eigen::Quaterniond dmpQuaternionVel{
244 targetState[3].vel, targetState[4].vel, targetState[5].vel, targetState[6].vel};
245 Eigen::Quaterniond dmpQuaternionPosi{
246 currentState[3].pos, currentState[4].pos, currentState[5].pos, currentState[6].pos};
247 Eigen::Quaterniond angularVel0 = dmpQuaternionVel * dmpQuaternionPosi.inverse();
248 angularVel0.w() *= 2;
249 angularVel0.x() *= 2;
250 angularVel0.y() *= 2;
251 angularVel0.z() *= 2;
252
253 /// Check for quaternion flip (continuity)
254 double angularChange = angularVel0.angularDistance(previousAngularVelocity);
255 if (angularVel0.coeffs().dot(previousAngularVelocity.coeffs()) < 0 && angularChange < 1e-2)
256 {
257 angularVel0.coeffs() = -angularVel0.coeffs();
258 }
259 // if (angularVel0.w() * previousAngularVelocity.w() < 0 &&
260 // angularVel0.x() * previousAngularVelocity.x() < 0 &&
261 // angularVel0.y() * previousAngularVelocity.y() < 0 &&
262 // angularVel0.z() * previousAngularVelocity.z() < 0 && angularChange < 1e-2)
263 // {
264 // angularVel0.w() = -angularVel0.w();
265 // angularVel0.x() = -angularVel0.x();
266 // angularVel0.y() = -angularVel0.y();
267 // angularVel0.z() = -angularVel0.z();
268 // }
269 previousAngularVelocity = angularVel0;
270
271 /// Scale by amplitude/duration
272 angularVel0.w() = 0;
273 angularVel0.vec() = tau * angularVel0.vec() * cfg.amplitude / timeDuration;
274 // angularVel0.x() =
275 // tau * angularVel0.x() * /*config.oriAmplitude*/ cfg.amplitude / timeDuration;
276 // angularVel0.y() =
277 // tau * angularVel0.y() * /*config.oriAmplitude*/ cfg.amplitude / timeDuration;
278 // angularVel0.z() =
279 // tau * angularVel0.z() * /*config.oriAmplitude*/ cfg.amplitude / timeDuration;
280
281 // Eigen::Quaterniond scaledQuat = (angularVel0 * dmpQuaternionPosi);
282 // currentState[3].vel = 0.5 * scaledQuat.w();
283 // currentState[4].vel = 0.5 * scaledQuat.x();
284 // currentState[6].vel = 0.5 * scaledQuat.z();
285 // currentState[5].vel = 0.5 * scaledQuat.y();
286
287 /// Update internal state
288 for (size_t i = 3; i < 7; ++i)
289 {
290 currentState[i].vel =
291 tau * targetState[i].vel * /*config.oriAmplitude*/ cfg.amplitude / timeDuration;
292 currentState[i].pos += currentState[i].vel * deltaT;
293 }
294
295 if (cfg.enablePhaseStop and cfg.enablePhaseStopMPC)
296 {
297 Eigen::Quaternionf targetQuaternionf{(float)targetPoseVec[3],
298 (float)targetPoseVec[4],
300 (float)targetPoseVec[6]};
301
302 Eigen::Matrix3f desiredMat(targetQuaternionf);
303 Eigen::Matrix3f currentMat = currentPose.block<3, 3>(0, 0);
304
305 /// Orientation PD
306 Eigen::Matrix3f diffMat = desiredMat * currentMat.inverse();
307 Eigen::Vector3f diffRPY = VirtualRobot::MathTools::eigen3f2rpy(diffMat);
308 Eigen::Vector3f angularVel1 =
309 cfg.psKpOri * diffRPY - cfg.psKdOri * currentVel.tail<3>();
310
311 /// Blend
312 out->vel(3) =
313 mpcFactor * angularVel0.x() / timeDuration + (1 - mpcFactor) * angularVel1(0);
314 out->vel(4) =
315 mpcFactor * angularVel0.y() / timeDuration + (1 - mpcFactor) * angularVel1(1);
316 out->vel(5) =
317 mpcFactor * angularVel0.z() / timeDuration + (1 - mpcFactor) * angularVel1(2);
318 }
319 else
320 {
321 out->vel(3) = angularVel0.x();
322 out->vel(4) = angularVel0.y();
323 out->vel(5) = angularVel0.z();
324 }
325
326 /// ----------------------------------------------------
327 /// Output Pose
328 /// ----------------------------------------------------
329 /// Flatten targetState to vector and normalize quaternion
330 std::vector<double> targetVec;
331 for (const auto& t : targetState)
332 {
333 targetVec.push_back(t.pos);
334 };
335
336 auto start = targetVec.end() - 4;
337
338 /// Compute norm of the last 4 elements
339 double norm = std::sqrt(std::inner_product(start, targetVec.end(), start, 0.0));
340
341 if (norm > 1e-8)
342 {
343 for (auto it = start; it != targetVec.end(); ++it)
344 {
345 *it /= norm;
346 }
347 }
348
349 out->pose = common::dVecToMat4(targetVec);
350
351 if (firstRun.load())
352 {
353 firstRun.store(false);
354 }
355 }
356
357} // namespace armarx::control::common::mp
#define float
Definition 16_Level.h:22
#define VAROUT(x)
constexpr T c
static DateTime Now()
Definition DateTime.cpp:51
MP(const MPConfig &c)
Definition MP.cpp:17
std::atomic_bool running
Definition MP.h:167
armarx::DateTime mpStartTimeSec
Definition MP.h:183
std::vector< mplib::representation::MPState > currentState
Definition MP.h:180
std::atomic_bool firstRun
Definition MP.h:172
void setStart(const DVec &starts)
Definition MP.cpp:378
std::atomic_bool paused
Definition MP.h:169
void validateInitialState(const DVec &starts) override
Definition TSMP.cpp:35
void computePhaseStop(const MPInputPtr input, const MPOutputPtr output, PhaseStopResult &phase) override
Definition TSMP.cpp:55
TSMP(const MPConfig &c)
Definition TSMP.cpp:15
DVec validateViaPoint(const DVec &viapoint) override
Definition TSMP.cpp:43
void run(MPInputPtr, MPOutputPtr, const PhaseStopResult &phase) override
Definition TSMP.cpp:106
Eigen::Quaterniond previousAngularVelocity
Definition TSMP.h:62
#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_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
Quaternion< double, 0 > Quaterniond
Quaternion< float, 0 > Quaternionf
Matrix< float, 6, 1 > Vector6f
This file is part of ArmarX.
std::shared_ptr< TSMPOutput > TSMPOutputPtr
Definition TSMP.h:47
Ice::DoubleSeq DVec
Definition MP.h:53
std::lock_guard< std::recursive_mutex > LockGuardType
Definition MP.h:45
std::shared_ptr< MPInput > MPInputPtr
Definition MP.h:70
std::shared_ptr< TSMPInput > TSMPInputPtr
Definition TSMP.h:46
std::shared_ptr< MPOutput > MPOutputPtr
Definition MP.h:71
void getError(Eigen::Matrix4f &currentPose, Eigen::Vector3d &targetPosition, Eigen::Quaterniond &targetQuaternion, double &posiError, double &oriError)
Definition utils.cpp:121
Eigen::Matrix4f dVecToMat4(const DVec &dvec)
create Eigen:Matrix4f from 7D double vector.
Definition utils.cpp:325
void validateTSViaPoint(std::vector< double > &viaPoint, std::vector< double > &viaPointReference)
Definition utils.cpp:648
DVec mat4ToDVec(const Eigen::Matrix4f &mat)
convert Eigen:Matrix4f to 7D double vector.
Definition utils.cpp:359
double norm(const Point &a)
Definition point.hpp:102