4 * @date Sun Aug 12 09:59:32 2007
6 * @brief Trajectory Generator
19 #ifdef MATLAB_MEX_FILE
20 #define dbgPrintf(format, ...) ssPrintf(format, ##__VA_ARGS__)
22 #define dbgPrintf(format, ...) printf(format, ##__VA_ARGS__)
25 #define dbgPrintf(format, ...)
28 #ifdef MATLAB_MEX_FILE
29 #define S_FUNCTION_LEVEL 2
30 #define S_FUNCTION_NAME sf_trgen
37 * Stright segment of the trajectory.
39 class Line : public TrajectorySegment
42 double angle; // uhle v globalnich souradnicich
43 double length; // delka segmentu
44 double cosphi, sinphi;
46 Line(Point *_p1, Point *_p2) : p1(_p1), p2(_p2) {
47 length = p1->distanceTo(*p2);
48 angle = p1->angleTo(*p2);
49 sinphi = (p2->y-p1->y)/length;
50 cosphi = (p2->x-p1->x)/length;
54 virtual void setMaxV(const TrajectoryConstraints &constr) {
58 virtual double getLength() const {return length;}
59 double getAngle() {return angle;}
61 virtual void getPointAt(double distance, Point *p) {
64 ratio = distance/length;
66 ratio = (length+distance)/length;
68 p->x = p1->x + ratio*(p2->x - p1->x);
69 p->y = p1->y + ratio*(p2->y - p1->y);
72 virtual void shortenBy(double distance, Point *newEnd) {
73 getPointAt(-distance, newEnd);
83 virtual TrajectorySegment* splitAt(double distance, Point *newEnd) {
84 if (distance <= 0 || distance >= length) {
85 dbgPrintf("splitAt: distance=%g length=%g\n", distance, length);
89 getPointAt(distance, newEnd);
90 Line *ns = new Line(*this);
93 ns->length -= distance;
98 virtual void getRefPos(double time, Pos &rp) {
100 double fraction = t/(t2-t1);
101 double distance = (v1 + 0.5*acc*t) * t;
102 rp.x = p1->x + distance*cosphi;
103 rp.y = p1->y + distance*sinphi;
106 rp.v = v1+fraction*(v2-v1);
110 #ifdef MATLAB_MEX_FILE
111 virtual void plot(const char *style) {
113 sprintf(cmd, "plot([%g %g], [%g %g], '%so')",
114 p1->x, p2->x, p1->y, p2->y, style);
121 * Arc shaped segment.
124 class Arc : public TrajectorySegment
127 double angle; // vrcholovy uhel [-pi,+pi]
128 double startAngle; // uhel od stredu k p1
129 double radius; // polomer
130 double length; // delka segmentu
133 Arc(Point *_p1, Point *_p2, double _radius) :
134 p1(_p1), p2(_p2), radius(fabs(_radius)) {
136 // Find the center of the arc
137 double angp1p2 = p1->angleTo(*p2);
138 double m = p1->distanceTo(*p2)/2.0;
141 dbgPrintf("EEEERRRRRRRRROOOOOOOOORRRRRRRRR!!!!!!!!!!\n");
143 double angcen = acos(m/radius);
144 if (_radius < 0) angcen = -angcen;
145 center = Point(p1->x + radius * cos(angp1p2+angcen),
146 p1->y + radius * sin(angp1p2+angcen));
148 startAngle = center.angleTo(*p1);
149 angle = center.angleTo(*p2) - startAngle;
150 if (angle < -M_PI) angle += 2.0*M_PI;
151 if (angle > +M_PI) angle -= 2.0*M_PI;
153 length = fabs(angle*radius);
158 virtual void setMaxV(const TrajectoryConstraints &constr) {
160 maxv = fmin(constr.maxv, constr.maxomega * r);
161 maxv = fmin(maxv, sqrt(constr.maxcenacc * r));
164 virtual double getLength() const {return length;}
166 virtual void getPointAt(double distance, Point *p) {
169 ratio = distance/length;
171 distance = -distance;
172 ratio = (length-distance)/length;
174 a = startAngle + ratio*angle;
175 p->x = center.x + radius*cos(a);
176 p->y = center.y + radius*sin(a);
178 virtual void shortenBy(double distance, Point *newEnd) {
179 getPointAt(-distance, newEnd);
181 angle *= (length-distance)/length;
184 distance = -distance;
185 startAngle = startAngle + distance/length*angle;
186 angle *= (length-distance)/length;
192 virtual TrajectorySegment* splitAt(double distance, Point *newEnd) {
193 if (distance <= 0 || distance >= length)
196 getPointAt(distance, newEnd);
197 Arc *ns = new Arc(*this);
199 double a = distance/length*angle;
206 ns->length -= distance;
211 virtual void getRefPos(double time, Pos &rp) {
213 double fraction = t/(t2-t1);
214 double distance = (v1 + 0.5*acc*t) * t;
215 double a = startAngle + distance/length*angle;
216 rp.x = center.x + radius*cos(a);
217 rp.y = center.y + radius*sin(a);
219 rp.phi = a + M_PI/2.0;
221 rp.phi = a - M_PI/2.0;
223 rp.v = v1+fraction*(v2-v1);
224 rp.omega =rp.v/radius;
225 if (angle < 0) rp.omega = -rp.omega;
228 #ifdef MATLAB_MEX_FILE
229 virtual void plot(const char *style) {
234 mxArray *x = mxCreateDoubleMatrix(1, len, mxREAL);
235 mxArray *y = mxCreateDoubleMatrix(1, len, mxREAL);
236 mxArray *s = mxCreateCharMatrixFromStrings(1, &style);
237 mxArray *rhs[] = {x,y,s};
239 for (i=0; i < len; i++) {
240 getRefPos(t1+(t2-t1)*i/(len-1), rp);
241 mxGetPr(x)[i] = rp.x;
242 mxGetPr(y)[i] = rp.y;
244 mexCallMATLAB(0, NULL, 3, rhs, "plot");
245 sprintf(cmd, "plot([%g %g], [%g %g], 'mo')",
246 p1->x, p2->x, p1->y, p2->y);
256 * Segment representing turn at one place.
258 * Speed related variables @c v1, @c v2 and @c acc represents
259 * angular speed here.
262 class Turn : public TrajectorySegment
265 double turnBy; /// turn by this angle [rad]
266 double startHeading; /// turning starts at this heading [rad]
268 Turn(Point *_center, double _startHeading, double _turnBy) :
269 center(_center), turnBy(_turnBy), startHeading(_startHeading) {}
273 virtual void setMaxV(const TrajectoryConstraints &constr) {
274 maxv = constr.maxomega;
277 virtual double getMaxAcc(const TrajectoryConstraints &constr) const {
278 return constr.maxangacc;
281 virtual bool isTurn() const { return true; };
283 virtual double getLength() const {return 0;}
284 virtual double getUnifiedLength() const {
288 virtual void getPointAt(double distance, Point *p) {
291 virtual void shortenBy(double distance, Point *newEnd) {}
293 virtual TrajectorySegment* splitAt(double distance, Point *newEnd) {
294 if (distance <= 0 || distance >= fabs(turnBy)) {
295 dbgPrintf("splitAt: distance=%g turnBy=%g\n", distance, turnBy);
299 Turn *ns = new Turn(*this);
301 distance = -distance;
303 ns->startHeading+=distance;
304 ns->turnBy -= distance;
308 virtual void getRefPos(double time, Pos &rp) {
310 double fraction = t/(t2-t1);
311 double distance = (v1 + 0.5*acc*t) * t;
315 rp.phi = startHeading + distance;
317 rp.phi = startHeading - distance;
320 rp.omega = v1+fraction*(v2-v1);
321 if (turnBy < 0) rp.omega = -rp.omega;
327 #ifdef MATLAB_MEX_FILE
328 virtual void plot(const char *style) {};
332 } // namespace Segment
335 using namespace Segment;
338 * Split a sergment and add the newly created segment to the
341 * @param seg Segment to split.
342 * @param distance Where to split.
347 Trajectory::splitSegment(iterator &seg, double distance)
349 TrajectorySegment *ns;
351 if (typeid(**seg) != typeid(Turn))
354 ns = (*seg)->splitAt(distance, np);
356 dbgPrintf("ERROR\n");
361 wayPoints.push_back(np);
362 seg = insert(++seg, ns);
366 // iterator insert(reverse_iterator it, const value_type& val) {
367 // return insert((--it).base(), val);
371 * Connects points by line segments.
374 Trajectory::points2segments()
376 TrajectoryPoints::iterator p1, p2;
377 Point ip(initPos.x, initPos.y);
379 if (*wayPoints.front() != ip) {
380 initialPoint = new Point(ip);
381 wayPoints.push_front(initialPoint);
383 initialPoint = wayPoints.front();
385 // Delete old segments
388 if (wayPoints.size() < 2 &&
389 (finalHeading.turn_type == FH_DONT_TURN ||
390 (finalHeading.turn_type == FH_SHORTEST && \
391 fabs(initPos.phi - finalHeading.heading) < 1.0/180*M_PI))) {
394 for (p1 = wayPoints.begin(), p2 = ++wayPoints.begin();
395 p2 != wayPoints.end();
397 push_back(new Line(*p1, *p2));
404 * Converts corners in trajectory to arcs.
405 * @todo Splines will be better.
409 // \frame{\begin{tikzpicture}
410 // \draw (-5cm,0) coordinate(p1) node[left]{$p_1$} --
411 // node[sloped,above]{$\mathrm seg_1$} (0,0) coordinate(p2)
412 // node[above]{$p_2$} -- node[sloped,above]{$\mathrm seg_2$} +(-30:5cm)
413 // node[right]{$p_3$} coordinate (p3);
414 // \coordinate[shift=(-105:5cm)] (c) at (p2);
415 // \draw (p2) -- (c);
416 // \draw (c) -- (c |- p2);
417 // \draw (c) -- (intersection of c--40:10cm and p2--p3);
418 // \end{tikzpicture}}
422 Trajectory::corners2arcs()
424 iterator seg, segNext, segPrev;
426 if (size() < 2) return;
428 // Find radiuses to meet e <= maxe
429 for (segPrev = begin(), segNext = ++begin();
431 segPrev++, segNext++) {
433 double alpha; // curve angle (-pi <= alpha <= +pi)
434 double r; // radius of proposed arc. (alpha < 0 -> r < 0)
435 double s; // length of the segment replaced by the arc (s > 0)
437 alpha = dynamic_cast<Line*>(*segNext)->getAngle() -
438 dynamic_cast<Line*>(*segPrev)->getAngle();
439 if (alpha > +M_PI) alpha -= 2.0*M_PI;
440 if (alpha < -M_PI) alpha += 2.0*M_PI;
442 if (fabs(alpha) < M_PI/180.0) continue;
444 r = constr.maxe / (1/cos(alpha/2.0) - 1);
445 if (alpha < 0) r = -r;
446 s = r*tan(alpha/2.0);
447 if (s > (*segPrev)->getLength()) {
448 s = (*segPrev)->getLength();
449 r = s/tan(alpha/2.0);
451 if (s > (*segNext)->getLength()) {
452 s = (*segNext)->getLength();
453 r = s/tan(alpha/2.0);
457 (*segPrev)->alphahalf = alpha/2.0;
459 catch (std::exception& e) {} // Skip wrong dynamic_cast
463 // Reduce overlapping arcs by decreasing radiuses
464 double s1, s2, r1, r2, l, alphahalf1, alphahalf2;
466 for (segPrev = begin(), segNext = ++begin();
468 segPrev++, segNext++) {
469 l = (*segNext)->getLength();
474 alphahalf1 = (*segPrev)->alphahalf;
475 alphahalf2 = (*segNext)->alphahalf;
477 if (fabs(r1) > fabs(r2)) {
479 r1 = s1/tan(alphahalf1);
480 wantSameR = (fabs(r1) < fabs(r2));
483 r2 = s2/tan(alphahalf2);
484 wantSameR = (fabs(r1) > fabs(r2));
487 if (alphahalf1 < 0 ^ alphahalf2 < 0)
488 s1 = l*tan(alphahalf1)/(tan(alphahalf1)-tan(alphahalf2));
490 s1 = l*tan(alphahalf1)/(tan(alphahalf1)+tan(alphahalf2));
492 r1 = s1/tan(alphahalf1);
493 r2 = s2/tan(alphahalf2);
503 // replace corners by arcs with previously computed radiuses
504 for (segPrev = begin(), segNext = ++begin();
506 ++segPrev, ++segNext) {
507 if (fabs((*segPrev)->r) > 0) {
508 Point *newEnd1, *newEnd2;
509 double s = (*segPrev)->s;
510 newEnd1 = new Point();
511 newEnd2 = new Point();
512 (*segPrev)->shortenBy(+s, newEnd1);
513 (*segNext)->shortenBy(-s, newEnd2);
514 wayPoints.push_back(newEnd1);
515 wayPoints.push_back(newEnd2);
516 segPrev = insert(segNext, new Arc(newEnd1, newEnd2,
521 //delete zero-length segments
523 while (seg != end()) {
524 if ((*seg)->getLength() <= 1e-9) {
534 * Assigns speeds and accelerations to segments; tries to maximize
535 * speed where possible. If necessary it also splits long segments
536 * to multiple parts with maximum acceleration/deceleration and
537 * maximal speed. The optimization is done in three passes. In the
538 * first pass, maximal speed of segments are set up (see
539 * TrajectoryConstraints::maxv, TrajectoryConstraints::maxomega,
540 * TrajectoryConstraints::maxcenacc). Then follows a forward pass, where
541 * acceleration is added to join segments with increasing maximal
542 * speeds. Finally, backward pass does almost the same by adding
543 * deceleration segments.
545 * Finding the split point of the segment where deceleration
546 * immediately follows acceleration is based on the following
549 * For uniformly accelerated motion, we can calculate time as
550 * function of speed: \f[ t(v)=\frac{v-v_0}{a}\f] Using this, we can
551 * express distance \f$ s \f$ as a function of speed: \f[ s(v) =
552 * \frac{1}{2} at(v)^2 + v_0 t(v) = \frac{v^2 - v_0^2}{2a}\f]
554 * Then, distance traveled during acceleration (\f$ s_1 \f$) and
555 * deceleration (\f$ s_2 \f$) must be equal to the length of the
556 * segment \f$ l = s_1 + s_2 \f$. Considering a segment with initial
557 * speed \f$ v_1 \f$, final speed \f$ v_2 \f$, and maximal speed (in
558 * the split point) \f$ v_{max} \f$, we can derive: \f[ l =
559 * s_1(v_{max}) + s_2(v_{max}) = \frac{2v_{max}^2 - v_1^2 - v_2^2}{2a}
560 * \f] \f[ v_{max}^2 = al + \frac{v_1^2 + v_2^2}{2} \f]
562 * Then, the length of the segment before split is: \f[ s_1 = l - s_2
563 * = l - \frac{v_{max}^2-v_2^2}{2a} = \frac{1}{2}l + \frac{v_2^2 -
568 Trajectory::calcSpeeds()
570 if (size() == 0) return;
572 // Assign maximum speed
573 for (iterator seg = begin(); seg != end(); seg++) {
574 (*seg)->setMaxV(constr);
575 (*seg)->v1 = (*seg)->maxv;
576 (*seg)->v2 = (*seg)->maxv;
578 #ifdef MATLAB_MEX_FILE
583 bool turning = false; // FIXME: turning je integer podle smeru otaceni
585 // Accelerare where possible
587 for (iterator seg = begin(); seg != end(); seg++) {
588 if (turning != (*seg)->isTurn()) {
589 turning = (*seg)->isTurn();
592 double v1 = (*seg)->v1;
593 double v2 = (*seg)->v2;
594 double maxv = (*seg)->maxv;
595 //dbgPrintf("processing: %p v1=%g\n", *seg, v1);
598 double l = (*seg)->getUnifiedLength();
599 double a = (*seg)->getMaxAcc(constr);
602 t = (-v1+sqrt(v1*v1 + 2.0*a*l))/a;
605 //dbgPrintf("t=%g v2=%g l=%g\n", t, v2, l);
606 if (v2 > maxv) { // split this segment
610 //dbgPrintf("t=%g v2=%g l=%g\n", t, v2, l);
612 if (splitSegment(ns, l)) {
622 #ifdef MATLAB_MEX_FILE
626 // Deccelerate where needed
628 lastv = 0; // Final speed
629 for (reverse_iterator seg = rbegin(); seg != rend(); seg++) {
630 if (turning != (*seg)->isTurn()) {
631 turning = (*seg)->isTurn();
634 double v1 = (*seg)->v1;
635 double v2 = (*seg)->v2;
636 double maxv = (*seg)->maxv;
637 dbgPrintf("processing: %p v2=%g\n", *seg, v2);
640 double l = (*seg)->getUnifiedLength();
641 double a = (*seg)->getMaxAcc(constr);
644 t = (-v2+sqrt(v2*v2 + 2.0*a*l))/a;
647 //dbgPrintf("t=%g v1=%g v2=%g l=%g\n", t, v1, v2, l);
648 if (v1 > maxv && (*seg)->v1 == maxv) {
651 double l2 = v2*t+0.5*a*t*t;
652 //dbgPrintf("t=%g v1=%g v2=%g l=%g l2=%g\n", t, v1, v2, l, l2);
653 iterator ns = --(seg.base());
654 //dbgPrintf("seg=%p ns=%p\n", *seg, *ns);
655 if (splitSegment(ns, l-l2)) {
656 //dbgPrintf("ns=%p\n", *ns);
660 } else if (v1 > (*seg)->v1) {
661 // Acceleration and deacceleration within one segment
662 // Where is the crossing af acceleration and decceleration?
664 double s1 = l/2.0 + (v2*v2 - v1*v1)/4.0*a;
666 if (s1 > l/1000 && s1 < l - l/1000) {
667 //dbgPrintf("t=%g v1=%g v2=%g l=%g s1=%g\n", t, v1, v2, l, s1);
668 iterator ns = --(seg.base());
669 //dbgPrintf("seg=%p ns=%p (before split)\n", *seg, *ns);
670 if (splitSegment(ns, s1)) {
671 ++seg; //move to the earlier subsegment of the splited one
672 //dbgPrintf("seg=%p ns=%p (after split)\n", *seg, *ns);
673 v1 = sqrt(v1*v1 + 2.0*a*s1);
687 #ifdef MATLAB_MEX_FILE
690 for (iterator seg = begin(); seg != end(); ++seg) {
691 dbgPrintf("final: %-16s %p v1=%-8.2g v2=%-8.2g l=%-8.2g\n",
692 typeid(**seg).name(),
693 *seg, (*seg)->v1, (*seg)->v2, (*seg)->getLength());
699 Trajectory::calcLength()
702 for (iterator seg = begin(); seg != end(); ++seg) {
703 totLen += (*seg)->getLength();
708 static double minimizeAngle(double rad)
710 rad = fmod(rad, 2.0*M_PI);
711 if (rad > +M_PI) rad -= 2.0*M_PI;
712 if (rad < -M_PI) rad += 2.0*M_PI;
716 static double calcTurnBy(double initialHeading, struct final_heading finalHeading)
719 if (finalHeading.turn_type == FH_DONT_TURN) turnBy = NAN;
721 turnBy = minimizeAngle(finalHeading.heading - initialHeading);
722 if (turnBy < 0 && finalHeading.turn_type == FH_CCW) turnBy += M_PI*2.0;
723 if (turnBy > 0 && finalHeading.turn_type == FH_CW) turnBy -= M_PI*2.0;
729 void Trajectory::addTurns()
732 TrajectorySegment *seg;
733 double initialHeading = initPos.phi;
736 initialHeading -= M_PI;
739 // We have to turn only. No movement.
740 dbgPrintf("*** Adding only the turn - start: %5.2f, end %5.2f\n", initialHeading/M_PI*180, finalHeading/M_PI*180);
741 turnBy = calcTurnBy(initialHeading, finalHeading);
742 if (fabs(turnBy) > 1e-3) {
743 push_front(new Turn(initialPoint, initialHeading, turnBy));
746 // Add turn at beginning
748 seg->v1 = 1; // Hack - speeds are not calculated yet
750 seg->startAt(0); // Assign times so we can use
752 seg->getRefPos(0, p);
753 dbgPrintf("*** Adding initial turn - start: %5.2f, end %5.2f\n", initialHeading/M_PI*180, p.phi/M_PI*180);
754 turnBy = minimizeAngle(p.phi - initialHeading);
755 if (fabs(turnBy) > 1e-3) {
756 push_front(new Turn(initialPoint, initialHeading, turnBy));
759 // Add turn at the end
761 seg->v1 = 1; // Hack - speeds are not calculated yet
763 seg->startAt(0); // Assign times so we can use getRefPos();
764 seg->getRefPos(seg->getT2(), p);
765 turnBy = calcTurnBy(p.phi, finalHeading);
766 if (fabs(turnBy) > 1e-3) { // NaN in finalHeading makes the condition false
767 dbgPrintf("*** Adding final turn - start: %5.2f, end %5.2f\n", p.phi/M_PI*180, finalHeading/M_PI*180);
768 push_back(new Turn(finalPoint, p.phi, turnBy));
774 * Prepares the trajectory. It converts points added by addPoint() to
775 * line segments (::Line). Then it replaces by sharp
776 * corners by arcs (see corners2arcs()), then it adds turns at the
777 * begin and end and finally it calculates speeds in the different
778 * parts of the trajectory by adding acceleration and decceleration
779 * segments (see calcSpeeds()).
781 * @param _initPos The point where the trajectory should
782 * start. Typically the current robot position.
783 * @todo Solve properly the case, when initPos has non-zero speed.
784 * @return True in case of success, false when the trajectory has only
785 * one point and initPos is already equal to that point.
788 Trajectory::prepare(Pos _initPos)
790 if (constr.maxv <= 0 || constr.maxv > 10 ||
791 constr.maxomega <= 0 || constr.maxomega > 10 ||
792 constr.maxangacc <= 0 || constr.maxangacc > 10 ||
793 constr.maxacc <= 0 || constr.maxacc > 10 ||
794 constr.maxcenacc <= 0 || constr.maxcenacc > 10 ||
795 constr.maxe <= 0 || constr.maxe > 10) {
796 dbgPrintf("wrong constraints!!!!\n");
799 if (wayPoints.size() == 0) {
800 dbgPrintf("No point in trajectory!\n");
805 if (points2segments() == false) {
806 dbgPrintf("points2segments error!\n");
815 // Assign times to segments
817 for (iterator seg = begin(); seg != end(); ++seg) {
818 t = (*seg)->startAt(t);
820 currentSeg = begin();
826 * Returns reference position of the trajectory at a given time.
828 * @param[in] time Time from the beginning of the trajectory.
829 * @param[out] rp Returned reference position.
830 * @return True if the time is after the trajectory ends or
831 * error. False otherwise.
834 Trajectory::getRefPos(double time, Pos &rp)
838 TrajectorySegment *seg;
844 d=(*currentSeg)->containsTime(time);
846 if (currentSeg == --end() && d > 0) break;
847 if (currentSeg == begin() && d < 0) break;
848 if (d>0) ++currentSeg;
849 else if (d<0) --currentSeg;
854 getSegRefPos(seg, time, rp);
856 getSegRefPos(seg, seg->getT1(), rp);
858 getSegRefPos(seg, seg->getT2(), rp);
859 ret = true; // We are at the end.
867 * Returns reference position of the given segment in the trajectory
868 * and takes @c backward into the account.
870 * @param[in] seg Segment to get the position from.
871 * @param[in] time Time from the beginning of the trajectory.
872 * @param[out] rp Returned reference position.
875 Trajectory::getSegRefPos(TrajectorySegment *seg, double time, Pos &rp)
877 seg->getRefPos(time, rp);
885 #ifdef MATLAB_MEX_FILE
887 Trajectory::plot(const char *style)
889 for (iterator seg = begin(); seg != end(); ++seg) {
894 Trajectory::plotSpeed(const char *style)
896 const char *f2 = "figure(2); xlabel('Time [s]'); ylabel('Speed [m/s], dotted [rad/s]');";
897 const char *f1 = "hold on; figure(1)";
899 strcpy(turnstyle, style);
900 if (turnstyle[1] == '-') turnstyle[1] = ':';
903 for (iterator seg = begin(); seg != end(); ++seg) {
904 t = (*seg)->startAt(t);
905 if (!(*seg)->isTurn())
906 (*seg)->plotSpeed(style);
908 (*seg)->plotSpeed(turnstyle);