Program Listing for File rrt_star.cpp¶
↰ Return to documentation for file (src/rrt_star.cpp
)
/*
NOTE:
Midway through refactor
Trying to make rewire more efficient as well as allow rewire to be iteratively
called on all nodes. SO if a node's value cahnges call reqire on all nodes
within the threshold amd see whether they need to be rewired
Nodes ill now need to maintain a list of neighbours that have been seen to
easily iterate over all neighbours to update them
*/
#include "path_planning/rrt_star.hpp"
#include <algorithm>
#include <cmath>
#include <random>
#include <vector>
// constants
constexpr double half_grid_unit = 0.5;
constexpr double precision_limit = 0.000001;
std::tuple<bool, Node> RRTStar::FindNearestPoint(Node& new_node) {
bool found = false;
Node nearest_node;
near_nodes_.insert({new_node, {}});
new_node.cost_ = std::numeric_limits<double>::max();
for (const auto node : point_list_) {
if (auto new_dist = std::sqrt(std::pow(node.x_ - new_node.x_, 2) +
std::pow(node.y_ - new_node.y_, 2));
new_dist <= threshold_ && !IsAnyObstacleInPath(node, new_node) &&
!CompareCoordinates(node, new_node) && node.pid_ != new_node.id_) {
near_nodes_[new_node].push_back(node);
near_nodes_[node].push_back(new_node);
found = true;
if (new_dist + node.cost_ < new_node.cost_) {
nearest_node = node;
new_node.pid_ = nearest_node.id_;
new_node.cost_ = new_dist + node.cost_;
}
}
}
if (!found) {
near_nodes_.erase(new_node);
}
return {found, nearest_node};
}
bool RRTStar::IsAnyObstacleInPath(const Node& n_1, const Node& n_2) const {
if (n_2.y_ - n_1.y_ == 0) {
const double c = n_2.y_;
return std::any_of(
std::begin(obstacle_list_), std::end(obstacle_list_),
[&c, &n_1, &n_2](const auto& obs_node) {
return obs_node.y_ == c &&
((n_1.x_ >= obs_node.x_ && obs_node.x_ >= n_2.x_) ||
(n_1.x_ <= obs_node.x_ && obs_node.x_ <= n_2.x_));
});
}
const double slope = static_cast<double>(n_2.x_ - n_1.x_) / (n_2.y_ - n_1.y_);
const double c = n_2.x_ - slope * n_2.y_;
// TODO: Cache this
for (const auto& obs_node : obstacle_list_) {
if (!(((n_1.y_ >= obs_node.y_) && (obs_node.y_ >= n_2.y_)) ||
((n_1.y_ <= obs_node.y_) && (obs_node.y_ <= n_2.y_)))) {
continue;
}
if (!(((n_1.x_ >= obs_node.x_) && (obs_node.x_ >= n_2.x_)) ||
((n_1.x_ <= obs_node.x_) && (obs_node.x_ <= n_2.x_)))) {
continue;
}
std::vector<double> arr(4);
// Using properties of a point and a line here.
// If the obtacle lies on one side of a line, substituting its edge points
// (all obstacles are grid sqaures in this example) into the equation of
// the line passing through the coordinated of the two nodes under
// consideration will lead to all four resulting values having the same
// sign. Hence if their sum of the value/abs(value) is 4 the obstacle is
// not in the way. If a single point is touched ie the substitution leads
// ot a value under 10^-7, it is set to 0. Hence the obstacle has
// 1 point on side 1, 3 points on side 2, the sum is 2 (-1+3)
// 2 point on side 1, 2 points on side 2, the sum is 0 (-2+2)
// 0 point on side 1, 3 points on side 2, (1 point on the line, ie,
// grazes the obstacle) the sum is 3 (0+3)
// Hence the condition < 3
arr[0] = obs_node.x_ + half_grid_unit -
slope * (obs_node.y_ + half_grid_unit) - c;
arr[1] = obs_node.x_ + half_grid_unit -
slope * (obs_node.y_ - half_grid_unit) - c;
arr[2] = obs_node.x_ - half_grid_unit -
slope * (obs_node.y_ + half_grid_unit) - c;
arr[3] = obs_node.x_ - half_grid_unit -
slope * (obs_node.y_ - half_grid_unit) - c;
double count = 0;
for (auto& a : arr) {
if (std::fabs(a) <= precision_limit) {
a = 0;
} else {
count += a / std::fabs(a);
}
}
if (std::abs(count) < 3) {
return true;
}
}
return false;
}
Node RRTStar::GenerateRandomNode() const {
std::random_device rd; // obtain a random number from hardware
std::mt19937 eng(rd()); // seed the generator
std::uniform_int_distribution<int> distr(0, n_ - 1); // define the range
const int x = distr(eng);
const int y = distr(eng);
return Node(x, y, 0, 0, n_ * x + y, 0);
}
void RRTStar::Rewire(const Node& new_node) {
Node new_node_in_point_list = *point_list_.find(new_node);
for (const auto& neighbour_coords_node :
near_nodes_[new_node_in_point_list]) {
Node neighbour = *point_list_.find(neighbour_coords_node);
if (const auto dist =
std::sqrt(std::pow(neighbour.x_ - new_node_in_point_list.x_, 2) +
std::pow(neighbour.y_ - new_node_in_point_list.y_, 2));
neighbour.cost_ > dist + new_node_in_point_list.cost_ &&
neighbour.id_ != new_node_in_point_list.id_) {
neighbour.pid_ = new_node_in_point_list.id_;
neighbour.cost_ = dist + new_node_in_point_list.cost_;
point_list_.erase(neighbour);
point_list_.insert(neighbour);
Rewire(neighbour);
}
}
}
std::tuple<bool, std::vector<Node>> RRTStar::Plan(const Node& start,
const Node& goal) {
start_ = start;
goal_ = goal;
grid_ = original_grid_;
int max_iterations = max_iter_x_factor_ * n_ * n_;
CreateObstacleList();
point_list_.insert(start_);
grid_[start_.x_][start_.y_] = 3;
int iteration = 0;
Node new_node = start_;
bool found_goal = false;
if (CheckGoalVisible(new_node)) {
found_goal = true;
}
while (iteration < max_iterations) {
iteration++;
new_node = GenerateRandomNode();
// Go back to beginning of loop if point already considered
if (grid_[new_node.x_][new_node.y_] != 0) {
continue;
}
// Go back to beginning of loop if no near neighbour
const auto [found_nearest_point, nearest_node] = FindNearestPoint(new_node);
if (!found_nearest_point) {
continue;
}
// Setting to 2 implies visited/considered
grid_[new_node.x_][new_node.y_] = 2;
point_list_.insert(new_node);
Rewire(new_node); // Rewire
if (CheckGoalVisible(new_node)) {
found_goal = true;
}
}
if (!found_goal) {
return {false, {}};
}
return {true, CreatePath()};
}
bool RRTStar::CheckGoalVisible(const Node& new_node) {
const auto dist = std::sqrt(std::pow(goal_.x_ - new_node.x_, 2) +
std::pow(goal_.y_ - new_node.y_, 2));
if (dist > threshold_) {
return false;
}
if (CompareCoordinates(goal_, new_node)) {
point_list_.erase(new_node);
point_list_.insert(new_node);
return true;
}
if (!IsAnyObstacleInPath(new_node, goal_)) {
if (auto it = point_list_.find(goal_);
it != point_list_.end() && it->cost_ > dist + new_node.cost_) {
auto goal_in_point_list = goal_;
goal_in_point_list.cost_ = dist + new_node.cost_;
goal_in_point_list.pid_ = new_node.id_;
point_list_.erase(goal_in_point_list);
point_list_.insert(goal_in_point_list);
} else if (it == point_list_.end()) {
auto goal_in_point_list = goal_;
goal_in_point_list.cost_ = dist + new_node.cost_;
goal_in_point_list.pid_ = new_node.id_;
point_list_.insert(goal_in_point_list);
}
return true;
}
return false;
}
void RRTStar::CreateObstacleList() {
for (int i = 0; i < n_; i++) {
for (int j = 0; j < n_; j++) {
if (grid_[i][j] == 1) {
Node obs(i, j, 0, 0, i * n_ + j, 0);
obstacle_list_.push_back(obs);
}
}
}
}
std::vector<Node> RRTStar::CreatePath() {
std::vector<Node> path;
Node current = *point_list_.find(goal_);
while (!CompareCoordinates(current, start_)) {
path.push_back(current);
current = *point_list_.find(
Node(current.pid_ / n_, current.pid_ % n_, 0, 0, current.pid_));
}
path.push_back(current);
return path;
}
void RRTStar::SetParams(const int threshold, const int max_iter_x_factor) {
threshold_ = threshold;
max_iter_x_factor_ = max_iter_x_factor;
}
#ifdef BUILD_INDIVIDUAL
int main() {
constexpr int n = 11;
std::vector<std::vector<int>> grid(n, std::vector<int>(n, 0));
MakeGrid(grid);
std::random_device rd; // obtain a random number from hardware
std::mt19937 eng(rd()); // seed the generator
std::uniform_int_distribution<int> distr(0, n - 1); // define the range
Node start(distr(eng), distr(eng), 0, 0, 0, 0);
Node goal(distr(eng), distr(eng), 0, 0, 0, 0);
start.id_ = start.x_ * n + start.y_;
start.pid_ = start.x_ * n + start.y_;
goal.id_ = goal.x_ * n + goal.y_;
start.h_cost_ = abs(start.x_ - goal.x_) + abs(start.y_ - goal.y_);
// Make sure start and goal are not obstacles and their ids are correctly
// assigned.
grid[start.x_][start.y_] = 0;
grid[goal.x_][goal.y_] = 0;
start.PrintStatus();
goal.PrintStatus();
PrintGrid(grid);
constexpr double threshold = 2;
constexpr int max_iter_x_factor = 20;
RRTStar new_rrt_star(grid);
new_rrt_star.SetParams(threshold, max_iter_x_factor);
const auto [found_path, path_vector] = new_rrt_star.Plan(start, goal);
PrintPath(path_vector, start, goal, grid);
return 0;
}
#endif // BUILD_INDIVIDUAL