MoveJointPlatformTrajectory.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 * @package RobotSkillTemplates::MotionAndPlatformControlGroup
17 * @author Raphael Grimm ( raphael dot grimm at kit dot edu )
18 * @date 2016
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
23
24#include <algorithm>
25#include <chrono>
26#include <thread>
27
28#include <VirtualRobot/RobotConfig.h>
29
31
32#include <RobotAPI/interface/units/KinematicUnitInterface.h>
34
35using namespace armarx;
36using namespace MotionAndPlatformControlGroup;
37
38// DO NOT EDIT NEXT LINE
39MoveJointPlatformTrajectory::SubClassRegistry
40 MoveJointPlatformTrajectory::Registry(MoveJointPlatformTrajectory::GetName(),
42
43void
45{
46 // put your user code for the enter-point here
47 // execution time should be short (<100ms)
48 timeoutEvent = setTimeoutEvent(in.getTimeoutInMs(), createEventFailure());
49
50 t = in.getTrajectory();
51 jointNames = t->getDimensionNames();
52 if (!validJointNames())
53 {
54 ARMARX_ERROR_S << "MoveJointPlatformTrajectory expects a trajectory with x, y, alpha as "
55 "its first coordinates!";
56 ARMARX_ERROR_S << "given trajectory dimensions: " << jointNames;
57 emitFailure();
58 return;
59 }
60 maxAccs.clear();
61 if (in.isJointMaxAccelsSet())
62 {
63 maxAccs = in.getJointMaxAccels();
64 }
65 jointMaxSpeeds.clear();
66 if (in.isJointMaxSpeedsSet())
67 {
68 jointMaxSpeeds = in.getJointMaxSpeeds();
69 }
70
71
72 jointTargetTolerance = std::fabs(in.getJointTargetTolerance());
73 translationTargetTolerance = std::fabs(in.getTranslationTargetTolerance());
74 context = getContext<MotionAndPlatformControlGroupStatechartContext>();
75 lastCfgIt = --(t->end());
76 currentCfgIt = t->begin();
77 ARMARX_DEBUG << "timeout installed" << std::endl;
78}
79
80void
82{
83 ARMARX_INFO_S << "trajectory:\n" << in.getTrajectory()->output();
84 if (in.getSkipFirstTimepoint() >= in.getTrajectory()->size())
85 {
86 //nothing to do
87 ARMARX_WARNING_S << "The Trajectory did not result in movement!\n"
88 << "skip first = " << in.getSkipFirstTimepoint();
89 emitSuccess();
90 }
91
92 //the platform has no control mode
93 //exec
95 if (in.getUseNativePositionControl())
96 {
97 setControlMode(ePositionControl);
98 ctrlNative();
99 }
100 else
101 {
103 setControlMode(eVelocityControl);
104 ctrlInterpol();
105 }
106}
107
108void
110{
111 waitForDone(false);
112 ARMARX_INFO_S << "exit MoveJointPlatformTrajectory";
113}
114
115void
117{
118 waitForDone(true);
119 ARMARX_INFO_S << "break MoveJointPlatformTrajectory";
120}
121
122void
124{
125 while (!isRunningTaskFinished())
126 {
127 std::this_thread::sleep_for(std::chrono::milliseconds{5});
128 }
129 if (in.getSkipFirstTimepoint() >= in.getTrajectory()->size())
130 {
131 return;
132 }
133 cacheValues();
134
135 if (in.getHoldPositionAfterwards())
136 {
137 NameValueMap targetJointAngles;
138 NameControlModeMap controlModes;
139 NameValueMap targetJointVelocities;
140
141 for (size_t i = 0; i < jointNames.size(); ++i)
142 {
143 targetJointAngles[jointNames.at(i)] =
144 stopAtCurrentPos ? getValue(i) : lastCfgIt->getPosition(i);
145 targetJointVelocities[jointNames.at(i)] = 0.0f;
146 controlModes[jointNames.at(i)] = ePositionControl;
147 }
148 ARMARX_INFO_S << "hold position aftawards is set! (using position contol to hold position)";
149 setControlMode(ePositionControl);
150 setValues(targetJointAngles);
151 setVelocity(targetJointVelocities);
152 }
153
154 ARMARX_INFO << "Joint target tolerance: " << in.getJointTargetTolerance();
155 NameValueMap differences;
156 for (size_t i = 0; i < jointNames.size(); ++i)
157 {
158 differences[jointNames.at(i)] = lastCfgIt->getPosition(i) - getValue(i);
159 }
160 ARMARX_INFO << VAROUT(differences);
161 out.setJointAndPlatformDistancesToTargets(differences);
162}
163
164// DO NOT EDIT NEXT FUNCTION
170
171void
173{
174 //running data per joint used for ctrl
175 struct JointData
176 {
177 //data per joint
178 float maxAcc;
179 float maxVel;
180 float maxDeltaVel;
181 //data changed per config
182 float targetAng;
183 float prevAng;
184 float configDeltaAng;
185 float allowedPercInaccuracy;
186
187 //data changed per iteration
188 float ang;
189 float vel;
190
191 float deltaAng;
192 float deltaAngSig;
193 float percAng; //percentage of the current angle done
194
195 float nextVel;
196 float nextPercAng;
197 bool nextVelForced;
198 };
199
200 std::vector<JointData> jointData(jointNames.size());
201
202 //init per joint data
203 {
204 cacheValues();
205 for (std::size_t i = 0; i < jointNames.size(); ++i)
206 {
207 JointData& data = jointData.at(i);
208 data.maxAcc = std::fabs(getMaxAcc(i));
209 data.maxVel = std::fabs(getMaxVel(i));
210 data.maxDeltaVel = data.maxAcc * secondsPerPeriod;
211 data.targetAng = getValue(i);
212 }
213 }
214
215 auto updateJoitnDataForNewConfig = [&]
216 {
217 for (std::size_t i = 0; i < jointNames.size(); ++i)
218 {
219 JointData& data = jointData.at(i);
220 data.prevAng = data.targetAng;
221 data.targetAng = currentCfgIt->getPosition(i);
222 data.configDeltaAng = data.targetAng - data.prevAng;
223 data.allowedPercInaccuracy = getTargetTolerance(i) / std::fabs(data.configDeltaAng);
224 ARMARX_INFO_S << jointNames.at(i) << " move " << data.prevAng << " -> "
225 << data.targetAng << " (delta = " << data.configDeltaAng << ")";
226 }
227 };
228
229 ARMARX_INFO_S << "MoveJointPlatformTrajectory start axis sync (acceleration limited) PTP";
230 NameValueMap targetJointVel;
231 //skip the first target?
232 if (in.getSkipFirstTimepoint())
233 {
234 ARMARX_INFO_S << "skipping first config";
235 ++currentCfgIt;
236 updateJoitnDataForNewConfig();
237 }
238 while (!isRunningTaskStopped() && !doneAllConfigs())
239 {
240 //update per config data
241 updateJoitnDataForNewConfig();
242
243 //ctrl for current target
244 while (!isRunningTaskStopped())
245 {
246 auto startIter = std::chrono::high_resolution_clock::now();
247 //per it
248 cacheValues();
250
251 // data to decide whether we go to the next config
252 // float maxAbsDeltaAng = -std::numeric_limits<float>::infinity();
253 // float maxAbsDeltaTrans = -std::numeric_limits<float>::infinity();
254 bool dontAdvanceToNextConfig = false;
255 //used to determine the major axis
256 float minNextPerc = std::numeric_limits<float>::infinity();
257
258 //calculate per joint the optimal velocity (getting it as close as possible to the goal without overshooting it)
259 //also calculate the axis lagging behind (aka the axis with the lowest % of distance to its current target
260 //this will be the major axis and all other axis will try to reach the same % of their current target
261 //if an axis has to deaccellerate maximally to stop from overshooting the target, it will do so (and may not try to reach the %)
262 //if an axis reached its goal already it will not move
263 //no axis will move backwards (they only stop to wait for others)
264 for (std::size_t i = 0; i < jointNames.size(); ++i)
265 {
266 JointData& data = jointData.at(i);
267 //init sentinels
268 data.nextPercAng = -1;
269 data.percAng = -1;
270 data.nextVelForced = false;
271 //angles
272 data.ang = getValue(i);
273 data.vel = getVelocity(i);
274 const float velSig = sign(data.vel);
275
276 data.deltaAng = data.targetAng - data.ang;
277 data.deltaAngSig = sign(data.deltaAng);
278
279 // float& maxAbsDelta = (i < 3 ? maxAbsDeltaTrans : maxAbsDeltaAng);
280 // maxAbsDelta = std::max(std::fabs(data.deltaAng), maxAbsDelta);
281
282 dontAdvanceToNextConfig |= std::fabs(data.deltaAng) > getConfigChangeTolerance(i);
283
284 const float absBrakingDistance = data.vel * data.vel / (2.f * data.maxAcc);
285
286 //if we are near the goal (in tolerance) -> deaccelerate/ move to goal (forced)
287 //if this joint should not move (reached target) -> deaccelerate/ move to goal (forced)
288 if (((data.deltaAngSig ==
289 velSig) //if we brake we move in the direction of the target
290 && (std::fabs(data.deltaAng) <=
291 absBrakingDistance +
292 getTargetTolerance(i)) //we are near the target and have to brake now
293 ) ||
294 (std::fabs(data.deltaAng) <= getTargetTolerance(i)) //we are at the goal
295 )
296 {
297 if (std::fabs(data.vel) < data.maxDeltaVel)
298 {
299 //we are slow enough to set the vel to 0
300 data.nextVel = 0;
301 }
302 else
303 {
304 //we are to fast to set the vel to 0 -> brake with max a
305 data.nextVel = (std::fabs(data.vel) - data.maxDeltaVel) * sign(data.vel);
306 }
307 data.nextVelForced = true;
308
309
310 assert(data.nextVel <= data.maxVel);
311 assert(data.nextVel >= -data.maxVel);
312 continue;
313 }
314
315 //calc opt next vel (vel required to reach target if we will deaccelerate starting at next step)
316 //it ignores max a for acceleration (it is only used for deacceleration) and v
317 const float p = data.maxDeltaVel;
318 const float q = data.deltaAngSig *
319 (data.vel * data.maxDeltaVel - 2 * data.maxAcc * data.deltaAng);
320 const float nextVelOpt =
321 data.deltaAngSig * (-p / 2.f + std::sqrt((p / 2.f) * (p / 2.f) - q));
322 const float nextVelAccCapped = std::clamp(
323 nextVelOpt, data.vel - data.maxDeltaVel, data.vel + data.maxDeltaVel);
324 const float nextVelVelCapped =
325 std::clamp(nextVelAccCapped, -data.maxVel, data.maxVel);
326 data.nextVel = nextVelVelCapped;
327
328 assert(data.nextVel <= data.maxVel);
329 assert(data.nextVel >= -data.maxVel);
330 const float nextAngPredict =
331 data.ang + secondsPerPeriod * (data.vel + data.nextVel) / 2.f;
332
333 //joint is past target
334 data.percAng =
335 1 - data.deltaAng / data.configDeltaAng; // should not be nan, may be +- inf
336 assert(std::isfinite(data.percAng));
337 if (data.percAng > 1.f || //past target ->move to target
338 std::fabs(data.configDeltaAng) < jointTargetTolerance
339 // this joint should not move (but it is not near enough) try to move it to the target
340 )
341 {
342 //we want the current joint to move to the target
343 data.nextVelForced = true;
344 continue;
345 }
346
347 const float nextAngPredictDelta = data.targetAng - nextAngPredict;
348 data.nextPercAng =
349 1 - nextAngPredictDelta / data.configDeltaAng; // should not be nan / +- inf
350 assert(std::isfinite(data.nextPercAng));
351 minNextPerc = std::min(data.nextPercAng, minNextPerc);
352 }
353
354 ARMARX_INFO << VAROUT(minNextPerc);
355 //calculate final vel (in consideration of the major axis)
356 //there are 4 options:
357 // 1: this joints velocity is forced -> don't adapt its vel
358 // 2: this joint is one of the major axis -> don't adapt its vel
359 // 3: this joint is further than the major axis will be -> try to stop
360 // 4: this joint is neither of the above -> try to reach the major axis %
361 for (std::size_t i = 0; i < jointNames.size(); ++i)
362 {
363 const auto& name = jointNames.at(i);
364 JointData& data = jointData.at(i);
365 //case 1+2
366 if (data.nextVelForced ||
367 data.nextPercAng <= minNextPerc + data.allowedPercInaccuracy)
368 {
369
370 //either this joint has to brake, or it is a major axis
371 targetJointVel[name] = data.nextVel;
372 assert(std::fabs(targetJointVel[name]) <=
373 std::fabs(data.maxVel) + std::numeric_limits<float>::epsilon());
374 assert(std::fabs(targetJointVel[name]) <=
375 std::fabs(data.vel) + std::fabs(data.maxDeltaVel) +
376 std::numeric_limits<float>::epsilon());
377
378 if (data.nextVelForced)
379 {
380 ARMARX_INFO << "for name " << name << "\t" << targetJointVel[name] << " ("
381 << data.maxVel << ") delta " << data.deltaAng;
382 }
383 else
384 {
385 ARMARX_INFO << "maj name " << name << "\t" << targetJointVel[name] << " ("
386 << data.maxVel << ") delta " << data.deltaAng << "\t p/np"
387 << data.percAng << "\t/\t" << data.nextPercAng;
388 }
389 continue;
390 }
391 //case 3
392 if (data.percAng >= minNextPerc)
393 {
394 // we don't want to move our joint backwards.
395 // => slow down
396 targetJointVel[name] =
397 std::max(0.f, std::fabs(data.vel) - data.maxDeltaVel) * sign(data.vel);
398 assert(std::fabs(targetJointVel[name]) <=
399 std::fabs(data.maxVel) + std::numeric_limits<float>::epsilon());
400 assert(std::fabs(targetJointVel[name]) <=
401 std::fabs(data.vel) + std::fabs(data.maxDeltaVel) +
402 std::numeric_limits<float>::epsilon());
403
404 ARMARX_INFO << "slo name " << name << "\t" << targetJointVel[name] << " ("
405 << data.maxVel << ") delta " << data.deltaAng << "\t p/np"
406 << data.percAng << "\t/\t" << data.nextPercAng;
407 continue;
408 }
409 //case 4
410 //the delta this joint should moved (is capped by maxAcc/maxVel, since the new vel will be less than the calculated nextVel
411 const float deltaAngMoved = data.configDeltaAng - data.deltaAng;
412 const float deltaAngMovedNext = minNextPerc * data.configDeltaAng;
413 const float delatAngToMoveInIteratiron = deltaAngMovedNext - deltaAngMoved;
414 const float newVel = 2.f * delatAngToMoveInIteratiron / secondsPerPeriod - data.vel;
415 assert(std::fabs(newVel) <=
416 std::fabs(data.nextVel) + std::numeric_limits<float>::epsilon());
417 targetJointVel[name] = newVel;
418 assert(std::fabs(targetJointVel[name]) <=
419 std::fabs(data.maxVel) + std::numeric_limits<float>::epsilon());
420 assert(std::fabs(targetJointVel[name]) <=
421 std::fabs(data.vel) + std::fabs(data.maxDeltaVel) +
422 std::numeric_limits<float>::epsilon());
423 ARMARX_INFO << "var name " << name << "\t" << targetJointVel[name] << " ("
424 << data.maxVel << ") delta " << data.deltaAng << "\t p/np"
425 << data.percAng << "\t/\t" << data.nextPercAng;
426 }
427 //set vels
428 ARMARX_INFO << "###############################";
429 setVelocity(targetJointVel);
430 if (!dontAdvanceToNextConfig)
431 {
432 break;
433 }
434 //check for speed
435 const auto tIter = std::chrono::high_resolution_clock::now() - startIter;
436 ARMARX_INFO_S << deactivateSpam() << "Controler iteration took "
437 << std::chrono::duration_cast<std::chrono::milliseconds>(tIter).count()
438 << " ms";
439 if (tIter > period)
440 {
442 << "iteration took long! ("
443 << std::chrono::duration_cast<std::chrono::milliseconds>(tIter).count()
444 << " ms. Period = " << period.count() << " ms)";
445 }
446 else
447 {
448 std::this_thread::sleep_until(period + startIter);
449 }
450 } //ctrl for current target
451 ++currentCfgIt;
452 } //ctrl for all targets
453 if (doneAllConfigs())
454 {
456 << "MoveJointPlatformTrajectory end success axis sync (acceleration limited) PTP";
457 emitSuccess();
458 }
460 << "MoveJointPlatformTrajectory exit failure axis sync (acceleration limited) PTP";
461}
462
463void
465{
466 ARMARX_INFO_S << "MoveJointPlatformTrajectory native PTP";
467 {
468 NameValueMap maxVelsMap;
469 for (std::size_t i = 0; i < jointNames.size(); ++i)
470 {
471 maxVelsMap[jointNames.at(i)] = getMaxVel(i);
472 }
473 setMaxVelocity(maxVelsMap);
474 }
475 NameValueMap jointTargetMap;
476 while (!doneAllConfigs())
477 {
478 if (isRunningTaskStopped())
479 {
480 return;
481 }
482 currentCfgIt->writePositionsToNameValueMap(jointTargetMap);
483 setValues(jointTargetMap);
484 //wait for joint reached
485 while (true)
486 {
487 setValues(jointTargetMap);
488 if (isRunningTaskStopped())
489 {
490 return;
491 }
492 //calc max delta
493 cacheValues();
494 float maxDelta = 0;
495 float maxTranslationDelta = std::max(std::fabs(getValue(0) - jointTargetMap.at("x")),
496 std::fabs(getValue(1) - jointTargetMap.at("y")));
497 //alpha is considered a rotation joint
498 for (std::size_t i = 2; i < jointNames.size(); ++i)
499 {
500 maxDelta = std::max(maxDelta,
501 std::fabs(getValue(i) - jointTargetMap.at(jointNames.at(i))));
502 }
503 //break if delta is small
504
505 if (maxDelta < jointTargetTolerance && maxTranslationDelta < translationTargetTolerance)
506 {
507 break;
508 }
509 std::this_thread::sleep_for(period);
510 }
511 ++currentCfgIt;
512 }
513 ARMARX_INFO_S << "MoveJointPlatformTrajectory exit native PTP";
514 emitSuccess();
515}
516
517float
519{
520 return getFromList(cachedValues, i);
521}
522
523float
525{
526 return getFromList(cachedVelocities, i);
527}
528
529int
531{
532 return (val < 0) ? -1 : 1;
533}
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
#define VAROUT(x)
DataFieldIdentifierBaseLists getDataFieldIdentifiers(const std::string &channelName)
static XMLStateFactoryBasePtr CreateInstance(XMLStateConstructorParams stateData)
static float getFromList(const TimedVariantBaseLists &lists, std::size_t i)
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_ERROR_S
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:216
#define ARMARX_INFO_S
Definition Logging.h:202
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define ARMARX_WARNING_S
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:213
#define q
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceInternal::Handle< XMLStateFactoryBase > XMLStateFactoryBasePtr
Definition XMLState.h:64
bool isfinite(const std::vector< T, Ts... > &v)
Definition algorithm.h:366