-#ifndef RRTS_H
-#define RRTS_H
+/*! \file */
+#ifndef RRTS_RRTS_H
+#define RRTS_RRTS_H
#include <chrono>
#include <functional>
#include <json/json.h>
#include <random>
#include <vector>
-#include "bcar.h"
-
-#define ETA 1.0 // for steer, nv
-#define GAMMA(cV) ({ \
- __typeof__ (cV) _cV = (cV); \
- pow(log(_cV) / _cV, 1.0 / 3.0); \
-})
-
-/*! \brief Possible type of RRT node.
-
-\param cusp The node that is cusp (change in direction).
-\param connected The node that branches generated steered path.
-*/
-class RRTNodeType {
- public:
- static const unsigned int cusp = 1 << 0;
- static const unsigned int connected = 1 << 1;
+#include "bcar.hh"
+
+namespace rrts {
+using namespace bcar;
+
+/*! Compute elapsed time class. */
+class Ter {
+private:
+ std::chrono::high_resolution_clock::time_point tstart_;
+public:
+ void start();
+ double scnt() const;
};
-/*! \brief RRT node basic class.
-
-\param c Cumulative cost from RRT data structure root.
-\param p Pointer to parent RRT node.
-\param t Type of the RRT node (RRTNodeType).
-// -- from BicycleCar
-\param x Horizontal coordinate of rear axle center.
-\param y Vertical coordinate of rear axle center.
-\param h Heading of the car in the interval [-pi,+pi] radians.
-\param sp Speed of the car.
-\param st Steering of the car.
-*/
-class RRTNode {
- private:
- RRTNode *p_ = nullptr;
- unsigned int t_ = 0;
- // -- from BicycleCar
- // coordinates
- double x_ = 0;
- double y_ = 0;
- double h_ = 0;
- // moving
- double sp_ = 0;
- double st_ = 0;
- public:
- double c_ = 0;
- double cc = 0.0;
- // getters, setters
- double c() const { return this->c_; }
- void c(double c)
- {
- this->c_ = c;
- this->cc = this->p_->cc + this->c_;
- }
-
- RRTNode *p() const { return this->p_; }
- void p(RRTNode *p) { this->p_ = p; }
-
- bool t(unsigned int flag) { return this->t_ & flag; }
- void set_t(unsigned int flag) { this->t_ |= flag; }
- void clear_t(unsigned int flag) { this->t_ &= ~flag; }
-
- // -- from BicycleCar
- // getters, setters
- double x() const { return this->x_; }
- void x(double x) { this->x_ = x; }
+/*! Store RRT node. */
+class RRTNode : public virtual Pose, public virtual CarMove {
+private:
+ double c_ = 0.0;
+ double cc_ = 0.0;
+ RRTNode* p_ = nullptr;
+public:
+ /*! Get cost to parent. */
+ double c() const;
- double y() const { return this->y_; }
- void y(double y) { this->y_ = y; }
+ /*! Set cost to parent. */
+ void c(double c);
- double h() const { return this->h_; }
- void h(double h)
- {
- while (h < -M_PI)
- h += 2 * M_PI;
- while (h > +M_PI)
- h -= 2 * M_PI;
- this->h_ = h;
- }
+ /*! Get cumulative cost from root. */
+ double cc() const;
- double sp() const { return this->sp_; }
- void sp(double sp) { this->sp_ = sp; }
+ /*! Get parent node. */
+ RRTNode* p() const;
- double st() const { return this->st_; }
- void st(double st) { this->st_ = st; }
+ /*! Set parent node. */
+ void p(RRTNode& p);
- RRTNode();
- RRTNode(const BicycleCar &bc);
- bool operator==(const RRTNode& n);
- friend std::ostream &operator<<(
- std::ostream &out,
- const RRTNode &bc
- )
- {
- out << "[" << bc.x();
- out << "," << bc.y();
- out << "," << bc.h();
- out << "]";
- return out;
- }
+ bool operator==(RRTNode const& n);
};
-/*! \brief Polygon obstacle basic class.
-
-\param poly Border polygon of the obstacle.
-*/
-class Obstacle {
- private:
- std::vector<std::tuple<double, double>> poly_;
- public:
- // getters, setters
- std::vector<std::tuple<double, double>> &poly()
- {
- return this->poly_;
- }
-
- Obstacle();
+class RRTGoal : public virtual RRTNode, public virtual PoseRange {
};
-/*! \brief RRT* algorithm basic class.
-
-\param icnt RRT algorithm iterations counter.
-\param goals The vector of goal nodes.
-\param nodes The vector of all nodes in RRT data structure.
-\param samples The vector of all samples of RRT algorithm.
-\param sample_dist_type Random distribution type for sampling function (0 -
-normal, 1 - uniform, 2 - uniform circle)
-*/
+/*! RRT* algorithm basic class. */
class RRTS {
- protected:
- unsigned int icnt_ = 0;
- std::chrono::high_resolution_clock::time_point tstart_;
- double scnt_ = 0;
- double pcnt_ = 0;
- bool gf_ = false;
- int sample_dist_type_ = 0;
-
- std::vector<RRTNode> goals_;
- std::vector<RRTNode> nodes_;
- std::vector<Obstacle> obstacles_;
- std::vector<RRTNode> samples_;
- std::vector<RRTNode> steered_;
- std::vector<RRTNode *> path_;
- double log_path_time_ = 0.1;
- unsigned int log_path_iter_ = 20;
-
- /*! \brief Update and return elapsed time.
- */
- double elapsed();
- /*! \brief Log current path cost.
- */
- void log_path_cost();
- /*! \brief Set normal distribution for sampling.
- */
- void set_sample_normal(
- double x1, double x2,
- double y1, double y2,
- double h1, double h2
- );
- /*! \brief Set uniform distribution for sampling.
- */
- void set_sample_uniform(
- double x1, double x2,
- double y1, double y2,
- double h1, double h2
- );
- /*! \brief Set uniform circle distribution for sampling.
- */
- void set_sample_uniform_circle();
- RRTNode* use_nn; // Used for RRTExt12.
- std::vector<RRTNode> tmp_steered_;
- bool finishit = false;
- double path_cost_before_opt_ = 9999;
-
- BicycleCar bc;
- /*! \brief Store RRT node to tree data structure.
- */
- virtual void store_node(RRTNode n);
-
- // RRT procedures
- std::tuple<bool, unsigned int, unsigned int>
- collide(std::vector<std::tuple<double, double>> &poly);
- virtual std::tuple<bool, unsigned int, unsigned int>
- collide_steered_from(RRTNode &f);
- virtual std::tuple<bool, unsigned int, unsigned int>
- collide_tmp_steered_from(RRTNode &f);
- virtual std::tuple<bool, unsigned int, unsigned int>
- collide_two_nodes(RRTNode &f, RRTNode &t);
- void sample();
- std::default_random_engine gen_;
- std::normal_distribution<double> ndx_;
- std::normal_distribution<double> ndy_;
- std::normal_distribution<double> ndh_;
- std::uniform_real_distribution<double> udx_;
- std::uniform_real_distribution<double> udy_;
- std::uniform_real_distribution<double> udh_;
- std::uniform_int_distribution<unsigned int> udi1_;
- std::uniform_int_distribution<unsigned int> udi2_;
- virtual RRTNode *nn(RRTNode &t);
- virtual std::vector<RRTNode *> nv(RRTNode &t);
- void steer(RRTNode &f, RRTNode &t);
- void tmp_steer(RRTNode &f, RRTNode &t);
- virtual void steer1(RRTNode &f, RRTNode &t);
- virtual void steer2(RRTNode &f, RRTNode &t);
- /*! \brief Join steered nodes to RRT data structure
-
- \param f RRT node to join steered nodes to.
- */
- void join_steered(RRTNode *f);
- void join_tmp_steered(RRTNode *f);
- virtual bool goal_found(RRTNode &f);
- // RRT* procedures
- virtual bool connect();
- void rewire();
- public:
- /// ---
- std::vector<double> log_opt_time_;
- std::vector<double> log_path_cost_;
- struct { double x=0; double y=0; double b=0; double e=0; } entry;
- bool entry_set = false;
- struct { double x=0; double y=0; double h=0; } entry1;
- struct { double x=0; double y=0; double h=0; } entry2;
- bool entries_set = false;
- std::vector<RRTNode *> steered1_;
- std::vector<RRTNode *> steered2_;
- /// ---
- /*! \brief Initialize RRT algorithm if needed.
- */
- virtual void init();
- virtual void reset();
- /*! \brief Deinitialize RRT algorithm if needed.
- */
- virtual void deinit();
- /*! \brief Return path found by RRT*.
- */
- virtual std::vector<RRTNode *>& path()
- {
- return this->path_;
- }
- virtual void compute_path();
- /*! \brief Return ``true`` if algorithm should stop.
-
- Update counters (iteration, seconds, ...) and return if
- the current iteration should be the last one.
- */
- bool should_stop();
- /*! \brief Return ``true`` if the algorithm should finish.
-
- Finish means that the algorithm will not be resumed.
- */
- bool should_finish();
- /*! \brief Return ``true`` if the algorithm shoud break.
-
- Break means that the algorithm can be resumed.
- */
- bool should_break();
- /*! \brief Return ``true`` if algorithm should continue.
-
- `pcnt_` is set to `scnt_`, so the difference is 0 and it can
- start from scratch. After the `should_continue` is called,
- there must be `while (rrts.next()) {}` loop.
- */
- bool should_continue();
- /*! \brief Run next RRT* iteration.
- */
- virtual bool next();
- /*! \brief Set sampling info.
-
- Based on `sample_dist_type`, set proper distribution
- parameters. The distribution parameters are relative to `front`
- node in `nodes` (init).
-
- For normal sampling:
- \param x1 Mean x value.
- \param x2 Standard deviation of x.
- \param y1 Mean y value.
- \param y2 Standard deviation of y.
- \param h1 Mean h value.
- \param h2 Standard deviation of h.
-
- For uniform sampling:
- \param x1 Minimum x value.
- \param x2 Maximum x value.
- \param y1 Minimum y value.
- \param y2 Maximum y value.
- \param h1 Minimum h value.
- \param h2 Maximum h value.
-
- For uniform circle sampling:
- \param x1 Ignored.
- \param x2 Ignored.
- \param y1 Ignored.
- \param y2 Ignored.
- \param h1 Ignored.
- \param h2 Ignored.
- */
- void set_sample(
- double x1, double x2,
- double y1, double y2,
- double h1, double h2
- );
- /*! \brief Generate JSON output.
- */
- Json::Value json();
- /*! \brief Load JSON input.
- */
- void json(Json::Value jvi);
-
- // RRT procedures
- virtual double cost_build(RRTNode &f, RRTNode &t);
- virtual double cost_search(RRTNode &f, RRTNode &t);
-
- // getters, setters
- unsigned int icnt() const { return this->icnt_; }
- void icnt(unsigned int i) { this->icnt_ = i; }
- double scnt() const { return this->scnt_; }
- bool gf() const { return this->gf_; }
- void gf(bool f) { this->gf_ = f; }
- int sample_dist_type() const { return this->sample_dist_type_;}
- void sample_dist_type(int t) { this->sample_dist_type_ = t; }
- std::vector<RRTNode> &goals() { return this->goals_; }
- std::vector<RRTNode> &nodes() { return this->nodes_; }
- std::vector<Obstacle> &obstacles() { return this->obstacles_; }
- std::vector<RRTNode> &samples() { return this->samples_; }
- std::vector<RRTNode> &steered() { return this->steered_; }
-
- RRTS();
+protected:
+ BicycleCar bc_;
+ RRTGoal goal_;
+ unsigned int icnt_ = 0;
+ Ter ter_;
+ std::default_random_engine gen_;
+ std::vector<RRTNode> nodes_;
+ std::vector<RRTNode> steered_;
+ std::vector<RRTNode*> path_;
+ RRTNode* nn_ = nullptr;
+ std::vector<RRTNode*> nv_;
+ double cost_ = 0.0;
+ double eta_ = 0.5;
+ double time_ = 0.0;
+ double min_gamma_eta() const;
+ bool should_continue() const;
+ void join_steered(RRTNode* f);
+ RRTNode& nn();
+ bool connect();
+ void rewire();
+ bool goal_drivable_from(RRTNode const& f);
+ virtual void store(RRTNode n);
+ virtual double cost_build(RRTNode const& f, RRTNode const& t) const;
+ virtual double cost_search(RRTNode const& f, RRTNode const& t) const;
+ virtual void find_nn(RRTNode const& t);
+ virtual void find_nv(RRTNode const& t);
+ virtual void compute_path();
+ virtual void steer(RRTNode const& f, RRTNode const& t) = 0;
+ virtual bool collide_steered() = 0;
+ virtual RRTNode sample() = 0;
+ virtual bool should_finish() const = 0;
+public:
+ RRTS();
+
+ /*! Get iterations counter. */
+ unsigned int icnt() const;
+
+ /*! Set iterations counter. */
+ void icnt(unsigned int i);
+
+ /*! Return elapsed time. */
+ double scnt() const;
+
+ /*! Generate JSON output. */
+ Json::Value json() const;
+
+ /*! Load JSON input. */
+ void json(Json::Value jvi);
+
+ /*! Run next RRT* iteration. */
+ virtual bool next();
+
+ /*! Reset the algorithm. */
+ virtual void reset();
};
-/*! \brief Compute cumulative cost of RRT node.
-
-\param t RRT node to compute cumulative cost to.
-*/
-double cc(RRTNode &t);
-
-#endif /* RRTS_H */
+} // namespace rrts
+#endif /* RRTS_RRTS_H */
#include <algorithm>
-#include "rrts.h"
+#include <cassert>
+#include "rrts.hh"
-#include "reeds_shepp.h"
+namespace rrts {
-template <typename T> int sgn(T val) {
- return (T(0) < val) - (val < T(0));
-}
-
-RRTNode::RRTNode()
-{
-}
-
-RRTNode::RRTNode(const BicycleCar &bc)
-{
- this->x(bc.x());
- this->y(bc.y());
- this->h(bc.h());
- this->sp(bc.sp());
- this->st(bc.st());
-}
-
-bool RRTNode::operator==(const RRTNode& n)
+void
+Ter::start()
{
- if (this == &n)
- return true;
- return false;
+ this->tstart_ = std::chrono::high_resolution_clock::now();
}
-Obstacle::Obstacle()
+double
+Ter::scnt() const
{
+ using namespace std::chrono;
+ auto t = high_resolution_clock::now() - this->tstart_;
+ auto d = duration_cast<duration<double>>(t);
+ return d.count();
}
-double RRTS::elapsed()
+double
+RRTNode::c() const
{
- std::chrono::duration<double> dt;
- dt = std::chrono::duration_cast<std::chrono::duration<double>>(
- std::chrono::high_resolution_clock::now()
- - this->tstart_
- );
- this->scnt_ = dt.count();
- return this->scnt_;
+ return this->c_;
}
-void RRTS::log_path_cost()
+void
+RRTNode::c(double c)
{
- if (this->log_path_cost_.size() == 0) {
- this->log_path_cost_.push_back(this->goals().front().cc);
- } else {
- auto lc = this->log_path_cost_.back();
- auto gc = this->goals().front().cc;
- auto goal_is_better = this->goals().front().cc > 0 && lc < gc;
- if (
- this->log_path_cost_.back() > 0
- && (
- this->goals().front().cc == 0
- || (
- this->goals().front().cc > 0
- && goal_is_better
- )
- )
- ) {
- this->log_path_cost_.push_back(
- this->log_path_cost_.back()
- );
- } else {
- this->log_path_cost_.push_back(
- this->goals().front().cc
- );
- }
- }
- this->log_path_iter_ += 1;
+ assert(this->p_ != nullptr);
+ this->c_ = c;
+ this->cc_ = this->p_->cc() + c;
}
-bool RRTS::should_stop()
+double
+RRTNode::cc() const
{
- // the following counters must be updated, do not comment
- this->icnt_++;
- this->elapsed();
- // current iteration stop conditions
- if (this->should_finish()) return true;
- if (this->should_break()) return true;
- // but continue by default
- return false;
+ return this->cc_;
}
-bool RRTS::should_finish()
+RRTNode*
+RRTNode::p() const
{
- // decide finish conditions (maybe comment some lines)
- if (this->icnt_ > 1000) return true;
- //if (this->scnt_ > 2) return true;
- if (this->finishit) return true;
- if (this->gf()) return true;
- // but continue by default
- return false;
+ return this->p_;
}
-bool RRTS::should_break()
+void
+RRTNode::p(RRTNode& p)
{
- // decide break conditions (maybe comment some lines)
- //if (this->scnt_ - this->pcnt_ > 2) return true;
- // but continue by default
- return false;
+ if (this != &p) {
+ this->p_ = &p;
+ }
}
-bool RRTS::should_continue()
+bool
+RRTNode::operator==(RRTNode const& n)
{
- // decide the stop conditions (maybe comment some lines)
- // it is exact opposite of `should_stop`
- //if (this->icnt_ > 999) return false;
- if (this->scnt_ > 10) return false;
- if (this->gf()) return false;
- // and reset pause counter if should continue
- this->pcnt_ = this->scnt_;
- return true;
+ return this == &n;
}
-void RRTS::store_node(RRTNode n)
+double
+RRTS::min_gamma_eta() const
{
- this->nodes().push_back(n);
+ double ns = this->nodes_.size();
+ double gamma = pow(log(ns) / ns, 1.0 / 3.0);
+ return std::min(gamma, this->eta_);
}
-// RRT procedures
-std::tuple<bool, unsigned int, unsigned int>
-RRTS::collide(std::vector<std::tuple<double, double>> &poly)
+bool
+RRTS::should_continue() const
{
- for (auto &o: this->obstacles())
- if (std::get<0>(::collide(poly, o.poly())))
- return ::collide(poly, o.poly());
- return std::make_tuple(false, 0, 0);
+ return !this->should_finish();
}
-std::tuple<bool, unsigned int, unsigned int>
-RRTS::collide_steered_from(RRTNode &f)
+void
+RRTS::join_steered(RRTNode* f)
{
- auto fbc = BicycleCar();
- fbc.x(f.x());
- fbc.y(f.y());
- fbc.h(f.h());
- std::vector<std::tuple<double, double>> s;
- s.push_back(std::make_tuple(fbc.x(), fbc.y()));
- for (auto &n: this->steered()) {
- auto nbc = BicycleCar();
- nbc.x(n.x());
- nbc.y(n.y());
- nbc.h(n.h());
- s.push_back(std::make_tuple(nbc.lfx(), nbc.lfy()));
- s.push_back(std::make_tuple(nbc.lrx(), nbc.lry()));
- s.push_back(std::make_tuple(nbc.rrx(), nbc.rry()));
- s.push_back(std::make_tuple(nbc.rfx(), nbc.rfy()));
- }
- auto col = this->collide(s);
- auto strip_from = this->steered().size() - std::get<1>(col) / 4;
- if (std::get<0>(col) && strip_from > 0) {
- while (strip_from-- > 0) {
- this->steered().pop_back();
- }
- return this->collide_steered_from(f);
+ while (this->steered_.size() > 0) {
+ this->store(this->steered_.front());
+ RRTNode* t = &this->nodes_.back();
+ t->p(*f);
+ t->c(this->cost_build(*f, *t));
+ this->steered_.erase(this->steered_.begin());
+ f = t;
}
- return col;
-}
-std::tuple<bool, unsigned int, unsigned int>
-RRTS::collide_tmp_steered_from(RRTNode &f)
-{
- return std::make_tuple(false, 0, 0);
-}
-
-std::tuple<bool, unsigned int, unsigned int>
-RRTS::collide_two_nodes(RRTNode &f, RRTNode &t)
-{
- auto fbc = BicycleCar();
- fbc.x(f.x());
- fbc.y(f.y());
- fbc.h(f.h());
- auto tbc = BicycleCar();
- tbc.x(f.x());
- tbc.y(f.y());
- tbc.h(f.h());
- std::vector<std::tuple<double, double>> p;
- p.push_back(std::make_tuple(fbc.lfx(), fbc.lfy()));
- p.push_back(std::make_tuple(fbc.lrx(), fbc.lry()));
- p.push_back(std::make_tuple(fbc.rrx(), fbc.rry()));
- p.push_back(std::make_tuple(fbc.rfx(), fbc.rfy()));
- p.push_back(std::make_tuple(tbc.lfx(), tbc.lfy()));
- p.push_back(std::make_tuple(tbc.lrx(), tbc.lry()));
- p.push_back(std::make_tuple(tbc.rrx(), tbc.rry()));
- p.push_back(std::make_tuple(tbc.rfx(), tbc.rfy()));
- return this->collide(p);
-}
-
-double RRTS::cost_build(RRTNode &f, RRTNode &t)
-{
- double cost = 0;
- cost = sqrt(pow(t.y() - f.y(), 2) + pow(t.x() - f.x(), 2));
- return cost;
}
-double RRTS::cost_search(RRTNode &f, RRTNode &t)
+RRTNode&
+RRTS::nn()
{
- double cost = 0;
- cost = sqrt(pow(t.y() - f.y(), 2) + pow(t.x() - f.x(), 2));
- return cost;
+ return *this->nn_;
}
-void RRTS::sample()
+bool
+RRTS::connect()
{
- double x = 0;
- double y = 0;
- double h = 0;
- switch (this->sample_dist_type()) {
- case 1: // uniform
- x = this->udx_(this->gen_);
- y = this->udy_(this->gen_);
- h = this->udh_(this->gen_);
- break;
- case 2: // uniform circle
- {
- // see https://stackoverflow.com/questions/5837572/generate-a-random-point-within-a-circle-uniformly/50746409#50746409
- double R = sqrt(
- pow(
- this->nodes().front().x()
- - this->goals().front().x(),
- 2
- )
- + pow(
- this->nodes().front().y()
- - this->goals().front().y(),
- 2
- )
- );
- double a = atan2(
- this->goals().front().y() - this->nodes().front().y(),
- this->goals().front().x() - this->nodes().front().x()
- );
- double cx = this->goals().front().x() - R/2 * cos(a);
- double cy = this->goals().front().y() - R/2 * sin(a);
- double r = R * sqrt(this->udx_(this->gen_));
- double theta = this->udy_(this->gen_) * 2 * M_PI;
- x = cx + r * cos(theta);
- y = cy + r * sin(theta);
- h = this->udh_(this->gen_);
- }
- break;
- case 3: {
- if (
- this->steered1_.size() == 0
- && this->steered2_.size() == 0
- ) {
- x = this->nodes().front().x();
- y = this->nodes().front().y();
- h = this->nodes().front().h();
- this->use_nn = &this->nodes().front();
- } else {
- this->udi1_ = std::uniform_int_distribution<unsigned int>(
- 0,
- this->steered1_.size() - 1
- );
- this->udi2_ = std::uniform_int_distribution<unsigned int>(
- 0,
- this->steered2_.size() - 1
- );
- auto ind1 = this->udi1_(this->gen_);
- auto ind2 = this->udi2_(this->gen_);
- if (
- this->steered2_.size() == 0
- ) {
- auto n1 = this->steered1_[ind1];
- x = n1->x();
- y = n1->y();
- h = n1->h();
- this->use_nn = this->steered1_[ind1];
- } else if (
- this->steered1_.size() == 0
- ) {
- auto n2 = this->steered2_[ind2];
- x = n2->x();
- y = n2->y();
- h = n2->h();
- this->use_nn = this->steered2_[ind2];
- } else {
- auto n1 = this->steered1_[ind1];
- auto n2 = this->steered2_[ind2];
- auto which = this->udx_(this->gen_);
- if (which > 0.5) {
- x = n1->x();
- y = n1->y();
- h = n1->h();
- this->use_nn = this->steered1_[ind1];
- } else {
- x = n2->x();
- y = n2->y();
- h = n2->h();
- this->use_nn = this->steered2_[ind2];
- }
- }
- }
- break;
+ RRTNode* f = this->nn_;
+ RRTNode* t = &this->steered_.front();
+ double cost = f->cc() + this->cost_build(*f, *t);
+ for (auto n: this->nv_) {
+ double nc = n->cc() + this->cost_build(*n, *t);
+ if (nc < cost) {
+ f = n;
+ cost = nc;
}
- default: // normal
- x = this->ndx_(this->gen_);
- y = this->ndy_(this->gen_);
- h = this->ndh_(this->gen_);
}
- this->samples().push_back(RRTNode());
- this->samples().back().x(x);
- this->samples().back().y(y);
- this->samples().back().h(h);
+ // Check if it's possible to drive from *f to *t. If not, then fallback
+ // to *f = nn_. This could be also solved by additional steer from *f to
+ // *t instead of the following code.
+ this->bc_.set_pose(*f);
+ if (!this->bc_.drivable(*t)) {
+ f = this->nn_;
+ }
+ this->store(this->steered_.front());
+ t = &this->nodes_.back();
+ t->p(*f);
+ t->c(this->cost_build(*f, *t));
+ this->steered_.erase(this->steered_.begin());
+ return true;
}
-RRTNode *RRTS::nn(RRTNode &t)
+void
+RRTS::rewire()
{
- RRTNode *nn = &this->nodes().front();
- double cost = this->cost_search(*nn, t);
- for (auto &f: this->nodes()) {
- if (this->cost_search(f, t) < cost) {
- nn = &f;
- cost = this->cost_search(f, t);
+ RRTNode *f = &this->nodes_.back();
+ for (auto n: this->nv_) {
+ double fc = f->cc() + this->cost_build(*f, *n);
+ this->bc_.set_pose(*f);
+ bool drivable = this->bc_.drivable(*n);
+ if (drivable && fc < n->cc()) {
+ n->p(*f);
+ n->c(this->cost_build(*f, *n));
}
}
- return nn;
-}
-
-std::vector<RRTNode *> RRTS::nv(RRTNode &t)
-{
- std::vector<RRTNode *> nv;
- double cost = std::min(GAMMA(this->nodes().size()), ETA);
- for (auto &f: this->nodes())
- if (this->cost_search(f, t) < cost)
- nv.push_back(&f);
- return nv;
}
-int cb_rs_steer(double q[4], void *user_data)
+bool
+RRTS::goal_drivable_from(RRTNode const& f)
{
- std::vector<RRTNode> *nodes = (std::vector<RRTNode> *) user_data;
- nodes->push_back(RRTNode());
- nodes->back().x(q[0]);
- nodes->back().y(q[1]);
- nodes->back().h(q[2]);
- nodes->back().sp(q[3]);
- if (nodes->back().sp() == 0) {
- nodes->back().set_t(RRTNodeType::cusp);
- } else if (nodes->size() >= 2) {
- RRTNode* lln = nodes->back().p();
- RRTNode* ln = &nodes->back();
- if (lln != nullptr && ln != nullptr && sgn(lln->sp()) != sgn(ln->sp()))
- ln->set_t(RRTNodeType::cusp);
- }
- return 0;
+ this->bc_.set_pose(f);
+ return this->bc_.drivable(this->goal_);
}
-void RRTS::steer(RRTNode &f, RRTNode &t)
-{
- this->steered().clear();
- double q0[] = {f.x(), f.y(), f.h()};
- double q1[] = {t.x(), t.y(), t.h()};
- ReedsSheppStateSpace rsss(this->bc.mtr());
- rsss.sample(q0, q1, 0.5, cb_rs_steer, &this->steered());
-}
-void RRTS::tmp_steer(RRTNode &f, RRTNode &t)
+void
+RRTS::store(RRTNode n)
{
- this->tmp_steered_.clear();
- double q0[] = {f.x(), f.y(), f.h()};
- double q1[] = {t.x(), t.y(), t.h()};
- ReedsSheppStateSpace rsss(this->bc.mtr());
- rsss.sample(q0, q1, 0.5, cb_rs_steer, &this->tmp_steered_);
+ this->nodes_.push_back(n);
}
-void RRTS::steer1(RRTNode &f, RRTNode &t)
+double
+RRTS::cost_build(RRTNode const& f, RRTNode const& t) const
{
- return this->steer(f, t);
+ return f.edist(t);
}
-void RRTS::steer2(RRTNode &f, RRTNode &t)
+double
+RRTS::cost_search(RRTNode const& f, RRTNode const& t) const
{
- return this->steer(f, t);
+ return this->cost_build(f, t);
}
-void RRTS::join_steered(RRTNode *f)
-{
- while (this->steered().size() > 0) {
- this->store_node(this->steered().front());
- RRTNode *t = &this->nodes().back();
- t->p(f);
- t->c(this->cost_build(*f, *t));
- this->steered().erase(this->steered().begin());
- f = t;
- }
-}
-void RRTS::join_tmp_steered(RRTNode *f)
+void
+RRTS::find_nn(RRTNode const& t)
{
- while (this->tmp_steered_.size() > 0) {
- this->store_node(this->tmp_steered_.front());
- RRTNode *t = &this->nodes().back();
- t->p(f);
- t->c(this->cost_build(*f, *t));
- this->tmp_steered_.erase(this->tmp_steered_.begin());
- f = t;
+ this->nn_ = &this->nodes_.front();
+ this->cost_ = this->cost_search(*this->nn_, t);
+ for (auto& f: this->nodes_) {
+ if (this->cost_search(f, t) < this->cost_) {
+ this->nn_ = &f;
+ this->cost_ = this->cost_search(f, t);
+ }
}
}
-bool RRTS::goal_found(RRTNode &f)
+void
+RRTS::find_nv(RRTNode const& t)
{
- auto &g = this->goals().front();
- double cost = this->cost_build(f, g);
- double edist = sqrt(
- pow(f.x() - g.x(), 2)
- + pow(f.y() - g.y(), 2)
- );
- double adist = std::abs(f.h() - g.h());
- if (edist < 0.05 && adist < M_PI / 32) {
- if (g.p() == nullptr || f.cc + cost < g.cc) {
- g.p(&f);
- g.c(cost);
+ this->nv_.clear();
+ this->cost_ = this->min_gamma_eta();
+ for (auto& f: this->nodes_) {
+ if (this->cost_search(f, t) < this->cost_) {
+ this->nv_.push_back(&f);
}
- return true;
}
- return false;
}
-// RRT* procedures
-bool RRTS::connect()
+void
+RRTS::compute_path()
{
- RRTNode *t = &this->steered().front();
- RRTNode *f = this->nn(this->samples().back());
- double cost = f->cc + this->cost_build(*f, *t);
- for (auto n: this->nv(*t)) {
- if (
- !std::get<0>(this->collide_two_nodes(*n, *t))
- && n->cc + this->cost_build(*n, *t) < cost
- ) {
- f = n;
- cost = n->cc + this->cost_build(*n, *t);
- }
+ this->path_.clear();
+ RRTNode *g = &this->goal_;
+ if (g->p() == nullptr) {
+ return;
}
- // steer from f->t and then continue with the steered.
- this->tmp_steer(*f, *t);
- if (this->tmp_steered_.size() > 0) {
- auto col = this->collide_tmp_steered_from(*f);
- if (std::get<0>(col))
- return false;
- this->join_tmp_steered(f);
- f = &this->nodes().back();
+ while (g != nullptr && this->path_.size() < 10000) {
+ /* FIXME in ext13
+ *
+ * There shouldn't be this->path_.size() < 10000 condition.
+ * However, the RRTS::compute_path() called from
+ * RRTExt13::compute_path tends to re-allocate this->path_
+ * infinitely. There's probably node->p() = &node somewhere...
+ */
+ this->path_.push_back(g);
+ g = g->p();
}
- auto fbc = BicycleCar();
- fbc.x(f->x());
- fbc.y(f->y());
- fbc.h(f->h());
- auto tbc = BicycleCar();
- tbc.x(t->x());
- tbc.y(t->y());
- tbc.h(t->h());
- if (!tbc.drivable(fbc))
- return false;
- // cont.
- this->store_node(this->steered().front());
- t = &this->nodes().back();
- t->p(f);
- t->c(this->cost_build(*f, *t));
- t->set_t(RRTNodeType::connected);
- return true;
+ std::reverse(this->path_.begin(), this->path_.end());
}
-void RRTS::rewire()
+RRTS::RRTS() : gen_(std::random_device{}())
{
- RRTNode *f = &this->nodes().back();
- for (auto n: this->nv(*f)) {
- if (
- !std::get<0>(this->collide_two_nodes(*f, *n))
- && f->cc + this->cost_build(*f, *n) < n->cc
- ) {
- this->tmp_steer(*f, *n);
- if (this->tmp_steered_.size() > 0) {
- auto col = this->collide_tmp_steered_from(*f);
- if (std::get<0>(col))
- continue;
- this->join_tmp_steered(f);
- f = &this->nodes().back();
- }
- n->p(f);
- n->c(this->cost_build(*f, *n));
- }
- }
+ this->nodes_.reserve(4000000);
+ this->steered_.reserve(1000);
+ this->path_.reserve(10000);
+ this->nv_.reserve(1000);
+ this->store(RRTNode()); // root
}
-// API
-void RRTS::init()
+unsigned int
+RRTS::icnt() const
{
+ return this->icnt_;
}
-void RRTS::reset()
+void
+RRTS::icnt(unsigned int i)
{
- RRTNode init = RRTNode();
- init.x(this->nodes().front().x());
- init.y(this->nodes().front().y());
- init.h(this->nodes().front().h());
- this->nodes().clear();
- this->store_node(RRTNode());
- this->nodes().front().x(init.x());
- this->nodes().front().y(init.y());
- this->nodes().front().h(init.h());
- this->samples().clear();
- this->steered().clear();
- this->path().clear();
- this->gf(false);
- for (auto& g: this->goals()) {
- g.p(nullptr);
- g.c_ = 0.0;
- g.cc = 0.0;
- }
+ this->icnt_ = i;
}
-void RRTS::deinit()
+double
+RRTS::scnt() const
{
- this->nodes().clear();
- this->samples().clear();
- this->steered().clear();
- this->store_node(RRTNode()); // root
- this->icnt_ = 0;
- this->scnt_ = 0;
- this->pcnt_ = 0;
- this->gf_ = false;
+ return this->ter_.scnt();
}
-void RRTS::compute_path()
+Json::Value
+RRTS::json() const
{
- if (this->goals().size() == 0)
- return;
- RRTNode *goal = &this->goals().front();
- if (goal->p() == nullptr)
- return;
- this->path_.clear();
- while (goal != nullptr) {
- this->path_.push_back(goal);
- goal = goal->p();
- }
- std::reverse(this->path_.begin(), this->path_.end());
+ Json::Value jvo;
+ unsigned int i = 0;
+ for (auto n: this->path_) {
+ jvo["path"][i][0] = n->x();
+ jvo["path"][i][1] = n->y();
+ jvo["path"][i][2] = n->h();
+ i++;
+ }
+ jvo["goal_cc"] = this->goal_.cc();
+ jvo["time"] = this->time_;
+ return jvo;
}
-bool RRTS::next()
+void
+RRTS::json(Json::Value jvi)
{
- if (this->icnt_ == 0)
- this->tstart_ = std::chrono::high_resolution_clock::now();
- bool next = true;
- if (this->should_stop()) {
- this->log_path_cost();
- return false;
- }
- if (this->samples().size() == 0) {
- this->samples().push_back(RRTNode());
- this->samples().back().x(this->goals().front().x());
- this->samples().back().y(this->goals().front().y());
- this->samples().back().h(this->goals().front().h());
+ assert(jvi["init"] != Json::nullValue);
+ assert(jvi["goal"] != Json::nullValue);
+ assert(jvi["obst"] != Json::nullValue);
+ this->nodes_.front().x(jvi["init"][0].asDouble());
+ this->nodes_.front().y(jvi["init"][1].asDouble());
+ this->nodes_.front().h(jvi["init"][2].asDouble());
+ this->goal_.x(jvi["goal"][0].asDouble());
+ this->goal_.y(jvi["goal"][1].asDouble());
+ this->goal_.b(jvi["goal"][2].asDouble());
+ if (jvi["goal"].size() == 4) {
+ this->goal_.e(jvi["goal"][3].asDouble());
} else {
- this->sample();
+ this->goal_.e(jvi["goal"][2].asDouble());
}
- this->steer1(
- *this->nn(this->samples().back()),
- this->samples().back()
- );
- if (this->steered().size() == 0) {
- this->log_path_cost();
- return next;
+}
+
+bool
+RRTS::next()
+{
+ if (this->icnt_ == 0) {
+ this->ter_.start();
}
- auto col = this->collide_steered_from(
- *this->nn(this->samples().back())
- );
- if (std::get<0>(col)) {
- auto rcnt = this->steered().size() - std::get<1>(col);
- while (rcnt-- > 0) {
- this->steered().pop_back();
- }
+ this->icnt_ += 1;
+ auto rs = this->sample();
+ this->find_nn(rs);
+ this->steer(this->nn(), rs);
+ if (this->collide_steered()) {
+ return this->should_continue();
}
+ this->find_nv(this->steered_.front());
if (!this->connect()) {
- this->log_path_cost();
- return next;
+ return this->should_continue();
}
this->rewire();
- unsigned scnt = this->steered().size();
- this->join_steered(&this->nodes().back());
- RRTNode *just_added = &this->nodes().back();
- while (scnt > 0) {
- // store all the steered1 nodes
- this->steered1_.push_back(just_added);
- scnt--;
- auto &g = this->goals().front();
- this->steer2(*just_added, g);
- auto col = this->collide_steered_from(*just_added);
- if (std::get<0>(col)) {
- auto rcnt = this->steered().size() - std::get<1>(col);
- while (rcnt-- > 0) {
- this->steered().pop_back();
- }
+ unsigned int ss = this->steered_.size();
+ this->join_steered(&this->nodes_.back());
+ RRTNode* just_added = &this->nodes_.back();
+ while (ss > 0 && just_added->p() != nullptr) {
+ //if (!this->goal_drivable_from(*just_added)) {
+ // ss--;
+ // just_added = just_added->p();
+ // continue;
+ //}
+ this->steer(*just_added, this->goal_);
+ if (this->collide_steered()) {
+ ss--;
+ just_added = just_added->p();
+ continue;
}
this->join_steered(just_added);
- // store all the steered2 nodes
- RRTNode* jap = &this->nodes().back();
- while (jap != just_added) {
- this->steered2_.push_back(jap);
- jap = jap->p();
+ bool gn = this->goal_.edist(this->nodes_.back()) < this->eta_;
+ bool gd = this->goal_drivable_from(this->nodes_.back());
+ if (gn && gd) {
+ double nc = this->cost_build(this->nodes_.back(),
+ this->goal_);
+ double ncc = this->nodes_.back().cc() + nc;
+ if (this->goal_.p() == nullptr
+ || ncc < this->goal_.cc()) {
+ this->goal_.p(this->nodes_.back());
+ this->goal_.c(nc);
+ this->compute_path();
+ }
}
- auto gf = this->goal_found(this->nodes().back());
- this->gf(gf);
+ ss--;
just_added = just_added->p();
}
- if (
- this->gf()
- && (
- this->path().size() == 0
- || this->goals().front().cc < this->path().back()->cc
- )
- ) {
- this->compute_path();
- }
- this->log_path_cost();
- return next;
-}
-
-void RRTS::set_sample_normal(
- double mx, double dx,
- double my, double dy,
- double mh, double dh
-)
-{
- this->ndx_ = std::normal_distribution<double>(mx, dx);
- this->ndy_ = std::normal_distribution<double>(my, dy);
- this->ndh_ = std::normal_distribution<double>(mh, dh);
-}
-void RRTS::set_sample_uniform(
- double xmin, double xmax,
- double ymin, double ymax,
- double hmin, double hmax
-)
-{
- this->udx_ = std::uniform_real_distribution<double>(xmin,xmax);
- this->udy_ = std::uniform_real_distribution<double>(ymin,ymax);
- this->udh_ = std::uniform_real_distribution<double>(hmin,hmax);
-}
-void RRTS::set_sample_uniform_circle()
-{
- this->udx_ = std::uniform_real_distribution<double>(0, 1);
- this->udy_ = std::uniform_real_distribution<double>(0, 1);
- this->udh_ = std::uniform_real_distribution<double>(0, 2 * M_PI);
-}
-void RRTS::set_sample(
- double x1, double x2,
- double y1, double y2,
- double h1, double h2
-)
-{
- switch (this->sample_dist_type()) {
- case 1: // uniform
- x1 += this->nodes().front().x();
- x2 += this->nodes().front().x();
- y1 += this->nodes().front().y();
- y2 += this->nodes().front().y();
- this->set_sample_uniform(x1, x2, y1, y2, h1, h2);
- break;
- case 2: // uniform circle
- this->set_sample_uniform_circle();
- break;
- case 3: // uniform index of node in nodes
- this->set_sample_uniform_circle();
- break;
- default: // normal
- this->set_sample_normal(x1, x2, y1, y2, h1, h2);
- }
-}
-Json::Value RRTS::json()
-{
- Json::Value jvo;
- {
- jvo["time"] = this->scnt();
- }
- {
- jvo["iterations"] = this->icnt();
- }
- {
- jvo["init"][0] = this->nodes().front().x();
- jvo["init"][1] = this->nodes().front().y();
- jvo["init"][2] = this->nodes().front().h();
- }
- {
- jvo["path_cost_before_opt"] = this->path_cost_before_opt_;
- }
- {
- if (this->path().size() > 0) {
- jvo["cost"] = this->path().back()->cc;
- jvo["entry"][0] = this->goals().front().x();
- jvo["entry"][1] = this->goals().front().y();
- jvo["entry"][2] = this->goals().front().h();
- if (this->entry_set) {
- jvo["entry"][2] = this->entry.b;
- jvo["entry"][3] = this->entry.e;
- }
- if (this->entries_set) {
- jvo["entries"][0][0] = this->entry1.x;
- jvo["entries"][0][1] = this->entry1.y;
- jvo["entries"][0][2] = this->entry1.h;
- jvo["entries"][1][0] = this->entry2.x;
- jvo["entries"][1][1] = this->entry2.y;
- jvo["entries"][1][2] = this->entry2.h;
- }
- jvo["goal"][0] = this->goals().back().x();
- jvo["goal"][1] = this->goals().back().y();
- jvo["goal"][2] = this->goals().back().h();
- }
- }
- {
- unsigned int cu = 0;
- unsigned int co = 0;
- unsigned int pcnt = 0;
- for (auto n: this->path()) {
- jvo["path"][pcnt][0] = n->x();
- jvo["path"][pcnt][1] = n->y();
- jvo["path"][pcnt][2] = n->h();
- if (n->t(RRTNodeType::cusp))
- cu++;
- if (n->t(RRTNodeType::connected))
- co++;
- pcnt++;
- }
- jvo["cusps-in-path"] = cu;
- jvo["connecteds-in-path"] = co;
- }
- {
- unsigned int gcnt = 0;
- for (auto g: this->goals()) {
- jvo["goals"][gcnt][0] = g.x();
- jvo["goals"][gcnt][1] = g.y();
- jvo["goals"][gcnt][2] = g.h();
- gcnt++;
- }
- }
- {
- unsigned int ocnt = 0;
- for (auto o: this->obstacles()) {
- unsigned int ccnt = 0;
- for (auto c: o.poly()) {
- jvo["obst"][ocnt][ccnt][0] = std::get<0>(c);
- jvo["obst"][ocnt][ccnt][1] = std::get<1>(c);
- ccnt++;
- }
- ocnt++;
- }
- }
- {
- jvo["nodes"] = (unsigned int) this->nodes().size();
- }
- {
- unsigned int cnt = 0;
- for (auto i: this->log_path_cost_)
- jvo["log_path_cost"][cnt++] = i;
- }
- {
- unsigned int cnt = 0;
- for (auto i: this->log_opt_time_)
- jvo["log_opt_time"][cnt++] = i;
- }
- //{
- // unsigned int ncnt = 0;
- // for (auto n: this->nodes()) {
- // jvo["nodes_x"][ncnt] = n.x();
- // jvo["nodes_y"][ncnt] = n.y();
- // //jvo["nodes_h"][ncnt] = n.h();
- // ncnt++;
- // }
+ ////if (!this->goal_drivable_from(this->nodes_.back())) {
+ //// return this->should_continue();
+ ////}
+ //this->steer(this->nodes_.back(), this->goal_);
+ //if (this->collide_steered()) {
+ // return this->should_continue();
//}
- //{
- // unsigned int ncnt = 0;
- // for (auto n: this->steered1_) {
- // jvo["steered1_x"][ncnt] = n->x();
- // jvo["steered1_y"][ncnt] = n->y();
- // //jvo["nodes_h"][ncnt] = n.h();
- // ncnt++;
- // }
- // ncnt = 0;
- // for (auto n: this->steered2_) {
- // jvo["steered2_x"][ncnt] = n->x();
- // jvo["steered2_y"][ncnt] = n->y();
- // //jvo["nodes_h"][ncnt] = n.h();
- // ncnt++;
+ //this->join_steered(&this->nodes_.back());
+ //bool gn = this->goal_.edist(this->nodes_.back()) < this->eta_;
+ //bool gd = this->goal_drivable_from(this->nodes_.back());
+ //if (gn && gd) {
+ // double nc = this->cost_build(this->nodes_.back(), this->goal_);
+ // double ncc = this->nodes_.back().cc() + nc;
+ // if (this->goal_.p() == nullptr || ncc < this->goal_.cc()) {
+ // this->goal_.p(this->nodes_.back());
+ // this->goal_.c(nc);
+ // this->compute_path();
// }
//}
- return jvo;
-}
-
-void RRTS::json(Json::Value jvi)
-{
- assert(jvi["init"] != Json::nullValue);
- assert(jvi["goals"] != Json::nullValue);
- assert(jvi["obst"] != Json::nullValue);
-
- this->nodes().front().x(jvi["init"][0].asDouble());
- this->nodes().front().y(jvi["init"][1].asDouble());
- this->nodes().front().h(jvi["init"][2].asDouble());
- {
- RRTNode* gp = nullptr;
- if (jvi["entry"] != Json::nullValue) {
- this->entry_set = true;
- this->entry.x = jvi["entry"][0].asDouble();
- this->entry.y = jvi["entry"][1].asDouble();
- this->entry.b = jvi["entry"][2].asDouble();
- this->entry.e = jvi["entry"][3].asDouble();
- RRTNode tmp_node;
- tmp_node.x(this->entry.x);
- tmp_node.y(this->entry.y);
- tmp_node.h((this->entry.b + this->entry.e) / 2.0);
- this->goals().push_back(tmp_node);
- this->goals().back().p(gp);
- gp = &this->goals().back();
- }
- if (jvi["entries"] != Json::nullValue) {
- this->entries_set = true;
- this->entry1.x = jvi["entries"][0][0].asDouble();
- this->entry1.y = jvi["entries"][0][1].asDouble();
- this->entry1.h = jvi["entries"][0][2].asDouble();
- this->entry2.x = jvi["entries"][1][0].asDouble();
- this->entry2.y = jvi["entries"][1][1].asDouble();
- this->entry2.h = jvi["entries"][1][2].asDouble();
- }
- for (auto g: jvi["goals"]) {
- RRTNode tmp_node;
- tmp_node.x(g[0].asDouble());
- tmp_node.y(g[1].asDouble());
- tmp_node.h(g[2].asDouble());
- this->goals().push_back(tmp_node);
- this->goals().back().p(gp);
- gp = &this->goals().back();
- }
- this->goals().front().set_t(RRTNodeType::cusp);
- this->goals().back().set_t(RRTNodeType::cusp);
- }
- {
- Obstacle tmp_obstacle;
- for (auto o: jvi["obst"]) {
- tmp_obstacle.poly().clear();
- for (auto c: o) {
- double tmp_x = c[0].asDouble();
- double tmp_y = c[1].asDouble();
- auto tmp_tuple = std::make_tuple(tmp_x, tmp_y);
- tmp_obstacle.poly().push_back(tmp_tuple);
- }
- this->obstacles().push_back(tmp_obstacle);
- }
- }
- {
- double edist_init_goal = sqrt(
- pow(
- this->nodes().front().x()
- - this->goals().front().x(),
- 2
- )
- + pow(
- this->nodes().front().y()
- - this->goals().front().y(),
- 2
- )
- );
- this->set_sample(
- this->nodes().front().x(), edist_init_goal,
- this->nodes().front().y(), edist_init_goal,
- 0, 2 * M_PI
- );
- }
+ this->time_ = this->ter_.scnt();
+ return this->should_continue();
}
-RRTS::RRTS()
- : gen_(std::random_device{}())
+void
+RRTS::reset()
{
- this->goals().reserve(100);
- this->nodes().reserve(4000000);
- this->samples().reserve(1000);
- this->steered().reserve(20000);
- this->store_node(RRTNode()); // root
+ this->goal_ = RRTGoal();
+ this->path_.clear();
+ this->steered_.clear();
+ this->nodes_.erase(this->nodes_.begin() + 1, this->nodes_.end());
+ this->nv_.clear();
+ this->nn_ = nullptr;
}
-double cc(RRTNode &t)
-{
- RRTNode *n = &t;
- double cost = 0;
- while (n != nullptr) {
- cost += n->c();
- n = n->p();
- }
- return cost;
-}
+} // namespace rrts