Bench-MR
A Motion Planning Benchmark for Wheeled Mobile Robots
PlannerUtils.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <ompl/geometric/PathGeometric.h>
4#include <ompl/control/PathControl.h>
5#include <cmath>
6#include <iomanip>
8#include "base/Primitives.h"
9
10#if QT_SUPPORT
11#include "gui/QtVisualizer.h"
12#endif
13
14namespace ob = ompl::base;
15namespace og = ompl::geometric;
16
23 public:
24 PlannerUtils() = delete;
25
38 template <typename N>
39 static double slope(const N &x1, const N &y1, const N &x2, const N &y2) {
40 const auto dy = y2 - y1;
41 const auto dx = x2 - x1;
42 return normalizeAngle(std::atan2(dy, dx));
43 }
44
50 static double slope(const Point &a, const Point &b) {
51 const auto dy = b.y - a.y;
52 const auto dx = b.x - a.x;
53 return normalizeAngle(std::atan2(dy, dx));
54 }
55
61 static double slope(const ompl::base::State *a, const ompl::base::State *b) {
62 const auto dy = b->as<State>()->getY() - a->as<State>()->getY();
63 const auto dx = b->as<State>()->getX() - a->as<State>()->getX();
64 return normalizeAngle(std::atan2(dy, dx));
65 }
66
79 static bool equals(const ompl::base::State *a, const ompl::base::State *b) {
80 const auto dy = b->as<State>()->getY() - a->as<State>()->getY();
81 const auto dx = b->as<State>()->getX() - a->as<State>()->getX();
82 const auto dt = b->as<State>()->getYaw() - a->as<State>()->getYaw();
83 return std::abs(dx) <= global::settings.ompl.state_equality_tolerance &&
86 }
87
88 static std::vector<Point> toSteeredPoints(const ob::State *a,
89 const ob::State *b) {
90 return Point::fromPath(
91 og::PathGeometric(global::settings.ompl.space_info, a, b));
92 }
93
104 static bool collides(const std::vector<Point> &path) {
105 std::cout << "Starting.. " << std::endl;
106
107 for (const auto &point : path) {
108 if (global::settings.environment->collides(point)) {
109 std::cout << "check.. " << std::endl;
110 // || global::settings.environment->bilinearDistance(path[i].x,
111 // path[i].y) < 1.5) {
112 return true;
113 }
114 }
115 return false;
116 }
117
130 static bool collides(const ompl::geometric::PathGeometric &path) {
131 for (std::size_t i = 0; i < path.getStateCount(); ++i) {
132 const auto *state = path.getState(i)->as<State>();
133 if (!global::settings.environment->checkValidity(state)) return true;
134 }
135 return false;
136 }
137
151 static bool collides(const ompl::base::State *a, const ompl::base::State *b) {
152#ifdef DEBUG
153 OMPL_DEBUG("Checking for collision between [%f %f] and [%f %f]",
154 a->as<State>()->getX(), a->as<State>()->getY(),
155 b->as<State>()->getX(), b->as<State>()->getY());
156 std::cout << "global::settings.ompl.state_space->validSegmentCount(a, b): "
157 << global::settings.ompl.state_space->validSegmentCount(a, b)
158 << std::endl;
159#endif
160 ompl::geometric::PathGeometric p(global::settings.ompl.space_info, a, b);
161 p = interpolated(p);
162 return collides(p);
163 }
164
180 static bool collides(const ompl::base::State *a, const ompl::base::State *b,
181 og::PathGeometric &path) {
182 // TODO this function currently behaves differently in debug mode
183#ifdef DEBUG
184 OMPL_DEBUG("Checking for collision between [%f %f] and [%f %f]",
185 a->as<State>()->getX(), a->as<State>()->getY(),
186 b->as<State>()->getX(), b->as<State>()->getY());
187 path =
188 ompl::geometric::PathGeometric(global::settings.ompl.space_info, a, b);
189 std::cout << "global::settings.ompl.state_space->validSegmentCount(a, b): "
190 << global::settings.ompl.state_space->validSegmentCount(a, b)
191 << std::endl;
192#endif
193 path = interpolated(path);
194 return collides(path);
195 }
196
208 static bool collides(const std::vector<Point> &path,
209 std::vector<Point> &collisions) {
210 collisions.clear();
211 for (std::size_t i = 1; i < path.size(); ++i) {
212 if (global::settings.environment->collides(path[i].x, path[i].y)) {
213#if QT_SUPPORT
214#ifdef DEBUG
215 // QtVisualizer::drawPath(path, QColor(255, 100, 0, 170));
216#endif
217#endif
218 collisions.emplace_back(path[i]);
219 continue;
220 }
221 }
222#ifdef DEBUG
223 // QtVisualizer::drawPath(path, QColor(150, 200, 0, 70));
224#endif
225 return !collisions.empty();
226 }
227
242 static bool collides(const ompl::base::State *a, const ompl::base::State *b,
243 std::vector<Point> &collisions) {
244 ompl::geometric::PathGeometric p(global::settings.ompl.space_info, a, b);
245#if DEBUG
246 std::cout << "global::settings.ompl.state_space->validSegmentCount(a, b): "
247 << global::settings.ompl.state_space->validSegmentCount(a, b)
248 << std::endl;
249#endif
250 p = interpolated(p);
251 const auto path = Point::fromPath(p, false);
252 return collides(path, collisions);
253 }
254
261 static ompl::geometric::PathGeometric interpolated(
262 ompl::geometric::PathGeometric path) {
263 if (path.getStateCount() < 2) {
264#if DEBUG
265 OMPL_WARN("Tried to interpolate an empty path.");
266#endif
267 return path;
268 }
269 if (path.getStateCount() > global::settings.interpolation_limit) {
270#if DEBUG
271 OMPL_WARN(
272 "Cannot interpolate path with %d nodes (maximal %d are allowed).",
273 path.getStateCount(), global::settings.interpolation_limit.value());
274#endif
275 return path;
276 }
277 if (path.length() > global::settings.max_path_length) {
278#if DEBUG
279 OMPL_WARN("Cannot interpolate path of length %f (maximal %f is allowed).",
280 path.length(), global::settings.max_path_length.value());
281#endif
282 return path;
283 }
284#if DEBUG
285 OMPL_DEBUG("Interpolating path with %d nodes and length %f.",
286 path.getStateCount(), path.length());
287#endif
288 path.interpolate(global::settings.interpolation_limit);
289 return path;
290 }
291
297 static ompl::control::PathControl interpolated(
298 ompl::control::PathControl path) {
299 if (path.getStateCount() < 2) {
300#if DEBUG
301 OMPL_WARN("Tried to interpolate an empty path.");
302#endif
303 return path;
304 }
305 if (path.getStateCount() > global::settings.interpolation_limit) {
306#if DEBUG
307 OMPL_WARN(
308 "Cannot interpolate path with %d nodes (maximal %d are allowed).",
309 path.getStateCount(), global::settings.interpolation_limit.value());
310#endif
311 return path;
312 }
313 if (path.length() > global::settings.max_path_length) {
314#if DEBUG
315 OMPL_WARN("Cannot interpolate path of length %f (maximal %f is allowed).",
316 path.length(), global::settings.max_path_length.value());
317#endif
318 return path;
319 }
320#if DEBUG
321 OMPL_DEBUG("Interpolating path with %d nodes and length %f.",
322 path.getStateCount(), path.length());
323#endif
324 path.interpolate();
325 return path;
326 }
327
328 static void updateAngles(ompl::geometric::PathGeometric &path,
329 bool AverageAngles = true,
330 bool preventCollisions = true) {
331 if (path.getStateCount() < 2) return;
332
333 std::vector<ob::State *> &states(path.getStates());
334
335 double theta_old = states[0]->as<State>()->getYaw();
336 states[0]->as<State>()->setYaw(slope(states[0], states[1]));
337 if (preventCollisions && collides(states[0], states[1]))
338 states[0]->as<State>()->setYaw(theta_old); // revert setting
339 for (int i = 1; i < path.getStateCount() - 1; ++i) {
340#ifdef DEBUG
341 OMPL_DEBUG("UpdateAngles: Round %d / %d", i, path.getStateCount() - 2);
342#endif
343 theta_old = states[i]->as<State>()->getYaw();
344 if (AverageAngles) {
345 double l = slope(states[i - 1], states[i]);
346 double r = slope(states[i], states[i + 1]);
347 // if (std::abs(l - r) >= M_PI) {
348 // if (l > r)
349 // l += 2. * M_PI;
350 // else
351 // r += 2. * M_PI;
352 // }
353 states[i]->as<State>()->setYaw((l + r) * 0.5);
354 } else
355 states[i]->as<State>()->setYaw(slope(states[i - 1], states[i]));
356
357 if (preventCollisions && (collides(states[i - 1], states[i]) ||
358 collides(states[i], states[i + 1])))
359 states[i]->as<State>()->setYaw(theta_old); // revert setting
360 }
361 theta_old = states[path.getStateCount() - 1]->as<State>()->getYaw();
362 states[path.getStateCount() - 1]->as<State>()->setYaw(slope(
363 states[path.getStateCount() - 2], states[path.getStateCount() - 1]));
364 if (preventCollisions && collides(states[path.getStateCount() - 1],
365 states[path.getStateCount() - 2]))
366 states[path.getStateCount() - 1]->as<State>()->setYaw(
367 theta_old); // revert setting
368 }
369
370 static void gradientDescent(ompl::geometric::PathGeometric &path,
371 unsigned int rounds, double eta,
372 double discount = 1.) {
373 double dx, dy;
374 for (int round = 0; round < rounds; ++round) {
375 // gradient descent along distance field, excluding start/end nodes
376 for (int i = 1; i < path.getStateCount() - 1; ++i) {
377 // compute gradient
378 global::settings.environment->distanceGradient(
379 path.getStates()[i]->as<State>()->getX(),
380 path.getStates()[i]->as<State>()->getY(), dx, dy, 1.);
381 double distance = global::settings.environment->bilinearDistance(
382 path.getStates()[i]->as<State>()->getX(),
383 path.getStates()[i]->as<State>()->getY());
384 distance = std::max(.1, distance);
385 path.getStates()[i]->as<State>()->setX(
386 path.getStates()[i]->as<State>()->getX() - eta * dx / distance);
387 path.getStates()[i]->as<State>()->setY(
388 path.getStates()[i]->as<State>()->getY() + eta * dy / distance);
389 }
390 eta *= discount;
391 }
392 }
393
394 static void gradientDescent(std::vector<Point> &path, unsigned int rounds,
395 double eta, double discount = 1.) {
396 double dx, dy;
397 for (int round = 0; round < rounds; ++round) {
398 // gradient descent along distance field, excluding start/end nodes
399 for (int i = 1; i < path.size() - 1; ++i) {
400 // compute gradient
401 global::settings.environment->distanceGradient(path[i].x, path[i].y, dx,
402 dy, 1.);
403 double distance = global::settings.environment->bilinearDistance(
404 path[i].x, path[i].y);
405 distance = std::max(.1, distance);
406 path[i].x -= eta * dx / distance;
407 path[i].y += eta * dy / distance;
408 }
409 eta *= discount;
410 }
411 }
412
421 static std::vector<Point> linearInterpolate(const Point &a, const Point &b,
422 double dt = 0.1) {
423 std::vector<Point> points;
424 double dx = (b.x - a.x);
425 double dy = (b.y - a.y);
426 const double size = std::sqrt(dx * dx + dy * dy);
427 if (size == 0) return std::vector<Point>{a};
428 dx = dx / size * dt;
429 dy = dy / size * dt;
430 const auto steps = (int)(size / std::sqrt(dx * dx + dy * dy));
431
432 for (int j = 1; j <= steps; ++j)
433 points.emplace_back(a.x + dx * j, a.y + dy * j);
434
435 if (points.empty()) points.emplace_back(a);
436
437 return points;
438 }
439
450 static std::vector<Point> linearInterpolate(ompl::base::State *a,
452 double dt = 0.1) {
453 return linearInterpolate(
454 Point(a->as<State>()->getX(), a->as<State>()->getY()),
455 Point(b->as<State>()->getX(), b->as<State>()->getY()), dt);
456 }
457
469 const std::vector<Point> &points) {
470 if (points.size() == 1) return points[0].toState();
471 unsigned int closest = 0;
472 double dist = points[closest].distanceSquared(x);
473 for (unsigned int i = 1; i < points.size() - 1; ++i) {
474 if (global::settings.environment->collides(points[i].x, points[i].y))
475 continue;
476 const double d = points[i].distanceSquared(x);
477 if (d < dist) {
478 dist = d;
479 closest = i;
480 }
481 }
482 double theta;
483 if (closest == 0)
484 theta = slope(points[0], points[1]);
485 else
486 theta = slope(points[closest - 1], points[closest + 1]);
487 return points[closest].toState(theta);
488 }
489
499 static double totalLength(const std::vector<Point> &path) {
500 double l = 0;
501 for (size_t i = 1; i < path.size(); ++i) {
502 l += path[i].distance(path[i - 1]);
503 }
504 return l;
505 }
506
514 static std::vector<Point> equidistantSampling(const std::vector<Point> &path,
515 size_t targetSize) {
516 std::vector<Point> result;
517 const double total = totalLength(path);
518 const double targetSegment = total / targetSize;
519 result.emplace_back(path[0]);
520 double sofar = 0;
521 double segment = 0;
522 size_t resultCounter = 1;
523 for (size_t i = 1; i < path.size(); ++i) {
524 double dx = (path[i].x - path[i - 1].x);
525 double dy = (path[i].y - path[i - 1].y);
526 const double l = std::sqrt(dx * dx + dy * dy);
527 if (std::abs(l) < 1e-3) continue;
528 dx /= l;
529 dy /= l;
530 if (segment + l < targetSegment) {
531 segment += l;
532 OMPL_DEBUG("EquidistantSampling: Segment too short between %i and %i",
533 i - 1, i);
534 continue;
535 }
536 double start = std::fmod(segment + l, targetSegment) - segment;
537 segment = 0;
538 const auto segmentSteps =
539 static_cast<unsigned int>(std::floor((segment + l) / targetSegment));
540 for (unsigned int j = 0; j < segmentSteps; ++j) {
541 const double alpha = start + j * targetSegment;
542 const Point p(path[i - 1].x + dx * alpha, path[i - 1].y + dy * alpha);
543 result.emplace_back(p);
544 }
545 sofar += segmentSteps * targetSegment;
546 segment = l - segmentSteps * targetSegment;
547 }
548 result.emplace_back(path.back());
549 return result;
550 }
551
559 template <typename N>
560 static std::string num2str(const N &v, unsigned int precision = 3) {
561 std::stringstream stream;
562 stream << std::fixed << std::setprecision(precision) << v;
563 return stream.str();
564 }
565
572 inline static double normalizeAngle(double angle) {
573 return std::atan2(std::sin(angle), std::cos(angle));
574 }
575};
ob::SE2StateSpace::StateType State
Definition: Primitives.h:12
Helper for common operations during planning and evaluation.
Definition: PlannerUtils.hpp:22
static bool collides(const std::vector< Point > &path, std::vector< Point > &collisions)
Check whether points on a path collide with the environment.
Definition: PlannerUtils.hpp:208
static std::vector< Point > linearInterpolate(const Point &a, const Point &b, double dt=0.1)
Linearly interpolate between two points.
Definition: PlannerUtils.hpp:421
static double slope(const Point &a, const Point &b)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition: PlannerUtils.hpp:50
static double slope(const ompl::base::State *a, const ompl::base::State *b)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition: PlannerUtils.hpp:61
PlannerUtils()=delete
static std::string num2str(const N &v, unsigned int precision=3)
Convert a number to a string with the given precision.
Definition: PlannerUtils.hpp:560
static ompl::geometric::PathGeometric interpolated(ompl::geometric::PathGeometric path)
Interpolate path based on its associated state space.
Definition: PlannerUtils.hpp:261
static bool collides(const ompl::base::State *a, const ompl::base::State *b, og::PathGeometric &path)
Collision check that respects the collision model.
Definition: PlannerUtils.hpp:180
static void gradientDescent(std::vector< Point > &path, unsigned int rounds, double eta, double discount=1.)
Definition: PlannerUtils.hpp:394
static double totalLength(const std::vector< Point > &path)
Compute total length of a path.
Definition: PlannerUtils.hpp:499
static bool equals(const ompl::base::State *a, const ompl::base::State *b)
Check whether two states are equal (up to some tolerance).
Definition: PlannerUtils.hpp:79
static bool collides(const ompl::geometric::PathGeometric &path)
Collision check that respects the collision model.
Definition: PlannerUtils.hpp:130
static void updateAngles(ompl::geometric::PathGeometric &path, bool AverageAngles=true, bool preventCollisions=true)
Definition: PlannerUtils.hpp:328
static bool collides(const std::vector< Point > &path)
Check whether points on a path collide with the environment.
Definition: PlannerUtils.hpp:104
static ompl::control::PathControl interpolated(ompl::control::PathControl path)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition: PlannerUtils.hpp:297
static bool collides(const ompl::base::State *a, const ompl::base::State *b, std::vector< Point > &collisions)
Collision check of segment between two states.
Definition: PlannerUtils.hpp:242
static std::vector< Point > linearInterpolate(ompl::base::State *a, ompl::base::State *b, double dt=0.1)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition: PlannerUtils.hpp:450
static std::vector< Point > equidistantSampling(const std::vector< Point > &path, size_t targetSize)
Compute equidistance samples along path.
Definition: PlannerUtils.hpp:514
static ompl::base::State * closestPoint(Point x, const std::vector< Point > &points)
Find closest collision-free point in an ordered set of points.
Definition: PlannerUtils.hpp:468
static bool collides(const ompl::base::State *a, const ompl::base::State *b)
Collision check of segment between two states.
Definition: PlannerUtils.hpp:151
static double slope(const N &x1, const N &y1, const N &x2, const N &y2)
Compute the angular slope between two points.
Definition: PlannerUtils.hpp:39
static std::vector< Point > toSteeredPoints(const ob::State *a, const ob::State *b)
Definition: PlannerUtils.hpp:88
static void gradientDescent(ompl::geometric::PathGeometric &path, unsigned int rounds, double eta, double discount=1.)
Definition: PlannerUtils.hpp:370
static double normalizeAngle(double angle)
Normalize angle in radians to the interval .
Definition: PlannerUtils.hpp:572
double max(const std::vector< double > &values)
Definition: PathStatistics.hpp:79
Property< double > state_equality_tolerance
Definition: PlannerSettings.h:416
ompl::base::StateSpacePtr state_space
Definition: PlannerSettings.h:405
std::shared_ptr< Environment > environment
Environment used for planning.
Definition: PlannerSettings.h:73
PlannerSettings::GlobalSettings::OmplSettings ompl
Property< double > max_path_length
Maximal length a og::PathGeometric can have to be interpolated.
Definition: PlannerSettings.h:272
Property< unsigned int > interpolation_limit
Any og::PathGeometric with more nodes will not get interpolated.
Definition: PlannerSettings.h:267
Definition: Primitives.h:42
static std::vector< Point > fromPath(const ompl::geometric::PathGeometric &p, bool interpolate=false)
Definition: Primitives.cpp:25
double x
Definition: Primitives.h:43
double y
Definition: Primitives.h:43
static PlannerSettings::GlobalSettings settings
Definition: PlannerSettings.h:699