Loading...
Searching...
No Matches
TrochoidStateSpace.cpp
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2025, Autonomous Systems Laboratory, ETH Zurich
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of the ETH Zurich nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34
35/* Author: Jaeyoung Lim */
36
37#include "ompl/base/spaces/TrochoidStateSpace.h"
38#include "ompl/base/SpaceInformation.h"
39#include "ompl/util/Exception.h"
40#include <queue>
41#include <boost/math/constants/constants.hpp>
42
43using namespace ompl::base;
44
45namespace
46{
47 constexpr double twopi = 2. * boost::math::constants::pi<double>();
48 const double TROCHOID_EPS = 1e-6;
49
50 double trochoid_delx(double omega, double dir, double delt, double phi0, double wind_ratio)
51 {
52 return .0 / (dir * omega) * (std::sin(dir * omega * delt + phi0) - std::sin(phi0)) + wind_ratio * delt;
53 }
54
55 double straight_delx(double wind_ratio, double delt, double hInitRad)
56 {
57 return (std::cos(hInitRad) + wind_ratio) * delt;
58 }
59
60 double straight_dely(double delt, double hInitRad)
61 {
62 return std::sin(hInitRad) * delt;
63 }
64
65 double trochoid_dely(double omega, double dir, double delt, double hInitRad)
66 {
67 return 1.0 / (dir * omega) * (-std::cos(dir * omega * delt + hInitRad) + std::cos(hInitRad));
68 }
69
70 double trochoid_delh(double omega, double dir, double delt)
71 {
72 return dir * omega * delt;
73 }
74
90 void computeEndpointBSB(double delta_1, double delta_2, double tA, double tBeta, double T, double phi0,
91 double radius, double wind_ratio, double &xf, double &yf, double &phif)
92 {
93 // state after first trochoid straight
94 double x0 = 0.0, y0 = 0.0;
95 double omega = (1.0 / radius);
96 double xBinit = x0 + trochoid_delx(omega, delta_1, tA, phi0, wind_ratio);
97 double yBinit = y0 + trochoid_dely(omega, delta_1, tA, phi0);
98 double hBinit = phi0 + trochoid_delh(omega, delta_1, tA);
99
100 double xBfinal, yBfinal, hBfinal;
101 xBfinal = xBinit + straight_delx(wind_ratio, tBeta - tA, hBinit);
102 yBfinal = yBinit + straight_dely(tBeta - tA, hBinit);
103 hBfinal = hBinit;
104
105 // final state
106 xf = xBfinal + trochoid_delx(omega, delta_2, T - tBeta, hBfinal, wind_ratio);
107 yf = yBfinal + trochoid_dely(omega, delta_2, T - tBeta, hBfinal);
108 phif = hBfinal + trochoid_delh(omega, delta_2, T - tBeta);
109 phif = fmod(phif, twopi);
110 if (phif < 0)
111 {
112 phif = phif + twopi;
113 }
114 }
115
139 bool checkConditionsBSB(double delta_1, double delta_2, double tA, double tB, double xt10, double yt10, double phi0,
140 double phit1, double xt20, double yt20, double phit2, double xf, double yf, double radius,
141 double wind_ratio, bool periodic, TrochoidStateSpace::PathType &trochoid_path)
142 {
143 // check that tA, tB, and T are valid numbers
144 if (std::isnan(tA) || std::isnan(tB))
145 {
146 return false;
147 }
148
149 double xtAdot = cos(delta_1 * (1. / radius) * tA + phit1) + wind_ratio;
150 double ytAdot = sin(delta_1 * (1. / radius) * tA + phit1);
151 double xtBdot = cos(delta_2 * (1. / radius) * tB + phit2) + wind_ratio;
152 double ytBdot = sin(delta_2 * (1. / radius) * tB + phit2);
153
154 double xtA = (radius / delta_1) * sin(delta_1 * (1. / radius) * tA + phit1) + wind_ratio * tA + xt10;
155 double ytA = (-radius / delta_1) * cos(delta_1 * (1. / radius) * tA + phit1) + yt10;
156 double xtB = (radius / delta_2) * sin(delta_2 * (1. / radius) * tB + phit2) + wind_ratio * tB + xt20;
157 double ytB = (-radius / delta_2) * cos(delta_2 * (1. / radius) * tB + phit2) + yt20;
158
159 double eps = 0.001;
160 double t2pi = twopi * radius;
161
162 if (tB < -t2pi || tB > t2pi)
163 {
164 return false;
165 }
166 if (tA < 0 || tA > 2 * t2pi)
167 {
168 return false;
169 }
170 if ((xtB - xtA) * xtAdot < -eps)
171 {
172 return false;
173 }
174 if ((ytB - ytA) * ytAdot < -eps)
175 {
176 return false;
177 }
178 if ((xtB - xtA) * xtBdot < -eps)
179 {
180 return false;
181 }
182 if ((ytB - ytA) * ytBdot < -eps)
183 {
184 return false;
185 }
186 // check that equal
187 if (fabs(ytB - ytA) > eps && fabs(ytAdot) > eps && fabs((xtB - xtA) / (ytB - ytA) - xtAdot / ytAdot) >= eps)
188 {
189 return false;
190 }
191 if (fabs(ytB - ytA) > eps && fabs(ytBdot) > eps && fabs((xtB - xtA) / (ytB - ytA) - xtBdot / ytBdot) >= eps)
192 {
193 return false;
194 }
195 // check endpoint
196 double p =
197 sqrt((xtB - xtA) * (xtB - xtA) + (ytB - ytA) * (ytB - ytA)) / sqrt(xtBdot * xtBdot + ytBdot * ytBdot);
198 double T = tA + p + (t2pi - tB);
199
200 // check if the candidate satisfies the desired final (x,y) position
201 // double xFinal, yFinal, hFinalRad;
202 double tBeta = tA + p;
203 double endpoint_x, endpoint_y, endoint_phi;
204 computeEndpointBSB(delta_1, delta_2, tA, tBeta, T, phi0, radius, wind_ratio, endpoint_x, endpoint_y,
205 endoint_phi);
206 if (fabs(endpoint_x - xf) > eps && fabs(endpoint_y - yf) > eps)
207 {
208 return false;
209 }
210
211 if (!periodic)
212 {
213 if (T < trochoid_path.length())
214 { // Update path if it is shorter
215 trochoid_path.length_[0] = tA;
216 trochoid_path.length_[1] = p;
217 trochoid_path.length_[2] = t2pi - tB;
218 }
219 return true;
220 }
221 else
222 {
223 if (T < trochoid_path.length() && T > 0.1)
224 { // Update path if it is shorter
225 trochoid_path.length_[0] = tA;
226 trochoid_path.length_[1] = p;
227 trochoid_path.length_[2] = t2pi - tB;
228 return true;
229 }
230 else
231 {
232 return false;
233 }
234 }
235 }
236
255 double fixedPointBSB(double p0, double delta_1, double delta_2, double k, double xt10, double yt10, double phit1,
256 double xt20, double yt20, double phit2, double radius, double wind_ratio)
257 {
258 constexpr double tol = 0.0001;
259 constexpr int N = 50;
260 std::array<double, N> pvec;
261 pvec[0] = p0;
262 int i = 1;
263 double omega = 1. / radius;
264 while (i < N)
265 {
266 double p = pvec[i - 1];
267 double E = (delta_1 - delta_2) / (delta_2 * delta_1 * omega) - (yt20 - yt10) / wind_ratio;
268 double F = (xt20 - xt10) / wind_ratio + (delta_1 / delta_2 - 1) * p +
269 (phit1 - phit2 + k * twopi) / (delta_2 * omega);
270 double G = (yt20 - yt10) + 1.0 / wind_ratio * (delta_2 - delta_1) / (delta_1 * delta_2 * omega);
271 double f = E * cos(delta_1 * omega * p + phit1) + F * sin(delta_1 * omega * p + phit1) - G;
272 double fBar = -E * sin(delta_1 * omega * p + phit1) * delta_1 * omega +
273 F * cos(delta_1 * omega * p + phit1) * delta_1 * omega +
274 sin(delta_1 * omega * p + phit1) * (delta_1 / delta_2 - 1);
275 pvec[i] = p - f / fBar;
276 if (fabs(pvec[i] - pvec[i - 1]) < tol)
277 {
278 return pvec[i];
279 }
280 i++;
281 }
282 return std::numeric_limits<double>::quiet_NaN();
283 }
284
301 void trochoidBSB(double x0, double y0, double phi0, double xf, double yf, double phif, double delta_1,
302 double delta_2, double radius, double wind_ratio, bool periodic,
304 {
305 double V_w = wind_ratio;
306 double omega = 1.0 / radius;
307 double t2pi = twopi * radius;
308
309 double phit1 = fmod(phi0, twopi);
310 double xt10 = x0 - delta_1 * radius * std::sin(phit1);
311 double yt10 = y0 + delta_1 * radius * std::cos(phit1);
312
313 double phit2 = fmod(phif - delta_2 * omega * t2pi, twopi);
314 double xt20 = xf - delta_2 * radius * std::sin(delta_2 * omega * t2pi + phit2) - V_w * t2pi;
315 double yt20 = yf + delta_2 * radius * cos(delta_2 * omega * t2pi + phit2);
316
317 if (delta_1 == delta_2)
318 { // RSR/LSL: analytic solution
319 for (int k_int = -2; k_int < 2; k_int++)
320 {
321 double k = (double)k_int;
322 double alpha = std::atan2(
323 yt20 - yt10, xt20 - xt10 + V_w * (fmod(phit1 - phit2, twopi) - k * twopi) / (delta_1 * omega));
324 double tA = t2pi / (delta_1 * twopi) * (std::asin(V_w * std::sin(alpha)) + alpha - phit1);
325 if (tA > t2pi || tA < 0)
326 {
327 tA = tA - t2pi * floor(tA / t2pi);
328 }
329 double tB = tA + (fmod(phit1 - phit2, twopi) - k * twopi) / (delta_1 * twopi) * t2pi;
330 checkConditionsBSB(delta_1, delta_2, tA, tB, xt10, yt10, phi0, phit1, xt20, yt20, phit2, xf, yf, radius,
331 wind_ratio, periodic, path);
332 }
333 }
334 else
335 { // RSL/LSR: numerical solution
336 for (int k_int = -2; k_int < 2; k_int++)
337 {
338 double k = (double)k_int;
339 constexpr int numTestPts = 10;
340 std::array<double, numTestPts> rootsComputed;
341 constexpr double sameRootEpsilon = 0.001;
342 for (int l = 0; l < numTestPts; l++)
343 {
344 bool rootAlreadyfound = false;
345 // initial guess
346 double p0 = (double)(l + 1) * t2pi / (double)numTestPts;
347 // root solving
348 double tA = fixedPointBSB(p0, delta_1, delta_2, k, xt10, yt10, phit1, xt20, yt20, phit2, radius,
349 wind_ratio);
350 // check bounds
351 if (tA < 0 && fabs(tA) < TROCHOID_EPS)
352 {
353 tA = 0.0;
354 }
355 if (tA > t2pi || tA < 0)
356 {
357 tA = tA - t2pi * floor(tA / t2pi);
358 }
359 // check if the root has already been computed
360 if (l > 0)
361 {
362 for (int i = 0; i < l; i++)
363 {
364 if (fabs(rootsComputed[i] - tA) <= sameRootEpsilon)
365 {
366 rootAlreadyfound = true;
367 }
368 }
369 }
370 // store the computed root
371 rootsComputed[l] = tA;
372 // if the root is unique
373 if (!rootAlreadyfound)
374 {
375 double tB = delta_1 / delta_2 * tA + (phit1 - phit2 + k * twopi) / (delta_2 * omega);
376 checkConditionsBSB(delta_1, delta_2, tA, tB, xt10, yt10, phi0, phit1, xt20, yt20, phit2, xf, yf,
377 radius, wind_ratio, periodic, path);
378 }
379 }
380 }
381 }
382
383 return;
384 }
385
406 bool checkConditionsBBB(double delta_1, double tA, double tB, double T, double xt10, double yt10, double phit1,
407 double xf, double yf, double phif, double radius, double wind_ratio, bool periodic,
408 TrochoidStateSpace::PathType &trochoid_path)
409 {
410 if (std::isnan(tA) || std::isnan(tB) || std::isnan(T))
411 {
412 return false;
413 }
414 double omega = 1. / radius;
415 double t2pi = twopi * radius;
416 double d2 = -delta_1;
417 double d3 = delta_1;
418
419 double phit3 = fmod(phif - d3 * omega * T, twopi);
420 double xt30 = xf - 1.0 / (d3 * omega) * sin(phif) - wind_ratio * T;
421 double yt30 = yf + 1.0 / (d3 * omega) * cos(phif);
422
423 double phit2 = 2 * delta_1 * omega * tA + phit1;
424 double xt20 = xt30 - 2 * 1.0 / (d2 * omega) * sin(d2 * omega * tB + phit2);
425 double yt20 = yt30 + 2 * 1.0 / (d2 * omega) * cos(d2 * omega * tB + phit2);
426
427 double xt1tA = 1.0 / (delta_1 * omega) * sin(delta_1 * omega * tA + phit1) + wind_ratio * tA + xt10;
428 double yt1tA = -1.0 / (delta_1 * omega) * cos(delta_1 * omega * tA + phit1) + yt10;
429
430 double xt2tA = 1.0 / (d2 * omega) * sin(d2 * omega * tA + phit2) + wind_ratio * tA + xt20;
431 double yt2tA = -1.0 / (d2 * omega) * cos(d2 * omega * tA + phit2) + yt20;
432
433 double xt2tB = 1.0 / (d2 * omega) * sin(d2 * omega * tB + phit2) + wind_ratio * tB + xt20;
434 double yt2tB = -1.0 / (d2 * omega) * cos(d2 * omega * tB + phit2) + yt20;
435 double xt3tB = 1.0 / (d3 * omega) * sin(d3 * omega * tB + phit3) + wind_ratio * tB + xt30;
436 double yt3tB = -1.0 / (d3 * omega) * cos(d3 * omega * tB + phit3) + yt30;
437
438 double xt1dottA = 1.0 * cos(delta_1 * omega * tA + phit1) + wind_ratio;
439 double yt1dottA = 1.0 * sin(delta_1 * omega * tA + phit1);
440 double xt2dottA = 1.0 * cos(d2 * omega * tA + phit2) + wind_ratio;
441 double yt2dottA = 1.0 * sin(d2 * omega * tA + phit2);
442
443 double xt2dottB = 1.0 * cos(d2 * omega * tB + phit2) + wind_ratio;
444 double yt2dottB = 1.0 * sin(d2 * omega * tB + phit2);
445 double xt3dottB = 1.0 * cos(d3 * omega * tB + phit3) + wind_ratio;
446 double yt3dottB = 1.0 * sin(d3 * omega * tB + phit3);
447
448 double eps = 0.001;
449
450 // check that: 0 <= tA <= tB <= T <= 4.0*t2pi
451 if (tA < 0 || tB < tA || T < tB || T > 4.0 * t2pi)
452 {
453 return false;
454 }
455 if (fabs(xt1tA - xt2tA) >= eps)
456 {
457 return false;
458 }
459 if (fabs(yt1tA - yt2tA) >= eps)
460 {
461 return false;
462 }
463 if (fabs(xt2tB - xt3tB) >= eps)
464 {
465 return false;
466 }
467 if (fabs(yt2tB - yt3tB) >= eps)
468 {
469 return false;
470 }
471 if (fabs(xt1dottA - xt2dottA) >= eps)
472 {
473 return false;
474 }
475 if (fabs(yt1dottA - yt2dottA) >= eps)
476 {
477 return false;
478 }
479 if (fabs(xt2dottB - xt3dottB) >= eps)
480 {
481 return false;
482 }
483 if (fabs(yt2dottB - yt3dottB) >= eps)
484 {
485 return false;
486 }
487
488 if (!periodic)
489 {
490 if (T < trochoid_path.length())
491 { // Update path if it is shorter
492 trochoid_path.length_[0] = tA;
493 trochoid_path.length_[1] = tB - tA;
494 trochoid_path.length_[2] = T - tB;
495 }
496 return true;
497 }
498 else
499 {
500 if (T < trochoid_path.length() && T > 0.1)
501 { // Update path if it is shorter
502 trochoid_path.length_[0] = tA;
503 trochoid_path.length_[1] = tB - tA;
504 trochoid_path.length_[2] = T - tB;
505 return true;
506 }
507 else
508 {
509 return false;
510 }
511 }
512 }
513
531 std::pair<double, double> fixedpointBBB(double p0, double p1, double delta_1, double k, double xt10, double yt10,
532 double phit1, double xf, double yf, double phif, double radius,
533 double wind_ratio)
534 {
535 // Implement TrochoidBBB
536 constexpr double tol = 0.0001;
537 constexpr int N = 50;
538 double d2 = -delta_1;
539 double d3 = delta_1;
540 double omega = 1.0 / radius;
541 std::array<double, N> pvec1;
542 std::array<double, N> pvec2;
543 pvec1[0] = p0;
544 pvec2[0] = p1;
545 int i = 1;
546 while (i < N)
547 {
548 double tA = pvec1[i - 1];
549 double T = pvec2[i - 1];
550 // vector F = [f1; f2];
551 double f1 =
552 2. / (delta_1 * omega) * sin(delta_1 * omega * tA + phit1) + wind_ratio * T + xt10 - xf +
553 1.0 / (d3 * omega) * sin(phif) +
554 2 / (d2 * omega) * sin(d2 * omega * T / 2 + (phif + phit1 + k * twopi) / 2 + delta_1 * omega * tA);
555 double f2 = -2 * 1.0 / (delta_1 * omega) * cos(delta_1 * omega * tA + phit1) + yt10 - yf -
556 1.0 / (d3 * omega) * cos(phif) -
557 2 * 1.0 / (d2 * omega) *
558 cos(d2 * omega * T / 2 + (phif + phit1 + k * twopi) / 2 + delta_1 * omega * tA);
559 // matrix FBar = [ a b ; c d ];
560 double a = 2 * 1.0 *
561 (cos(delta_1 * omega * tA + phit1) -
562 cos(d2 * omega * T / 2 + (phif + phit1 + k * twopi) / 2 + delta_1 * omega * tA));
563 double b =
564 wind_ratio + 1.0 * cos(d2 * omega * T / 2 + (phif + phit1 + k * twopi) / 2 + delta_1 * omega * tA);
565 double c = 2 * 1.0 *
566 (sin(delta_1 * omega * tA + phit1) -
567 sin(d2 * omega * T / 2 + (phif + phit1 + k * twopi) / 2 + delta_1 * omega * tA));
568 double d = 1.0 * sin(d2 * omega * T / 2 + (phif + phit1 + k * twopi) / 2 + delta_1 * omega * tA);
569 double det = a * d - b * c;
570 // pvec = x - FBar*FBarInv;
571 pvec1[i] = pvec1[i - 1] - (d / det * f1 - b / det * f2);
572 pvec2[i] = pvec2[i - 1] - (-c / det * f1 + a / det * f2);
573 // convergence criteria
574 if ((pvec1[i] - pvec1[i - 1]) * (pvec1[i] - pvec1[i - 1]) +
575 (pvec2[i] - pvec2[i - 1]) * (pvec2[i] - pvec2[i - 1]) <
576 tol)
577 {
578 return {pvec1[i], pvec2[i]};
579 }
580 i++;
581 }
582 return {std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()};
583 }
584
600 void trochoidBBB(double x0, double y0, double phit1, double xf, double yf, double phif, double delta_1,
601 double radius, double wind_ratio, bool periodic, TrochoidStateSpace::PathType &path)
602 {
603 double omega = (1 / radius);
604 double t2pi = twopi * radius;
605
606 double xt10 = x0 - 1.0 / (delta_1 * omega) * sin(phit1);
607 double yt10 = y0 + 1.0 / (delta_1 * omega) * cos(phit1);
608
609 double delta_2 = -delta_1;
610
611 // test a grid of initial conditions, grid resolution
612 constexpr int numTestPts = 10;
613 std::array<std::array<double, 3>, numTestPts * numTestPts> rootsComputed;
614 double sameRootEpsilon = 0.001;
615 for (int kint = -2; kint < 3; kint++)
616 {
617 double k = (double)kint;
618 for (int l = 0; l < numTestPts; l++)
619 {
620 for (int n = 0; n < numTestPts; n++)
621 {
622 bool rootAlreadyfound = false;
623 // initial guess
624 double tA_test = (double)(l + 1) * t2pi / numTestPts;
625 double T_test = (double)(n + 1) * 3 * t2pi / numTestPts;
626 auto [tA, T] =
627 fixedpointBBB(tA_test, T_test, delta_1, k, xt10, yt10, phit1, xf, yf, phif, radius, wind_ratio);
628 double tB = tA + T / 2 + (phif - phit1 + k * twopi) / (2 * delta_2 * omega);
629
630 int curRow = (l)*numTestPts + (n);
631 // check if the root has already been computed
632 if (l > 0 || n > 0)
633 {
634 for (int i = 0; i < curRow; i++)
635 {
636 if (fabs(rootsComputed[i][0] - tA) <= sameRootEpsilon &&
637 fabs(rootsComputed[i][1] - tB) <= sameRootEpsilon &&
638 fabs(rootsComputed[i][2] - T) <= sameRootEpsilon)
639 {
640 rootAlreadyfound = true;
641 }
642 }
643 }
644 // store the computed roots
645 rootsComputed[curRow] = {tA, tB, T};
646
647 // if the root is unique
648 if (!rootAlreadyfound)
649 {
650 checkConditionsBBB(delta_1, tA, tB, T, xt10, yt10, phit1, xf, yf, phif, radius, wind_ratio,
651 periodic, path);
652 }
653 }
654 }
655 }
656 }
657
658 bool isLongPathCase(double x0, double y0, double phi0, double xf, double yf, double phif, double radius,
659 double /* wind_ratio */)
660 {
661 double dx = (xf - x0) / radius, dy = (yf - y0) / radius;
662 double d = sqrt(dx * dx + dy * dy), theta = atan2(dy, dx);
663 double alpha = phi0 - theta, beta = phif - theta;
664 bool is_geometric_long_path = (std::abs(std::sin(alpha)) + std::abs(std::sin(beta)) +
665 std::sqrt(4 - std::pow(std::cos(alpha) + std::cos(beta), 2)) - d) < 0;
666 bool is_wind_blowingaway = dx < 0;
667 return is_geometric_long_path && is_wind_blowingaway;
668 }
669
670 TrochoidStateSpace::PathType trochoidRSR(double x0, double y0, double phi0, double xf, double yf, double phif,
671 double radius, double wind_ratio, bool periodic)
672 {
674 trochoidBSB(x0, y0, phi0, xf, yf, phif, -1, -1, radius, wind_ratio, periodic, path);
675 return path;
676 }
677
678 TrochoidStateSpace::PathType trochoidLSL(double x0, double y0, double phi0, double xf, double yf, double phif,
679 double radius, double wind_ratio, bool periodic)
680 {
682 trochoidBSB(x0, y0, phi0, xf, yf, phif, 1, 1, radius, wind_ratio, periodic, path);
683 return path;
684 }
685
686 TrochoidStateSpace::PathType trochoidRSL(double x0, double y0, double phi0, double xf, double yf, double phif,
687 double radius, double wind_ratio, bool periodic)
688 {
690 trochoidBSB(x0, y0, phi0, xf, yf, phif, -1, 1, radius, wind_ratio, periodic, path);
691 return path;
692 }
693
694 TrochoidStateSpace::PathType trochoidLSR(double x0, double y0, double phi0, double xf, double yf, double phif,
695 double radius, double wind_ratio, bool periodic)
696 {
698 trochoidBSB(x0, y0, phi0, xf, yf, phif, 1, -1, radius, wind_ratio, periodic, path);
699 return path;
700 }
701
702 TrochoidStateSpace::PathType trochoidLRL(double x0, double y0, double phi0, double xf, double yf, double phif,
703 double radius, double wind_ratio, bool periodic)
704 {
706 trochoidBBB(x0, y0, phi0, xf, yf, phif, 1, radius, wind_ratio, periodic, path);
707 return path;
708 }
709
710 TrochoidStateSpace::PathType trochoidRLR(double x0, double y0, double phi0, double xf, double yf, double phif,
711 double radius, double wind_ratio, bool periodic)
712 {
714 trochoidBBB(x0, y0, phi0, xf, yf, phif, -1, radius, wind_ratio, periodic, path);
715 return path;
716 }
717
718 TrochoidStateSpace::PathType getPath(const double x0, const double y0, const double phi0, const double xf,
719 const double yf, const double phif, double radius, double wind_ratio,
720 bool periodic)
721 {
722 if (fabs(x0 - xf) < TROCHOID_EPS && fabs(y0 - yf) < TROCHOID_EPS && fabs(phi0 - phif) < TROCHOID_EPS &&
723 !periodic)
724 return {TrochoidStateSpace::dubinsPathType()[0], 0.0, 0.0};
725
726 TrochoidStateSpace::PathType path(trochoidLSL(x0, y0, phi0, xf, yf, phif, radius, wind_ratio, periodic)),
727 tmp(trochoidRSR(x0, y0, phi0, xf, yf, phif, radius, wind_ratio, periodic));
728 double len, minLength = path.length();
729
730 if ((len = tmp.length()) < minLength)
731 {
732 minLength = len;
733 path = tmp;
734 }
735 tmp = trochoidRSL(x0, y0, phi0, xf, yf, phif, radius, wind_ratio, periodic);
736 if ((len = tmp.length()) < minLength)
737 {
738 minLength = len;
739 path = tmp;
740 }
741 tmp = trochoidLSR(x0, y0, phi0, xf, yf, phif, radius, wind_ratio, periodic);
742 if ((len = tmp.length()) < minLength)
743 {
744 minLength = len;
745 path = tmp;
746 }
747 if (!isLongPathCase(x0, y0, phi0, xf, yf, phif, radius, wind_ratio))
748 {
749 tmp = trochoidRLR(x0, y0, phi0, xf, yf, phif, radius, wind_ratio, periodic);
750 if ((len = tmp.length()) < minLength)
751 {
752 minLength = len;
753 path = tmp;
754 }
755 tmp = trochoidLRL(x0, y0, phi0, xf, yf, phif, radius, wind_ratio, periodic);
756 if ((len = tmp.length()) < minLength)
757 path = tmp;
758 }
759 return path;
760 }
761
762} // namespace
763
764namespace ompl::base
765{
766 std::ostream &operator<<(std::ostream &os, const TrochoidStateSpace::PathType &path)
767 {
768 os << "TrochoidPath[ type=";
769 for (unsigned i = 0; i < 3; ++i)
770 if (path.type_->at(i) == TrochoidStateSpace::TROCHOID_LEFT)
771 os << "L";
772 else if (path.type_->at(i) == TrochoidStateSpace::TROCHOID_STRAIGHT)
773 os << "S";
774 else
775 os << "R";
776 os << ", length=" << path.length_[0] << '+' << path.length_[1] << '+' << path.length_[2] << '=' << path.length()
777 << ", reverse=" << path.reverse_ << " ]";
778 return os;
779 }
780} // namespace ompl::base
781
782const std::vector<std::vector<TrochoidStateSpace::TrochoidPathSegmentType>> &TrochoidStateSpace::dubinsPathType()
783{
784 static const std::vector<std::vector<TrochoidStateSpace::TrochoidPathSegmentType>> *pathType =
785 new std::vector<std::vector<TrochoidStateSpace::TrochoidPathSegmentType>>(
786 {{{TROCHOID_LEFT, TROCHOID_STRAIGHT, TROCHOID_LEFT},
787 {TROCHOID_RIGHT, TROCHOID_STRAIGHT, TROCHOID_RIGHT},
788 {TROCHOID_RIGHT, TROCHOID_STRAIGHT, TROCHOID_LEFT},
789 {TROCHOID_LEFT, TROCHOID_STRAIGHT, TROCHOID_RIGHT},
790 {TROCHOID_RIGHT, TROCHOID_LEFT, TROCHOID_RIGHT},
791 {TROCHOID_LEFT, TROCHOID_RIGHT, TROCHOID_LEFT}}});
792 return *pathType;
793}
794
795double TrochoidStateSpace::distance(const State *state1, const State *state2) const
796{
797 return isSymmetric_ ? symmetricDistance(state1, state2, rho_, eta_, psi_w_) :
798 distance(state1, state2, rho_, eta_, psi_w_);
799}
800double TrochoidStateSpace::distance(const State *state1, const State *state2, double radius, double wind_ratio,
801 double wind_heading)
802{
803 return getPath(state1, state2, radius, wind_ratio, wind_heading).length();
804}
805double TrochoidStateSpace::symmetricDistance(const State *state1, const State *state2, double radius, double wind_ratio,
806 double wind_heading)
807{
808 return std::min(getPath(state1, state2, radius, wind_ratio, wind_heading).length(),
809 getPath(state2, state1, radius, wind_ratio, wind_heading).length());
810}
811
812void TrochoidStateSpace::interpolate(const State *from, const State *to, const double t, State *state) const
813{
814 bool firstTime = true;
815 PathType path;
816 interpolate(from, to, t, firstTime, path, state);
817}
818
819void TrochoidStateSpace::interpolate(const State *from, const State *to, const double t, bool &firstTime,
820 PathType &path, State *state) const
821{
822 if (firstTime)
823 {
824 if (t >= 1.)
825 {
826 if (to != state)
827 copyState(state, to);
828 return;
829 }
830 if (t <= 0.)
831 {
832 if (from != state)
833 copyState(state, from);
834 return;
835 }
836 if (equalStates(from, to))
837 {
838 path = getPath(from, to, rho_, eta_, psi_w_, true);
839 }
840 else
841 {
842 path = getPath(from, to, rho_, eta_, psi_w_);
843 }
844 if (isSymmetric_)
845 {
846 PathType path2(getPath(to, from));
847 if (path2.length() < path.length())
848 {
849 path2.reverse_ = true;
850 path = path2;
851 }
852 }
853 firstTime = false;
854 }
855 interpolate(from, path, t, state, rho_, eta_, psi_w_);
856}
857
858void TrochoidStateSpace::interpolate(const State *from, const PathType &path, double t, State *state, double radius,
859 double wind_ratio, double wind_heading) const
860{
861 auto *s = allocState()->as<StateType>();
862 double seg = t * path.length(), phi, v, x_t10, y_t10, delta;
863 s->setXY(0., 0.);
864 s->setYaw(from->as<StateType>()->getYaw() - wind_heading);
865 if (!path.reverse_)
866 {
867 for (unsigned int i = 0; i < 3 && seg > 0; ++i)
868 {
869 v = std::min(seg, path.length_[i]);
870 phi = s->getYaw();
871 seg -= v;
872 switch (path.type_->at(i))
873 {
874 case TROCHOID_LEFT:
875 delta = 1;
876 x_t10 = s->getX() - (radius * delta) * sin(phi);
877 y_t10 = s->getY() + (radius * delta) * cos(phi);
878 s->setXY(x_t10 + (radius * delta) * sin(delta * (1.0 / radius) * v + phi) + wind_ratio * v,
879 y_t10 - (radius * delta) * cos(delta * (1.0 / radius) * v + phi));
880 s->setYaw(phi + delta * (1.0 / radius) * v);
881 break;
882 case TROCHOID_RIGHT:
883 delta = -1;
884 x_t10 = s->getX() - (radius * delta) * sin(phi);
885 y_t10 = s->getY() + (radius * delta) * cos(phi);
886 s->setXY(x_t10 + (radius * delta) * sin(delta * (1.0 / radius) * v + phi) + wind_ratio * v,
887 y_t10 - (radius * delta) * cos(delta * (1.0 / radius) * v + phi));
888 s->setYaw(phi + delta * (1.0 / radius) * v);
889 break;
890 case TROCHOID_STRAIGHT:
891 s->setXY(s->getX() + v * cos(phi) + v * wind_ratio, s->getY() + v * sin(phi));
892 break;
893 }
894 }
895 }
896
897 state->as<StateType>()->setX(s->getX() * std::cos(wind_heading) - s->getY() * std::sin(wind_heading) +
898 from->as<StateType>()->getX());
899 state->as<StateType>()->setY(s->getX() * std::sin(wind_heading) + s->getY() * std::cos(wind_heading) +
900 from->as<StateType>()->getY());
901 getSubspace(1)->enforceBounds(s->as<SO2StateSpace::StateType>(1));
902 state->as<StateType>()->setYaw(s->getYaw() + wind_heading);
903 freeState(s);
904}
905
906unsigned int TrochoidStateSpace::validSegmentCount(const State *state1, const State *state2) const
907{
908 return StateSpace::validSegmentCount(state1, state2);
909}
910
911TrochoidStateSpace::PathType TrochoidStateSpace::getPath(const State *state1, const State *state2, bool periodic) const
912{
913 return getPath(state1, state2, rho_, eta_, psi_w_, periodic);
914}
915
916TrochoidStateSpace::PathType TrochoidStateSpace::getPath(const State *state1, const State *state2, double radius,
917 double wind_ratio, double wind_heading, bool periodic)
918{
919 const auto *s1 = static_cast<const TrochoidStateSpace::StateType *>(state1);
920 const auto *s2 = static_cast<const TrochoidStateSpace::StateType *>(state2);
921 double x0 = s1->getX(), y0 = s1->getY(), psi_0 = s1->getYaw();
922 double xf = s2->getX(), yf = s2->getY(), psi_f = s2->getYaw();
923
924 double phi0 = psi_0 - wind_heading;
925 double phif = psi_f - wind_heading;
926
927 // Transform into trochoid frame
928 double x_trochoid = (xf - x0) * std::cos(wind_heading) + (yf - y0) * std::sin(wind_heading);
929 double y_trochoid = -(xf - x0) * std::sin(wind_heading) + (yf - y0) * std::cos(wind_heading);
930
931 return ::getPath(0.0, 0.0, phi0, x_trochoid, y_trochoid, phif, radius, wind_ratio, periodic);
932}
const StateSpacePtr & getSubspace(unsigned int index) const
Get a specific subspace from the compound state space.
bool equalStates(const State *state1, const State *state2) const override
Checks whether two states are equal.
void copyState(State *destination, const State *source) const override
Copy a state to another. The memory of source and destination should NOT overlap.
A state in SE(2): (x, y, yaw).
double getYaw() const
Get the yaw component of the state. This is the rotation in plane, with respect to the Z axis.
double getY() const
Get the Y component of the state.
double getX() const
Get the X component of the state.
State * allocState() const override
Allocate a state that can store a point in the described space.
void freeState(State *state) const override
Free the memory of the allocated state.
ompl::base::State StateType
Define the type of state allocated by this space.
Definition StateSpace.h:78
virtual unsigned int validSegmentCount(const State *state1, const State *state2) const
Count how many segments of the "longest valid length" fit on the motion from state1 to state2.
Definition of an abstract state.
Definition State.h:50
const T * as() const
Cast this instance to a desired type.
Definition State.h:66
Complete description of a Dubins path.
double distance(const State *state1, const State *state2) const override
Computes distance between two states. This function satisfies the properties of a metric if isMetricS...
static const std::vector< std::vector< TrochoidPathSegmentType > > & dubinsPathType()
Dubins path types.
bool isSymmetric_
Whether the distance is "symmetrized".
double psi_w_
Wind direction [0, 2pi].
PathType getPath(const State *state1, const State *state2, bool periodic=false) const
Return a shortest Dubins path from SE(2) state state1 to SE(2) state state2.
unsigned int validSegmentCount(const State *state1, const State *state2) const override
Count how many segments of the "longest valid length" fit on the motion from state1 to state2....
void interpolate(const State *from, const State *to, double t, State *state) const override
Computes the state that lies at time t in [0, 1] on the segment that connects from state to to state....
This namespace contains sampling based planning routines shared by both planning under geometric cons...
std::ostream & operator<<(std::ostream &stream, Cost c)
Output operator for Cost.
Definition Cost.cpp:39