diff --git a/src/navigation/navigation.cc b/src/navigation/navigation.cc index 7efe3f4..d7629b6 100644 --- a/src/navigation/navigation.cc +++ b/src/navigation/navigation.cc @@ -1,1437 +1,1436 @@ -//======================================================================== -// This software is free: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License Version 3, -// as published by the Free Software Foundation. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// Version 3 in the file COPYING that came with this distribution. -// If not, see . -//======================================================================== -/*! -\file navigation.cc -\brief Implementation for reference Navigation class. -\author Joydeep Biswas, Jarrett Holtz, Kavan Sikand (C) 2021 -*/ -//======================================================================== - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "navigation.h" -#include "geometry_msgs/PoseStamped.h" -#include "gflags/gflags.h" -#include "eigen3/Eigen/Dense" -#include "eigen3/Eigen/Geometry" -#include "glog/logging.h" -#include "shared/math/math_util.h" -#include "shared/util/helpers.h" -#include "shared/util/timer.h" -#include "shared/util/timer.h" -#include "eight_connected_domain.h" -#include "graph_domain.h" -#include "astar.h" -#include "simple_queue.h" - -#include "motion_primitives.h" -#include "constant_curvature_arcs.h" -#include "ackermann_motion_primitives.h" -#include "deep_cost_map_evaluator.h" -#include "linear_evaluator.h" -#include "amrl_msgs/NavStatusMsg.h" -#include "amrl_msgs/Pose2Df.h" - -#include "nlohmann/json.hpp" -using json = nlohmann::json; - -using Eigen::Rotation2Df; -using Eigen::Vector2f; -using navigation::MotionLimits; -using navigation::Odom; -using navigation::Twist; -using std::atan2; -using std::deque; -using std::max; -using std::min; -using std::swap; -using std::shared_ptr; -using std::string; -using std::vector; -using std::unordered_set; -using std::unordered_map; -using std::set; - -using namespace math_util; -using namespace motion_primitives; - -// Special test modes. -DEFINE_bool(test_toc, false, "Run 1D time-optimal controller test"); -DEFINE_bool(test_obstacle, false, "Run obstacle detection test"); -DEFINE_bool(test_avoidance, false, "Run obstacle avoidance test"); -DEFINE_bool(test_planner, false, "Run navigation planner test"); -DEFINE_bool(test_latency, false, "Run Latency test"); -DEFINE_double(test_dist, 0.5, "Test distance"); -DEFINE_string(test_log_file, "", "Log test results to file"); - -DEFINE_double(max_curvature, 2.0, "Maximum curvature of turning"); - -DEFINE_bool(no_local, false, "can be used to turn off local planner"); - -namespace { -// Epsilon value for handling limited numerical precision. -const float kEpsilon = 1e-5; - -DEFINE_int32(num_options, 41, "Number of options to consider"); - -// TODO(jaholtz) figure out how to handle this visualization without -// having astar contain ros dependencies -struct EightGridVisualizer { - EightGridVisualizer(bool visualize) : - kVisualize(visualize) { } - - void DrawEdge(const navigation::EightConnectedDomain::State& s1, - const navigation::EightConnectedDomain::State& s2) { - if (!kVisualize) return; - static const bool kDebug = false; - if (kDebug) { - printf("%7.2f,%7.2f -> %7.2f,%7.2f\n", - s1.x(), - s1.y(), - s2.x(), - s2.y()); - } - // visualization::DrawLine(s1, s2, 0x606060, global_viz_msg_); - // viz_pub_.publish(global_viz_msg_); - if (kDebug) Sleep(0.05); - } - - const bool kVisualize; -}; - -struct GraphVisualizer { - GraphVisualizer(bool visualize) : - kVisualize(visualize) { } - - void DrawEdge(const navigation::GraphDomain::State& s1, - const navigation::GraphDomain::State& s2) { - if (!kVisualize) return; - static const bool kDebug = false; - if (kDebug) { - printf("%7.2f,%7.2f -> %7.2f,%7.2f\n", - s1.loc.x(), - s1.loc.y(), - s2.loc.x(), - s2.loc.y()); - } - // visualization::DrawLine(s1.loc, s2.loc, 0xC0C0C0, global_viz_msg_); - // viz_pub_.publish(global_viz_msg_); - if (kDebug) Sleep(0.05); - } - - const bool kVisualize; -}; - -} // namespace - -namespace navigation { - -Navigation::Navigation() : - robot_loc_(0, 0), - robot_angle_(0), - robot_vel_(0, 0), - robot_omega_(0), - nav_state_(NavigationState::kStopped), - nav_goal_loc_(0, 0), - nav_goal_angle_(0), - odom_initialized_(false), - loc_initialized_(false), - t_point_cloud_(0), - t_odometry_(0), - enabled_(false), - initialized_(false), - sampler_(nullptr), - evaluator_(nullptr), - costmap_(60, 60, 0.5, -15, -15), - global_costmap_(200, 200, 0.5, -50, -50), - intermediate_path_found_(false), - intermediate_goal_(0, 0){ //parameters are (x/y size in cells, resolution, bottom left x/y origin coordinates) - sampler_ = std::unique_ptr(new AckermannSampler()); -} - -void Navigation::Initialize(const NavigationParameters& params, - const string& map_file) { - // Initialize status message - params_ = params; - int local_costmap_size = 2*static_cast(std::round(params_.local_costmap_radius/params_.local_costmap_resolution)); - costmap_ = costmap_2d::Costmap2D(local_costmap_size, local_costmap_size, params_.local_costmap_resolution, -params.local_costmap_radius, -params.local_costmap_radius); - int global_costmap_size = 2*static_cast(std::round(params_.global_costmap_radius/params_.global_costmap_resolution)); - global_costmap_ = costmap_2d::Costmap2D(global_costmap_size, global_costmap_size, params_.global_costmap_resolution, params.global_costmap_origin_x, params.global_costmap_origin_y); - planning_domain_ = GraphDomain(map_file, ¶ms_); - - LoadVectorMap(map_file); - - initialized_ = true; - sampler_->SetNavParams(params); - - PathEvaluatorBase* evaluator = nullptr; - if (params_.evaluator_type == "cost_map") { - auto cost_map_evaluator = new DeepCostMapEvaluator(params_); - cost_map_evaluator->LoadModel(); - evaluator = (PathEvaluatorBase*) cost_map_evaluator; - } else if (params_.evaluator_type == "linear") { - evaluator = (PathEvaluatorBase*) new LinearEvaluator(); - } else { - printf("Uknown evaluator type %s\n", params_.evaluator_type.c_str()); - exit(1); - } - evaluator_ = std::unique_ptr(evaluator); -} - -void Navigation::LoadVectorMap(const string& map_file){ //Assume map is given as MAP.navigation.json - - std::string vector_map_file = map_file; - - // Find the position of ".navigation.json" - size_t found = vector_map_file.find(".navigation.json"); - - // Replace ".navigation.json" with ".vectormap.json" - if (found != std::string::npos) { - vector_map_file.replace(found, std::string(".navigation.json").length(), ".vectormap.json"); - } - - // Output the modified string - std::cout << "Loading vectormap file: " << vector_map_file << std::endl; - - int x_max = global_costmap_.getSizeInCellsX(); - int y_max = global_costmap_.getSizeInCellsY(); - global_costmap_.resetMap(0, 0, x_max, y_max); - global_costmap_obstacles_.clear(); - - std::ifstream i(vector_map_file); - json j; - i >> j; - i.close(); - - unordered_map inflation_cells; - - for (const auto& line : j) { - // Access specific fields in each dictionary - Vector2f p0(line["p0"]["x"], line["p0"]["y"]); - Vector2f p1(line["p1"]["x"], line["p1"]["y"]); - - float length = (p0 - p1).norm(); - for (float i = 0; i < length; i += params_.global_costmap_resolution){ - Vector2f costmap_point = p0 + i*(p1 - p0)/length; - uint32_t unsigned_mx, unsigned_my; - bool in_map = global_costmap_.worldToMap(costmap_point.x(), costmap_point.y(), unsigned_mx, unsigned_my); - if(in_map){ - int cell_inflation_size = std::ceil(params_.global_costmap_inflation_size/global_costmap_.getResolution()); - int mx = static_cast(unsigned_mx); - int my = static_cast(unsigned_my); - for (int j = -cell_inflation_size; j <= cell_inflation_size; j++){ - for (int k = -cell_inflation_size; k <= cell_inflation_size; k++){ - float cell_dist = sqrt(pow(j, 2) + pow(k, 2)); - float dist = cell_dist * global_costmap_.getResolution(); - if((cell_dist <= cell_inflation_size) && (mx + j >= 0) && (mx + j < x_max) && (my + k >= 0) && (my + k < y_max)){ - unsigned char cost; - if(j == 0 && k == 0){ - cost = costmap_2d::LETHAL_OBSTACLE; - } - else if(dist <= params_.replan_inflation_size){ - cost = costmap_2d::INSCRIBED_INFLATED_OBSTACLE; - } - else{ - cost = std::ceil(std::exp(-1 * params_.inflation_coeff * (dist - params_.replan_inflation_size)) * (costmap_2d::INSCRIBED_INFLATED_OBSTACLE-1)); - } - global_costmap_.setCost(mx + j, my + k, std::max(cost, global_costmap_.getCost(mx + j, my + k))); - inflation_cells[global_costmap_.getIndex(mx + j, my + k)] = std::max(cost, global_costmap_.getCost(mx + j, my + k)); - } - // if((sqrt(pow(j, 2) + pow(k, 2)) <= cell_inflation_size) && (mx + j >= 0) && (mx + j < x_max) - // && (my + k >= 0) && (my + k < y_max)){ - // inflation_cells.insert(global_costmap_.getIndex(mx + j, my + k)); - // } - } - } - } - } - } - - for (const auto& pair : inflation_cells) { - auto index = pair.first; - uint32_t mx = 0; - uint32_t my = 0; - global_costmap_.indexToCells(index, mx, my); - // global_costmap_.setCost(mx, my, costmap_2d::LETHAL_OBSTACLE); - - double wx, wy; - global_costmap_.mapToWorld(mx, my, wx, wy); - global_costmap_obstacles_.push_back(ObstacleCost{Vector2f(wx, wy), pair.second}); - } - -} - -bool Navigation::Enabled() const { - return enabled_; -} - -void Navigation::Enable(bool enable) { - enabled_ = enable; -} - -void Navigation::SetNavGoal(const Vector2f& loc, float angle) { - cout << "set nav goal goto" << endl; - nav_state_ = NavigationState::kGoto; - nav_goal_loc_ = loc; - nav_goal_angle_ = angle; - plan_path_.clear(); -} - -void Navigation::ResetNavGoals() { - nav_state_ = NavigationState::kStopped; - nav_goal_loc_ = robot_loc_; - nav_goal_angle_ = robot_angle_; - local_target_.setZero(); - plan_path_.clear(); -} - -void Navigation::SetOverride(const Vector2f& loc, float angle) { - nav_state_ = NavigationState::kOverride; - override_target_ = loc; -} - -void Navigation::Resume() { - cout << "resume goto" << endl; - nav_state_ = NavigationState::kGoto; -} - -void Navigation::UpdateMap(const string& map_path) { - LoadVectorMap(map_path); - planning_domain_.Load(map_path); - plan_path_.clear(); - prev_obstacles_.clear(); - costmap_obstacles_.clear(); -} - -void Navigation::UpdateLocation(const Eigen::Vector2f& loc, float angle) { - robot_loc_ = loc; - robot_angle_ = angle; - loc_initialized_ = true; -} - -void Navigation::PruneLatencyQueue() { - if (command_history_.empty()) return; - const double update_time = min(t_point_cloud_, t_odometry_); - static const bool kDebug = false; - for (size_t i = 0; i < command_history_.size(); ++i) { - const double t_cmd = command_history_[i].time; - if (kDebug) { - printf("Command %d %f\n", int(i), t_cmd - update_time); - } - if (t_cmd < update_time - params_.dt) { - if (kDebug) { - printf("Erase %d %f %f\n", - int(i), - t_cmd - update_time, - command_history_[i].linear.x()); - } - command_history_.erase( command_history_.begin() + i); - --i; - } - } -} - -void Navigation::UpdateOdometry(const Odom& msg) { - latest_odom_msg_ = msg; - t_odometry_ = msg.time; - PruneLatencyQueue(); - if (!odom_initialized_) { - starting_loc_ = Vector2f(msg.position.x(), msg.position.y()); - odom_initialized_ = true; - } -} - -void Navigation::UpdateCommandHistory(Twist twist) { - twist.time += params_.system_latency; - command_history_.push_back(twist); - if (false) { - printf("Push %f %f\n", - twist.linear.x(), - command_history_.back().linear.x()); - } -} - -void Navigation::ForwardPredict(double t) { - if (command_history_.empty()) { - robot_vel_ = Vector2f(0, 0); - robot_omega_ = 0; - } else { - const Twist latest_twist = command_history_.back(); - robot_vel_ = Vector2f(latest_twist.linear.x(), latest_twist.linear.y()); - robot_omega_ = latest_twist.angular.z(); - } - if (false) { - for (size_t i = 0; i < command_history_.size(); ++i) { - const auto& c = command_history_[i]; - printf("%d %f %f\n", int(i), t - c.time, c.linear.x()); - } - printf("Predict: %f %f\n", t - t_odometry_, t - t_point_cloud_); - } - odom_loc_ = Vector2f(latest_odom_msg_.position.x(), - latest_odom_msg_.position.y()); - odom_angle_ = 2.0f * atan2f(latest_odom_msg_.orientation.z(), - latest_odom_msg_.orientation.w()); - using Eigen::Affine2f; - using Eigen::Rotation2Df; - using Eigen::Translation2f; - Affine2f lidar_tf = Affine2f::Identity(); - for (const Twist& c : command_history_) { - const double cmd_time = c.time; - if (cmd_time > t) continue; - if (cmd_time >= t_odometry_ - params_.dt) { - const float dt = (t_odometry_ > cmd_time) ? - min(t_odometry_ - cmd_time, params_.dt) : - min(t - cmd_time, params_.dt); - odom_loc_ += dt * (Rotation2Df(odom_angle_) * Vector2f( - c.linear.x(), c.linear.y())); - odom_angle_ = AngleMod(odom_angle_ + dt * c.angular.z()); - } - if (t_point_cloud_ >= cmd_time - params_.dt) { - const float dt = (t_point_cloud_ > cmd_time) ? - min(t_point_cloud_ - cmd_time, params_.dt) : - min(t - cmd_time, params_.dt); - lidar_tf = - Translation2f(-dt * Vector2f(c.linear.x(), c.linear.y())) * - Rotation2Df(-c.angular.z() * dt) * - lidar_tf; - } - } - fp_point_cloud_.resize(point_cloud_.size()); - for (size_t i = 0; i < point_cloud_.size(); ++i) { - fp_point_cloud_[i] = lidar_tf * point_cloud_[i]; - } -} - -void Navigation::TrapezoidTest(Vector2f& cmd_vel, float& cmd_angle_vel) { - if (!odom_initialized_) return; - const float x = (odom_loc_ - starting_loc_).norm(); - const float speed = robot_vel_.norm(); - const float velocity_cmd = Run1DTimeOptimalControl( - params_.linear_limits, - x, - speed, - FLAGS_test_dist, - 0, - params_.dt); - cmd_vel = {velocity_cmd, 0}; - cmd_angle_vel = 0; - printf("x: %.3f d:%.3f v: %.3f cmd:%.3f\n", x, FLAGS_test_dist, speed, velocity_cmd); -} - -void Navigation::LatencyTest(Vector2f& cmd_vel, float& cmd_angle_vel) { - static FILE* fid = nullptr; - if (!FLAGS_test_log_file.empty() && fid == nullptr) { - fid = fopen(FLAGS_test_log_file.c_str(), "w"); - } - const float kMaxSpeed = 0.75; - const float kFrequency = 0.4; - - static double t_start_ = GetMonotonicTime(); - const double t = GetMonotonicTime() - t_start_; - // float v_current = robot_vel_.x(); - float v_cmd = kMaxSpeed * - sin(2.0 * M_PI * kFrequency * t); - cmd_vel = {v_cmd, 0}; - cmd_angle_vel = 0.0; -} - -void Navigation::ObstAvTest(Vector2f& cmd_vel, float& cmd_angle_vel) { - const Vector2f kTarget(4, 0); - local_target_ = kTarget; - RunObstacleAvoidance(cmd_vel, cmd_angle_vel); -} - -void Navigation::ObstacleTest(Vector2f& cmd_vel, float& cmd_angle_vel) { - const float speed = robot_vel_.norm(); - float free_path_length = 30.0; - float clearance = 10; - GetStraightFreePathLength(&free_path_length, &clearance); - const float dist_left = - max(0.0f, free_path_length - params_.obstacle_margin); - printf("%f\n", free_path_length); - const float velocity_cmd = Run1DTimeOptimalControl( - params_.linear_limits, - 0, - speed, - dist_left, - 0, - params_.dt); - cmd_vel = {velocity_cmd, 0}; - cmd_angle_vel = 0; -} - -Vector2f GetClosestApproach(const PathOption& o, const Vector2f& target) { - if (fabs(o.curvature) < kEpsilon) { - // Straight line path - if (target.x() > o.free_path_length) { - return Vector2f(o.free_path_length, 0); - } else if (target.x() < 0.0) { - return Vector2f(0, 0); - } else { - return Vector2f(target.x(), 0); - } - } - const float end_angle = fabs(o.curvature * o.free_path_length); - const float turn_radius = 1.0f / o.curvature; - const Vector2f turn_center(0, turn_radius); - const Vector2f target_radial = target - turn_center; - - const Vector2f start(0, 0); - const Vector2f middle_radial = fabs(turn_radius) * target_radial.normalized(); - const Vector2f middle = turn_center + middle_radial; - const float middle_angle = - atan2(fabs(middle_radial.x()), fabs(middle_radial.y())); - - const Vector2f end(fabs(turn_radius) * sin(end_angle), - turn_radius * (1.0f - cos(end_angle))); - - Vector2f closest_point = start; - if (middle_angle < end_angle && - (closest_point - target).squaredNorm() > - (middle - target).squaredNorm()) { - closest_point = middle; - } - if ((closest_point - target).squaredNorm() > - (end - target).squaredNorm()) { - closest_point = end; - } - return closest_point; -} - -float GetClosestDistance(const PathOption& o, const Vector2f& target) { - const Vector2f closest_point = GetClosestApproach(o, target); - return (target - closest_point).norm(); -} - -void Navigation::ObservePointCloud(const vector& cloud, - double time) { - point_cloud_ = cloud; - t_point_cloud_ = time; - PruneLatencyQueue(); -} - -void Navigation::ObserveImage(cv::Mat image, double time) { - latest_image_ = image; - t_image_ = time; -} - -vector Navigation::GlobalPlan(const Vector2f& initial, - const Vector2f& end) { - auto plan = Plan(initial, end); - std::vector path; - for (auto& node : plan ) { - path.push_back(node.id); - } - return path; -} - -vector Navigation::Plan(const Vector2f& initial, - const Vector2f& end) { - vector path; - static CumulativeFunctionTimer function_timer_(__FUNCTION__); - CumulativeFunctionTimer::Invocation invoke(&function_timer_); - static const bool kVisualize = true; - typedef navigation::GraphDomain Domain; - planning_domain_.ResetDynamicStates(); - const uint64_t start_id = planning_domain_.AddDynamicState(initial); - const uint64_t goal_id = planning_domain_.AddDynamicState(end); - Domain::State start = planning_domain_.states[start_id]; - Domain::State goal = planning_domain_.states[goal_id]; - GraphVisualizer graph_viz(kVisualize); - const bool found_path = - AStar(start, goal, planning_domain_, &graph_viz, &path); - if (found_path) { - CHECK(path.size() > 0); - Vector2f s1 = plan_path_[0].loc; - for (size_t i = 1; i < plan_path_.size(); ++i) { - Vector2f s2 = plan_path_[i].loc; - s1 = s2; - } - } else { - printf("No path found!\n"); - } - - SimpleQueue intermediate_queue; // - unordered_map parent; - unordered_map cost; - - uint32_t mx = 0, my = 0; - costmap_.worldToMap(0, 0, mx, my); - uint32_t robot_index = costmap_.getIndex(mx, my); - intermediate_queue.Push(robot_index, 0); - cost[robot_index] = 0; - - global_plan_path_ = path; - Vector2f intermediate_goal_global = end; - GetGlobalCarrot(intermediate_goal_global); - - - if(path.size() == 0){ - return path; - } - - Vector2f intermediate_goal_local = intermediate_goal_global - robot_loc_; - - int goal_map_x, goal_map_y; - - costmap_.worldToMapEnforceBounds(intermediate_goal_local.x(), intermediate_goal_local.y(), goal_map_x, goal_map_y); - uint32_t goal_index = costmap_.getIndex(goal_map_x, goal_map_y); - double goal_relative_x, goal_relative_y; - costmap_.mapToWorld(goal_map_x, goal_map_y, goal_relative_x, goal_relative_y); - intermediate_goal_ = Vector2f(goal_relative_x, goal_relative_y) + robot_loc_; - - unordered_set visited; - - while(!intermediate_queue.Empty()){ - uint32_t current_index = intermediate_queue.Pop(); - visited.insert(current_index); - - if(current_index == goal_index){ - break; - } - - costmap_.indexToCells(current_index, mx, my); - vector neighbors {-1, 0, 1, 0}; - for(size_t i = 0; i < neighbors.size(); i++){ - int new_row = mx + neighbors[i]; - int new_col = my + neighbors[(i+1)%4]; - uint32_t neighbor_index = costmap_.getIndex(new_row, new_col); - - //TODO: fix when robot is in an obstacle in the costmap - if(new_row >= 0 && new_row < static_cast(costmap_.getSizeInCellsX()) && new_col >= 0 - && new_col < static_cast(costmap_.getSizeInCellsY())) { - - - double wx, wy; - costmap_.mapToWorld(new_row, new_col, wx, wy); - wx += robot_loc_.x(); - wy += robot_loc_.y(); - uint32_t global_mx, global_my; - bool in_global_map = global_costmap_.worldToMap(wx, wy, global_mx, global_my); - if(in_global_map){ - unsigned char max_costmap_cost = std::max(costmap_.getCost(new_row, new_col), global_costmap_.getCost(global_mx, global_my)); - float new_cost = cost[current_index] + params_.distance_weight + max_costmap_cost; - if(cost.count(neighbor_index) == 0 || new_cost < cost[neighbor_index]){ - cost[neighbor_index] = new_cost; - parent[neighbor_index] = current_index; - intermediate_queue.Push(neighbor_index, -new_cost); - } - } - } - } - } - - vector intermediate_vector_path; - vector intermediate_path; - - if(cost.count(goal_index) != 0){ - - uint32_t row, col; - costmap_.indexToCells(robot_index, row, col); - double wx, wy; - costmap_.mapToWorld(row, col, wx, wy); - Vector2f robot_location(wx, wy); - - uint32_t current_index = goal_index; - while(parent.count(current_index) > 0){ - costmap_.indexToCells(current_index, row, col); - costmap_.mapToWorld(row, col, wx, wy); - intermediate_vector_path.push_back(Vector2f(wx, wy) - robot_location); - current_index = parent[current_index]; - } - reverse(intermediate_vector_path.begin(), intermediate_vector_path.end()); - planning_domain_.ResetDynamicStates(); - - for(size_t i = 0; i < intermediate_vector_path.size(); i++){ - Vector2f map_frame_position = intermediate_vector_path[i] + robot_loc_; - const uint64_t path_id = planning_domain_.AddDynamicState(map_frame_position); - intermediate_path.push_back(planning_domain_.states[path_id]); - } - reverse(intermediate_path.begin(), intermediate_path.end()); - intermediate_path_found_ = true; - return intermediate_path; - } - else{ - intermediate_path_found_ = false; - printf("No intermediate planner path found\n"); - } - - - return path; -} - -void Navigation::PlannerTest() { - if (!loc_initialized_) return; - Plan(robot_loc_, nav_goal_loc_); -} - -DEFINE_double(max_plan_deviation, 0.5, - "Maximum premissible deviation from the plan"); -bool Navigation::PlanStillValid() { - if (plan_path_.size() < 2) return false; - for (size_t i = 0; i + 1 < plan_path_.size(); ++i) { - const float dist_from_segment = - geometry::DistanceFromLineSegment(robot_loc_, - plan_path_[i].loc, - plan_path_[i + 1].loc); - if (dist_from_segment < FLAGS_max_plan_deviation) { - return true; - } - } - return false; -} - -bool Navigation::IntermediatePlanStillValid(){ - - if (plan_path_.size() < 2 || !intermediate_path_found_) return false; - - // TODO: Add parameter for when to look for new goal or use different heuristic, may not be necessary with carrot replan - if ((nav_goal_loc_ - plan_path_[0].loc).norm() > sqrt(2 * params_.local_costmap_resolution) / 2 - && (robot_loc_ - plan_path_[0].loc).norm() < 1){ - return false; - } - - Vector2f global_carrot; - GetGlobalCarrot(global_carrot); - if((intermediate_goal_ - global_carrot).norm() > params_.replan_carrot_dist){ - return false; - } - - for (size_t i = 0; i < plan_path_.size(); i++){ - uint32_t mx, my; - Vector2f relative_path_location = plan_path_[i].loc - robot_loc_; - bool in_map = costmap_.worldToMap(relative_path_location.x(), relative_path_location.y(), mx, my); - if(in_map && costmap_.getCost(mx, my) == costmap_2d::LETHAL_OBSTACLE){ - return false; - } - } - return true; -} - -bool Navigation::GetGlobalCarrot(Vector2f& carrot) { - for(float carrot_dist = params_.carrot_dist; carrot_dist > params_.intermediate_carrot_dist; carrot_dist -= 0.5){ - if(GetCarrot(carrot, true, carrot_dist)){ - Vector2f robot_frame_carrot = carrot - robot_loc_; - uint32_t mx, my; - bool in_map = costmap_.worldToMap(robot_frame_carrot.x(), robot_frame_carrot.y(), mx, my); - if(in_map && costmap_.getCost(mx, my) != costmap_2d::LETHAL_OBSTACLE - && costmap_.getCost(mx, my) != costmap_2d::INSCRIBED_INFLATED_OBSTACLE){ - return true; - } - } - } - - return false; - // return GetCarrot(carrot, true, params_.carrot_dist); -} - -bool Navigation::GetIntermediateCarrot(Vector2f& carrot) { - return GetCarrot(carrot, false, params_.intermediate_carrot_dist); -} - -bool Navigation::GetCarrot(Vector2f& carrot, bool global, float carrot_dist) { - vector plan_path = plan_path_; - if(global){ - plan_path = global_plan_path_; - } - const float kSqCarrotDist = Sq(carrot_dist); - - CHECK_GE(plan_path.size(), 2u); - - if ((plan_path[0].loc - robot_loc_).squaredNorm() < kSqCarrotDist) { - // Goal is within the carrot dist. - carrot = plan_path[0].loc; - return true; - } - - // Find closest line segment in plan to current location - float closest_dist = FLT_MAX; - int i0 = 0, i1 = 1; - for (size_t i = 0; i + 1 < plan_path.size(); ++i) { - const Vector2f v0 = plan_path[i].loc; - const Vector2f v1 = plan_path[i + 1].loc; - const float dist_to_segment = geometry::DistanceFromLineSegment( - robot_loc_, v0, v1); - if (dist_to_segment < closest_dist) { - closest_dist = dist_to_segment; - i0 = i; - i1 = i + 1; - } - } - // printf("closest: %d %d %f\n", i0, i1, closest_dist); - - if (closest_dist > kSqCarrotDist) { - // Closest edge on the plan is farther than carrot dist to the robot. - // The carrot will be the projection of the robot loc on to the edge. - const Vector2f v0 = plan_path[i0].loc; - const Vector2f v1 = plan_path[i1].loc; - carrot = geometry::ProjectPointOntoLineSegment(robot_loc_, v0, v1); - return true; - } - - // Iterate from current line segment to goal until the segment intersects - // the circle centered at the robot, of radius kCarrotDist. - // The goal is not within carrot dist of the robot, and the robot is within - // carrot dist of some line segment. Hence, there must exist at least one - // vertex along the plan towards the goal that is out of the carrot dist. - for (int i = i1; i - 1 >= 0; --i) { - i0 = i; - // const Vector2f v0 = plan_path_[i].loc; - const Vector2f v1 = plan_path[i - 1].loc; - if ((v1 - robot_loc_).squaredNorm() > kSqCarrotDist) { - break; - } - } - i1 = i0 - 1; - // printf("i0:%d i1:%d\n", i0, i1); - const Vector2f v0 = plan_path[i0].loc; - const Vector2f v1 = plan_path[i1].loc; - Vector2f r0, r1; - #define V2COMP(v) v.x(), v.y() - // printf("%f,%f %f,%f %f,%f %f\n", - // V2COMP(robot_loc_), V2COMP(v0), V2COMP(v1), (v0 - v1).norm()); - const int num_intersections = geometry::CircleLineIntersection( - robot_loc_, carrot_dist, v0, v1, &r0, &r1); - if (num_intersections == 0) { - fprintf(stderr, "Error obtaining intersections:\n v0: (%f %f), v1: (%f %f), robot_loc_: (%f %f) sq_carrot_dist: (%f) closest_dist: (%f)\n", - v0.x(), v0.y(), v1.x(), v1.y(), robot_loc_.x(), robot_loc_.y(), kSqCarrotDist, closest_dist); - return false; - } - - if (num_intersections == 1 || (r0 - v1).squaredNorm() < (r1 - v1).squaredNorm()) { - carrot = r0; - } else { - carrot = r1; - } - return true; -} - -void Navigation::GetStraightFreePathLength(float* free_path_length, - float* clearance) { - // How much the robot's body extends in front of its base link frame. - const float l = 0.5 * params_.robot_length - params_.base_link_offset + params_.obstacle_margin; - // The robot's half-width. - const float w = 0.5 * params_.robot_width + params_.obstacle_margin; - for (const Vector2f& p : fp_point_cloud_) { - if (fabs(p.y()) > w || p.x() < 0.0f) continue; - *free_path_length = min(*free_path_length, p.x() - l); - } - *clearance = params_.max_clearance; - for (const Vector2f& p : point_cloud_) { - if (p.x() - l > *free_path_length || p.x() < 0.0) continue; - *clearance = min(*clearance, fabs(fabs(p.y() - w))); - } - *clearance = max(0.0f, *clearance); - *free_path_length = max(0.0f, *free_path_length); -} - -Vector2f GetFinalPoint(const PathOption& o) { - if (fabs(o.curvature) < 0.01) { - return Vector2f(o.free_path_length, 0); - } else { - const float r = 1.0f / o.curvature; - const float a = o.free_path_length / fabs(r); - return Vector2f(fabs(r) * sin(a), r * (1.0 - cos(a))); - } -} - -DEFINE_double(tx, 0.4, "Test obstacle point - X"); -DEFINE_double(ty, -0.38, "Test obstacle point - Y"); - -void Navigation::RunObstacleAvoidance(Vector2f& vel_cmd, float& ang_vel_cmd) { - static CumulativeFunctionTimer function_timer_(__FUNCTION__); - CumulativeFunctionTimer::Invocation invoke(&function_timer_); - const bool debug = FLAGS_v > 1; - - if(debug){ - printf("Running obstacle avoidance\n"); - } - - // Handling potential carrot overrides from social nav - Vector2f local_target = local_target_; - if (nav_state_ == NavigationState::kOverride) { - local_target = override_target_; - } - - sampler_->Update(robot_vel_, robot_omega_, local_target, fp_point_cloud_, latest_image_); - evaluator_->Update(robot_loc_, robot_angle_, robot_vel_, robot_omega_, local_target, fp_point_cloud_, latest_image_); - auto paths = sampler_->GetSamples(params_.num_options); - if (debug) { - printf("%lu options\n", paths.size()); - int i = 0; - for (auto p : paths) { - ConstantCurvatureArc arc = - *reinterpret_cast(p.get()); - printf("%3d: %7.5f %7.3f %7.3f\n", - i++, arc.curvature, arc.length, arc.curvature); - } - } - if (paths.size() == 0) { - // No options, just stop. - Halt(vel_cmd, ang_vel_cmd); - if (debug) printf("No paths found\n"); - return; - } - auto best_path = evaluator_->FindBest(paths); - if (best_path == nullptr) { - if (debug) printf("No best path found\n"); - // No valid path found! - // TODO: Change this to rotate instead of halt - TurnInPlace(vel_cmd, ang_vel_cmd); - // Halt(vel_cmd, ang_vel_cmd); - return; - } - ang_vel_cmd = 0; - vel_cmd = {0, 0}; - - float max_map_speed = params_.linear_limits.max_speed; - planning_domain_.GetClearanceAndSpeedFromLoc( - robot_loc_, nullptr, &max_map_speed); - auto linear_limits = params_.linear_limits; - linear_limits.max_speed = min(max_map_speed, params_.linear_limits.max_speed); - best_path->GetControls(linear_limits, - params_.angular_limits, - params_.dt, robot_vel_, - robot_omega_, - vel_cmd, - ang_vel_cmd); - last_options_ = paths; - best_option_ = best_path; -} - -void Navigation::Halt(Vector2f& cmd_vel, float& angular_vel_cmd) { - const float kEpsSpeed = 0.01; - const float velocity = robot_vel_.x(); - float velocity_cmd = 0; - if (fabs(velocity) > kEpsSpeed) { - const float dv = params_.linear_limits.max_deceleration * params_.dt; - if (velocity < -dv) { - velocity_cmd = velocity + dv; - } else if (velocity > dv) { - velocity_cmd = velocity - dv; - } else { - velocity_cmd = 0; - } - } - cmd_vel = {velocity_cmd, 0}; - //TODO: motion profiling for omega - angular_vel_cmd = 0; -} - -void Navigation::TurnInPlace(Vector2f& cmd_vel, float& cmd_angle_vel) { - cout << "turn in place call" << endl; - static const bool kDebug = false; - const float kMaxLinearSpeed = 0.1; - const float velocity = robot_vel_.x(); - cmd_angle_vel = 0; - - cout << "turn in place test 1" << endl; - - if (fabs(velocity) > kMaxLinearSpeed) { - Halt(cmd_vel, cmd_angle_vel); - return; - } - float dTheta = 0; - if (nav_state_ == NavigationState::kGoto) { - dTheta = atan2(local_target_.y(), local_target_.x()); - } else if (nav_state_ == NavigationState::kOverride) { - dTheta = atan2(override_target_.y(), override_target_.x()); - } else if (nav_state_ == NavigationState::kTurnInPlace) { - dTheta = AngleDiff(nav_goal_angle_, robot_angle_); - } - if (kDebug) printf("dTheta: %f robot_angle: %f\n", RadToDeg(dTheta), RadToDeg(robot_angle_)); - - cout << "turn in place test 2" << endl; - - const float s = Sign(dTheta); - if (robot_omega_ * dTheta < 0.0f) { - if (kDebug) printf("Wrong way\n"); - const float dv = params_.dt * params_.angular_limits.max_acceleration; - // Turning the wrong way! - if (fabs(robot_omega_) < dv) { - cmd_angle_vel = 0; - } else { - cmd_angle_vel = robot_omega_ - Sign(robot_omega_) * dv; - } - } else { - cout << "turn in place test 5" << endl; - cmd_angle_vel = s * Run1DTimeOptimalControl( - params_.angular_limits, - 0, - s * robot_omega_, - s * dTheta, - 0, - params_.dt); - cout << "test5: angle_vel = " << cmd_angle_vel << endl; - } - cmd_vel = {0, 0}; -} - -void Navigation::Pause() { - nav_state_ = NavigationState::kPaused; -} - -void Navigation::SetMaxVel(const float vel) { - params_.linear_limits.max_speed = vel; -} - -void Navigation::SetMaxAccel(const float accel) { - params_.linear_limits.max_acceleration = accel; - return; -} - -void Navigation::SetMaxDecel(const float decel) { - params_.linear_limits.max_deceleration = decel; - return; -} - -void Navigation::SetAngAccel(const float accel) { - params_.angular_limits.max_acceleration = accel; - return; -} - -void Navigation::SetAngVel(const float vel) { - params_.angular_limits.max_speed = vel; - return; -} - -void Navigation::SetObstacleMargin(const float margin) { - params_.obstacle_margin = margin; - return; -} - -void Navigation::SetClearanceWeight(const float weight) { - LinearEvaluator* evaluator = - dynamic_cast(evaluator_.get()); - evaluator->SetClearanceWeight(weight); - return; -} - -void Navigation::SetCarrotDist(const float carrot_dist) { - params_.carrot_dist = carrot_dist; - return; -} - -Eigen::Vector2f Navigation::GetTarget() { - return local_target_; -} - -Eigen::Vector2f Navigation::GetOverrideTarget() { - return override_target_; -} - -Eigen::Vector2f Navigation::GetVelocity() { - return robot_vel_; -} - -float Navigation::GetAngularVelocity() { - return robot_omega_; -} - -string Navigation::GetNavStatus() { - switch (nav_state_) { - case NavigationState::kStopped: { - return "Stopped"; - } break; - case NavigationState::kPaused: { - return "Paused"; - } break; - case NavigationState::kGoto: { - return "Goto"; - } break; - case NavigationState::kOverride: { - return "Override"; - } break; - case NavigationState::kTurnInPlace: { - return "TurnInPlace"; - } break; - default: { - return "Unknown"; - } break; - } -} - -uint8_t Navigation::GetNavStatusUint8() { - return static_cast(nav_state_); -} - -vector Navigation::GetPredictedCloud() { - return fp_point_cloud_; -} - -float Navigation::GetCarrotDist() { - return params_.intermediate_carrot_dist; -} - -float Navigation::GetObstacleMargin() { - return params_.obstacle_margin; -} - -float Navigation::GetRobotWidth() { - return params_.robot_width; -} - -float Navigation::GetRobotLength() { - return params_.robot_length; -} - -vector> Navigation::GetLastPathOptions() { - return last_options_; -} - -const cv::Mat& Navigation::GetVisualizationImage() { - if (params_.evaluator_type == "cost_map") { - return dynamic_cast(evaluator_.get())->latest_vis_image_; - } else { - std::cerr << "No visualization image for linear evaluator" << std::endl; - exit(1); - } -} - -std::shared_ptr Navigation::GetOption() { - return best_option_; -} - -vector Navigation::GetPlanPath() { - return plan_path_; -} - -vector Navigation::GetGlobalPath() { - return global_plan_path_; -} - -vector Navigation::GetCostmapObstacles(){ - return costmap_obstacles_; -} - -vector Navigation::GetGlobalCostmapObstacles(){ - return global_costmap_obstacles_; -} - -Eigen::Vector2f Navigation::GetIntermediateGoal(){ - return intermediate_goal_; -} - - -bool Navigation::Run(const double& time, - Vector2f& cmd_vel, - float& cmd_angle_vel) { - const bool kDebug = FLAGS_v > 0; - if (!initialized_) { - if (kDebug) printf("Parameters and maps not initialized\n"); - return false; - } - if (!odom_initialized_) { - if (kDebug) printf("Odometry not initialized\n"); - return false; - } - - ForwardPredict(time + params_.system_latency); - if (FLAGS_test_toc) { - TrapezoidTest(cmd_vel, cmd_angle_vel); - return true; - } else if (FLAGS_test_obstacle) { - ObstacleTest(cmd_vel, cmd_angle_vel); - return true; - } else if (FLAGS_test_avoidance) { - ObstAvTest(cmd_vel, cmd_angle_vel); - return true; - } else if (FLAGS_test_planner) { - PlannerTest(); - return true; - } else if (FLAGS_test_latency) { - LatencyTest(cmd_vel, cmd_angle_vel); - return true; - } - - int cell_inflation_size = std::ceil(params_.local_costmap_inflation_size/costmap_.getResolution()); - - int x_max = costmap_.getSizeInCellsX(); - int y_max = costmap_.getSizeInCellsY(); - - // Reset map to empty from previous iteration - costmap_.resetMap(0, 0, x_max, y_max); - costmap_obstacles_.clear(); - - // True if distance is less than replan inflation size - // Assign different value for points within inflation size but farther than replan size - unordered_map inflation_cells; - unordered_set obstacle_cells; - - // Map from costmap index to real coordinates relative to robot - unordered_map index_to_obstacle; - - uint32_t robot_mx, robot_my; - costmap_.worldToMap(0, 0, robot_mx, robot_my); - uint32_t robot_index = costmap_.getIndex(robot_mx, robot_my); - uint32_t robot_row, robot_col; - costmap_.indexToCells(robot_index, robot_row, robot_col); - double robot_wx, robot_wy; - costmap_.mapToWorld(robot_row, robot_col, robot_wx, robot_wy); - Vector2f robot_location(robot_wx, robot_wy); - - - // Add new points to costmap - for (size_t i = 0; i < point_cloud_.size(); i++){ - uint32_t unsigned_mx, unsigned_my; - Vector2f relative_location_map_frame = Rotation2Df(robot_angle_) * point_cloud_[i]; - bool in_map = costmap_.worldToMap(relative_location_map_frame.x(), relative_location_map_frame.y(), unsigned_mx, unsigned_my); - - //TODO: change max distance based on lidar to base link transformation - if (in_map && relative_location_map_frame.norm() < (params_.range_max - params_.robot_length) && relative_location_map_frame.norm() > params_.range_min){ - uint32_t index = costmap_.getIndex(unsigned_mx, unsigned_my); - double wx, wy; - costmap_.mapToWorld(unsigned_mx, unsigned_my, wx, wy); - obstacle_cells.insert(index); - - SeenObstacle obs; - obs.location = Vector2f(wx - robot_location.x(), wy - robot_location.y()); - obs.last_seen = std::time(nullptr); - index_to_obstacle[index] = obs; - } - } - - // Point sorting function - struct PointComparison { - const costmap_2d::Costmap2D costmap; - const int robot_mx; - const int robot_my; - - - PointComparison(const costmap_2d::Costmap2D map, const int robot_x, const int robot_y) : costmap(map), robot_mx(robot_x), robot_my(robot_y) {} - - bool operator()(int a, int b) const { - uint32_t row_a, col_a; - costmap.indexToCells(a, row_a, col_a); - uint32_t row_b, col_b; - costmap.indexToCells(b, row_b, col_b); - - double angle_a = atan2(row_a - robot_mx, col_a); - double angle_b = atan2(row_b - robot_my, col_b); - - return angle_a < angle_b; - } - }; - PointComparison pointComparison(costmap_, robot_mx, robot_my); - - // Define set of points to exclude from new costmap - set sorted_obstacle_cells(pointComparison); - unordered_set empty_cells; - - for (const auto& index : obstacle_cells) { - sorted_obstacle_cells.insert(index); - } - - auto iter = sorted_obstacle_cells.begin(); - - while (iter != sorted_obstacle_cells.end() && iter != std::prev(sorted_obstacle_cells.end())) { - costmap_2d::MapLocation point_a; - costmap_.indexToCells(*iter, point_a.x, point_a.y); - costmap_2d::MapLocation point_b; - costmap_.indexToCells(*std::next(iter), point_b.x, point_b.y); - costmap_2d::MapLocation robot_point; - robot_point.x = robot_mx; - robot_point.y = robot_my; - robot_point = robot_point; - vector polygon = {point_a, point_b, robot_point}; - vector fill_cells; - costmap_.convexFillCells(polygon, fill_cells); - - for (size_t i = 0; i < fill_cells.size(); i++){ - empty_cells.insert(costmap_.getIndex(fill_cells[i].x, fill_cells[i].y)); - } - - iter++; - } - - // Add old obstacles that are still in map to new costmap - for (const auto& obs : prev_obstacles_) { - Vector2f point = obs.location; - uint32_t unsigned_mx, unsigned_my; - Vector2f new_relative_point = point + prev_robot_loc_ - robot_loc_; - bool in_map = costmap_.worldToMap(new_relative_point.x(), new_relative_point.y(), unsigned_mx, unsigned_my); - uint32_t index = costmap_.getIndex(unsigned_mx, unsigned_my); - if (in_map && empty_cells.count(index) == 0 && std::time(nullptr) - obs.last_seen < params_.object_lifespan){ - obstacle_cells.insert(index); - if(index_to_obstacle.count(index) == 0){ - SeenObstacle new_obs; - new_obs.location = new_relative_point; - new_obs.last_seen = obs.last_seen; - index_to_obstacle[index] = new_obs; - } - else{ - index_to_obstacle[index].location = new_relative_point; - } - } - } - - for (const auto& index : obstacle_cells) { - uint32_t unsigned_mx, unsigned_my; - costmap_.indexToCells(index, unsigned_mx, unsigned_my); - int mx = static_cast(unsigned_mx); - int my = static_cast(unsigned_my); - for (int j = -cell_inflation_size; j <= cell_inflation_size; j++){ - for (int k = -cell_inflation_size; k <= cell_inflation_size; k++){ - float cell_dist = sqrt(pow(j, 2) + pow(k, 2)); - float dist = cell_dist * costmap_.getResolution(); - if((cell_dist <= cell_inflation_size) && (mx + j >= 0) && (mx + j < x_max) && (my + k >= 0) && (my + k < y_max)){ - unsigned char cost; - if(j == 0 && k == 0){ - cost = costmap_2d::LETHAL_OBSTACLE; - } - else if(dist <= params_.replan_inflation_size){ - cost = costmap_2d::INSCRIBED_INFLATED_OBSTACLE; - } - else{ - cost = std::ceil(std::exp(-1 * params_.inflation_coeff * (dist - params_.replan_inflation_size)) * (costmap_2d::INSCRIBED_INFLATED_OBSTACLE-1)); - } - costmap_.setCost(mx + j, my + k, std::max(cost, costmap_.getCost(mx + j, my + k))); - inflation_cells[costmap_.getIndex(mx + j, my + k)] = std::max(cost, costmap_.getCost(mx + j, my + k)); - } - } - } - } - - for (const auto& pair : inflation_cells) { - uint32_t index = pair.first; - uint32_t mx = 0; - uint32_t my = 0; - costmap_.indexToCells(index, mx, my); - - double wx, wy; - costmap_.mapToWorld(mx, my, wx, wy); - costmap_obstacles_.push_back(ObstacleCost{Vector2f(wx, wy) + robot_loc_, pair.second}); - } - - // Record last seen locations of points - prev_robot_loc_ = robot_loc_; - prev_obstacles_.clear(); - - for (const auto& pair : index_to_obstacle) { - SeenObstacle obs = pair.second; - prev_obstacles_.push_back(obs); - } - - - // Before switching states we need to update the local target. - - if (nav_state_ == NavigationState::kGoto || - nav_state_ == NavigationState::kOverride) { - // Recompute global plan as necessary. - if (!PlanStillValid() || !IntermediatePlanStillValid()) { - if (kDebug) printf("Replanning\n"); - plan_path_ = Plan(robot_loc_, nav_goal_loc_); - } - if (nav_state_ == NavigationState::kGoto) { - // Get Carrot and check if done - Vector2f carrot(0, 0); - bool foundCarrot = GetIntermediateCarrot(carrot); - if (!foundCarrot) { - Halt(cmd_vel, cmd_angle_vel); - return false; - } - // Local Navigation - cout << "set local target" << endl; - local_target_ = Rotation2Df(-robot_angle_) * (carrot - robot_loc_); - } - } - - // Switch between navigation states. - NavigationState prev_state = nav_state_; - do { - prev_state = nav_state_; - if (nav_state_ == NavigationState::kGoto && - local_target_.squaredNorm() < Sq(params_.target_dist_tolerance) && - robot_vel_.squaredNorm() < Sq(params_.target_vel_tolerance)) { - cout << "set turn in place" << endl; - nav_state_ = NavigationState::kTurnInPlace; - } else if (nav_state_ == NavigationState::kTurnInPlace && - AngleDist(robot_angle_, nav_goal_angle_) < - params_.target_angle_tolerance) { - nav_state_ = NavigationState::kStopped; - } - } while (prev_state != nav_state_); - - - switch (nav_state_) { - case NavigationState::kStopped: { - if (kDebug) printf("\nNav complete\n"); - } break; - case NavigationState::kPaused: { - if (kDebug) printf("\nNav paused\n"); - } break; - case NavigationState::kGoto: { - if (kDebug) printf("\nNav Goto\n"); - } break; - case NavigationState::kTurnInPlace: { - if (kDebug) printf("\nNav TurnInPlace\n"); - } break; - case NavigationState::kOverride: { - if (kDebug) printf("\nNav override\n"); - } break; - default: { - fprintf(stderr, "ERROR: Unknown nav state %d\n", - static_cast(nav_state_)); - } - } - - if (nav_state_ == NavigationState::kPaused || - nav_state_ == NavigationState::kStopped) { - Halt(cmd_vel, cmd_angle_vel); - return true; - } else if (nav_state_ == NavigationState::kGoto || - nav_state_ == NavigationState::kOverride) { - Vector2f local_target(0, 0); - if (nav_state_ == NavigationState::kGoto) { - // Local Navigation - local_target = local_target_; - } else { - // Running NavigationState::kOverride . - local_target = override_target_; - } - const float theta = atan2(local_target.y(), local_target.x()); - if (local_target.squaredNorm() > params_.intermediate_carrot_dist) { - local_target = params_.intermediate_carrot_dist * local_target.normalized(); - } - if (!FLAGS_no_local) { - if (fabs(theta) > params_.local_fov) { - if (kDebug) printf("TurnInPlace\n"); - TurnInPlace(cmd_vel, cmd_angle_vel); - } else { - if (kDebug) printf("ObstAv\n"); - cout << "running obstacle avoidance original" << endl; - RunObstacleAvoidance(cmd_vel, cmd_angle_vel); - } - } - } else if (nav_state_ == NavigationState::kTurnInPlace) { - if (kDebug) printf("Reached Goal: TurnInPlace\n"); - TurnInPlace(cmd_vel, cmd_angle_vel); - } - - // viz_pub_.publish(local_viz_msg_); - // viz_pub_.publish(global_viz_msg_); - - return true; -} - -} // namespace navigation +//======================================================================== +// This software is free: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License Version 3, +// as published by the Free Software Foundation. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// Version 3 in the file COPYING that came with this distribution. +// If not, see . +//======================================================================== +/*! +\file navigation.cc +\brief Implementation for reference Navigation class. +\author Joydeep Biswas, Jarrett Holtz, Kavan Sikand (C) 2021 +*/ +//======================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "navigation.h" +#include "geometry_msgs/PoseStamped.h" +#include "gflags/gflags.h" +#include "eigen3/Eigen/Dense" +#include "eigen3/Eigen/Geometry" +#include "glog/logging.h" +#include "shared/math/math_util.h" +#include "shared/util/helpers.h" +#include "shared/util/timer.h" +#include "shared/util/timer.h" +#include "eight_connected_domain.h" +#include "graph_domain.h" +#include "astar.h" +#include "simple_queue.h" + +#include "motion_primitives.h" +#include "constant_curvature_arcs.h" +#include "ackermann_motion_primitives.h" +#include "deep_cost_map_evaluator.h" +#include "linear_evaluator.h" +#include "amrl_msgs/NavStatusMsg.h" +#include "amrl_msgs/Pose2Df.h" + +#include "nlohmann/json.hpp" +using json = nlohmann::json; + +using Eigen::Rotation2Df; +using Eigen::Vector2f; +using navigation::MotionLimits; +using navigation::Odom; +using navigation::Twist; +using std::atan2; +using std::deque; +using std::max; +using std::min; +using std::swap; +using std::shared_ptr; +using std::string; +using std::vector; +using std::unordered_set; +using std::unordered_map; +using std::set; + +using namespace math_util; +using namespace motion_primitives; + +// Special test modes. +DEFINE_bool(test_toc, false, "Run 1D time-optimal controller test"); +DEFINE_bool(test_obstacle, false, "Run obstacle detection test"); +DEFINE_bool(test_avoidance, false, "Run obstacle avoidance test"); +DEFINE_bool(test_planner, false, "Run navigation planner test"); +DEFINE_bool(test_latency, false, "Run Latency test"); +DEFINE_double(test_dist, 0.5, "Test distance"); +DEFINE_string(test_log_file, "", "Log test results to file"); + +DEFINE_double(max_curvature, 2.0, "Maximum curvature of turning"); + +DEFINE_bool(no_local, false, "can be used to turn off local planner"); + +namespace { +// Epsilon value for handling limited numerical precision. +const float kEpsilon = 1e-5; + +DEFINE_int32(num_options, 41, "Number of options to consider"); + +// TODO(jaholtz) figure out how to handle this visualization without +// having astar contain ros dependencies +struct EightGridVisualizer { + EightGridVisualizer(bool visualize) : + kVisualize(visualize) { } + + void DrawEdge(const navigation::EightConnectedDomain::State& s1, + const navigation::EightConnectedDomain::State& s2) { + if (!kVisualize) return; + static const bool kDebug = false; + if (kDebug) { + printf("%7.2f,%7.2f -> %7.2f,%7.2f\n", + s1.x(), + s1.y(), + s2.x(), + s2.y()); + } + // visualization::DrawLine(s1, s2, 0x606060, global_viz_msg_); + // viz_pub_.publish(global_viz_msg_); + if (kDebug) Sleep(0.05); + } + + const bool kVisualize; +}; + +struct GraphVisualizer { + GraphVisualizer(bool visualize) : + kVisualize(visualize) { } + + void DrawEdge(const navigation::GraphDomain::State& s1, + const navigation::GraphDomain::State& s2) { + if (!kVisualize) return; + static const bool kDebug = false; + if (kDebug) { + printf("%7.2f,%7.2f -> %7.2f,%7.2f\n", + s1.loc.x(), + s1.loc.y(), + s2.loc.x(), + s2.loc.y()); + } + // visualization::DrawLine(s1.loc, s2.loc, 0xC0C0C0, global_viz_msg_); + // viz_pub_.publish(global_viz_msg_); + if (kDebug) Sleep(0.05); + } + + const bool kVisualize; +}; + +} // namespace + +namespace navigation { + +Navigation::Navigation() : + robot_loc_(0, 0), + robot_angle_(0), + robot_vel_(0, 0), + robot_omega_(0), + nav_state_(NavigationState::kStopped), + nav_goal_loc_(0, 0), + nav_goal_angle_(0), + odom_initialized_(false), + loc_initialized_(false), + t_point_cloud_(0), + t_odometry_(0), + enabled_(false), + initialized_(false), + sampler_(nullptr), + evaluator_(nullptr), + costmap_(60, 60, 0.5, -15, -15), + global_costmap_(200, 200, 0.5, -50, -50), + intermediate_path_found_(false), + intermediate_goal_(0, 0){ //parameters are (x/y size in cells, resolution, bottom left x/y origin coordinates) + sampler_ = std::unique_ptr(new AckermannSampler()); +} + +void Navigation::Initialize(const NavigationParameters& params, + const string& map_file) { + // Initialize status message + params_ = params; + int local_costmap_size = 2*static_cast(std::round(params_.local_costmap_radius/params_.local_costmap_resolution)); + costmap_ = costmap_2d::Costmap2D(local_costmap_size, local_costmap_size, params_.local_costmap_resolution, -params.local_costmap_radius, -params.local_costmap_radius); + int global_costmap_size = 2*static_cast(std::round(params_.global_costmap_radius/params_.global_costmap_resolution)); + global_costmap_ = costmap_2d::Costmap2D(global_costmap_size, global_costmap_size, params_.global_costmap_resolution, params.global_costmap_origin_x, params.global_costmap_origin_y); + planning_domain_ = GraphDomain(map_file, ¶ms_); + + LoadVectorMap(map_file); + + initialized_ = true; + sampler_->SetNavParams(params); + + PathEvaluatorBase* evaluator = nullptr; + if (params_.evaluator_type == "cost_map") { + auto cost_map_evaluator = new DeepCostMapEvaluator(params_); + cost_map_evaluator->LoadModel(); + evaluator = (PathEvaluatorBase*) cost_map_evaluator; + } else if (params_.evaluator_type == "linear") { + evaluator = (PathEvaluatorBase*) new LinearEvaluator(); + } else { + printf("Uknown evaluator type %s\n", params_.evaluator_type.c_str()); + exit(1); + } + evaluator_ = std::unique_ptr(evaluator); +} + +void Navigation::LoadVectorMap(const string& map_file){ //Assume map is given as MAP.navigation.json + + std::string vector_map_file = map_file; + + // Find the position of ".navigation.json" + size_t found = vector_map_file.find(".navigation.json"); + + // Replace ".navigation.json" with ".vectormap.json" + if (found != std::string::npos) { + vector_map_file.replace(found, std::string(".navigation.json").length(), ".vectormap.json"); + } + + // Output the modified string + std::cout << "Loading vectormap file: " << vector_map_file << std::endl; + + int x_max = global_costmap_.getSizeInCellsX(); + int y_max = global_costmap_.getSizeInCellsY(); + global_costmap_.resetMap(0, 0, x_max, y_max); + global_costmap_obstacles_.clear(); + + std::ifstream i(vector_map_file); + json j; + i >> j; + i.close(); + + unordered_map inflation_cells; + + for (const auto& line : j) { + // Access specific fields in each dictionary + Vector2f p0(line["p0"]["x"], line["p0"]["y"]); + Vector2f p1(line["p1"]["x"], line["p1"]["y"]); + + float length = (p0 - p1).norm(); + for (float i = 0; i < length; i += params_.global_costmap_resolution){ + Vector2f costmap_point = p0 + i*(p1 - p0)/length; + uint32_t unsigned_mx, unsigned_my; + bool in_map = global_costmap_.worldToMap(costmap_point.x(), costmap_point.y(), unsigned_mx, unsigned_my); + if(in_map){ + int cell_inflation_size = std::ceil(params_.global_costmap_inflation_size/global_costmap_.getResolution()); + int mx = static_cast(unsigned_mx); + int my = static_cast(unsigned_my); + for (int j = -cell_inflation_size; j <= cell_inflation_size; j++){ + for (int k = -cell_inflation_size; k <= cell_inflation_size; k++){ + float cell_dist = sqrt(pow(j, 2) + pow(k, 2)); + float dist = cell_dist * global_costmap_.getResolution(); + if((cell_dist <= cell_inflation_size) && (mx + j >= 0) && (mx + j < x_max) && (my + k >= 0) && (my + k < y_max)){ + unsigned char cost; + if(j == 0 && k == 0){ + cost = costmap_2d::LETHAL_OBSTACLE; + } + else if(dist <= params_.replan_inflation_size){ + cost = costmap_2d::INSCRIBED_INFLATED_OBSTACLE; + } + else{ + cost = std::ceil(std::exp(-1 * params_.inflation_coeff * (dist - params_.replan_inflation_size)) * (costmap_2d::INSCRIBED_INFLATED_OBSTACLE-1)); + } + global_costmap_.setCost(mx + j, my + k, std::max(cost, global_costmap_.getCost(mx + j, my + k))); + inflation_cells[global_costmap_.getIndex(mx + j, my + k)] = std::max(cost, global_costmap_.getCost(mx + j, my + k)); + } + // if((sqrt(pow(j, 2) + pow(k, 2)) <= cell_inflation_size) && (mx + j >= 0) && (mx + j < x_max) + // && (my + k >= 0) && (my + k < y_max)){ + // inflation_cells.insert(global_costmap_.getIndex(mx + j, my + k)); + // } + } + } + } + } + } + + for (const auto& pair : inflation_cells) { + auto index = pair.first; + uint32_t mx = 0; + uint32_t my = 0; + global_costmap_.indexToCells(index, mx, my); + // global_costmap_.setCost(mx, my, costmap_2d::LETHAL_OBSTACLE); + + double wx, wy; + global_costmap_.mapToWorld(mx, my, wx, wy); + global_costmap_obstacles_.push_back(ObstacleCost{Vector2f(wx, wy), pair.second}); + } + +} + +bool Navigation::Enabled() const { + return enabled_; +} + +void Navigation::Enable(bool enable) { + enabled_ = enable; +} + +void Navigation::SetNavGoal(const Vector2f& loc, float angle) { + cout << "set nav goal goto" << endl; + nav_state_ = NavigationState::kGoto; + nav_goal_loc_ = loc; + nav_goal_angle_ = angle; + plan_path_.clear(); +} + +void Navigation::ResetNavGoals() { + nav_state_ = NavigationState::kStopped; + nav_goal_loc_ = robot_loc_; + nav_goal_angle_ = robot_angle_; + local_target_.setZero(); + plan_path_.clear(); +} + +void Navigation::SetOverride(const Vector2f& loc, float angle) { + nav_state_ = NavigationState::kOverride; + override_target_ = loc; +} + +void Navigation::Resume() { + cout << "resume goto" << endl; + nav_state_ = NavigationState::kGoto; +} + +void Navigation::UpdateMap(const string& map_path) { + LoadVectorMap(map_path); + planning_domain_.Load(map_path); + plan_path_.clear(); + prev_obstacles_.clear(); + costmap_obstacles_.clear(); +} + +void Navigation::UpdateLocation(const Eigen::Vector2f& loc, float angle) { + robot_loc_ = loc; + robot_angle_ = angle; + loc_initialized_ = true; +} + +void Navigation::PruneLatencyQueue() { + if (command_history_.empty()) return; + const double update_time = min(t_point_cloud_, t_odometry_); + static const bool kDebug = false; + for (size_t i = 0; i < command_history_.size(); ++i) { + const double t_cmd = command_history_[i].time; + if (kDebug) { + printf("Command %d %f\n", int(i), t_cmd - update_time); + } + if (t_cmd < update_time - params_.dt) { + if (kDebug) { + printf("Erase %d %f %f\n", + int(i), + t_cmd - update_time, + command_history_[i].linear.x()); + } + command_history_.erase( command_history_.begin() + i); + --i; + } + } +} + +void Navigation::UpdateOdometry(const Odom& msg) { + latest_odom_msg_ = msg; + t_odometry_ = msg.time; + PruneLatencyQueue(); + if (!odom_initialized_) { + starting_loc_ = Vector2f(msg.position.x(), msg.position.y()); + odom_initialized_ = true; + } +} + +void Navigation::UpdateCommandHistory(Twist twist) { + twist.time += params_.system_latency; + command_history_.push_back(twist); + if (false) { + printf("Push %f %f\n", + twist.linear.x(), + command_history_.back().linear.x()); + } +} + +void Navigation::ForwardPredict(double t) { + if (command_history_.empty()) { + robot_vel_ = Vector2f(0, 0); + robot_omega_ = 0; + } else { + const Twist latest_twist = command_history_.back(); + robot_vel_ = Vector2f(latest_twist.linear.x(), latest_twist.linear.y()); + robot_omega_ = latest_twist.angular.z(); + } + if (false) { + for (size_t i = 0; i < command_history_.size(); ++i) { + const auto& c = command_history_[i]; + printf("%d %f %f\n", int(i), t - c.time, c.linear.x()); + } + printf("Predict: %f %f\n", t - t_odometry_, t - t_point_cloud_); + } + odom_loc_ = Vector2f(latest_odom_msg_.position.x(), + latest_odom_msg_.position.y()); + odom_angle_ = 2.0f * atan2f(latest_odom_msg_.orientation.z(), + latest_odom_msg_.orientation.w()); + using Eigen::Affine2f; + using Eigen::Rotation2Df; + using Eigen::Translation2f; + Affine2f lidar_tf = Affine2f::Identity(); + for (const Twist& c : command_history_) { + const double cmd_time = c.time; + if (cmd_time > t) continue; + if (cmd_time >= t_odometry_ - params_.dt) { + const float dt = (t_odometry_ > cmd_time) ? + min(t_odometry_ - cmd_time, params_.dt) : + min(t - cmd_time, params_.dt); + odom_loc_ += dt * (Rotation2Df(odom_angle_) * Vector2f( + c.linear.x(), c.linear.y())); + odom_angle_ = AngleMod(odom_angle_ + dt * c.angular.z()); + } + if (t_point_cloud_ >= cmd_time - params_.dt) { + const float dt = (t_point_cloud_ > cmd_time) ? + min(t_point_cloud_ - cmd_time, params_.dt) : + min(t - cmd_time, params_.dt); + lidar_tf = + Translation2f(-dt * Vector2f(c.linear.x(), c.linear.y())) * + Rotation2Df(-c.angular.z() * dt) * + lidar_tf; + } + } + fp_point_cloud_.resize(point_cloud_.size()); + for (size_t i = 0; i < point_cloud_.size(); ++i) { + fp_point_cloud_[i] = lidar_tf * point_cloud_[i]; + } +} + +void Navigation::TrapezoidTest(Vector2f& cmd_vel, float& cmd_angle_vel) { + if (!odom_initialized_) return; + const float x = (odom_loc_ - starting_loc_).norm(); + const float speed = robot_vel_.norm(); + const float velocity_cmd = Run1DTimeOptimalControl( + params_.linear_limits, + x, + speed, + FLAGS_test_dist, + 0, + params_.dt); + cmd_vel = {velocity_cmd, 0}; + cmd_angle_vel = 0; + printf("x: %.3f d:%.3f v: %.3f cmd:%.3f\n", x, FLAGS_test_dist, speed, velocity_cmd); +} + +void Navigation::LatencyTest(Vector2f& cmd_vel, float& cmd_angle_vel) { + static FILE* fid = nullptr; + if (!FLAGS_test_log_file.empty() && fid == nullptr) { + fid = fopen(FLAGS_test_log_file.c_str(), "w"); + } + const float kMaxSpeed = 0.75; + const float kFrequency = 0.4; + + static double t_start_ = GetMonotonicTime(); + const double t = GetMonotonicTime() - t_start_; + // float v_current = robot_vel_.x(); + float v_cmd = kMaxSpeed * + sin(2.0 * M_PI * kFrequency * t); + cmd_vel = {v_cmd, 0}; + cmd_angle_vel = 0.0; +} + +void Navigation::ObstAvTest(Vector2f& cmd_vel, float& cmd_angle_vel) { + const Vector2f kTarget(4, 0); + local_target_ = kTarget; + RunObstacleAvoidance(cmd_vel, cmd_angle_vel); +} + +void Navigation::ObstacleTest(Vector2f& cmd_vel, float& cmd_angle_vel) { + const float speed = robot_vel_.norm(); + float free_path_length = 30.0; + float clearance = 10; + GetStraightFreePathLength(&free_path_length, &clearance); + const float dist_left = + max(0.0f, free_path_length - params_.obstacle_margin); + printf("%f\n", free_path_length); + const float velocity_cmd = Run1DTimeOptimalControl( + params_.linear_limits, + 0, + speed, + dist_left, + 0, + params_.dt); + cmd_vel = {velocity_cmd, 0}; + cmd_angle_vel = 0; +} + +Vector2f GetClosestApproach(const PathOption& o, const Vector2f& target) { + if (fabs(o.curvature) < kEpsilon) { + // Straight line path + if (target.x() > o.free_path_length) { + return Vector2f(o.free_path_length, 0); + } else if (target.x() < 0.0) { + return Vector2f(0, 0); + } else { + return Vector2f(target.x(), 0); + } + } + const float end_angle = fabs(o.curvature * o.free_path_length); + const float turn_radius = 1.0f / o.curvature; + const Vector2f turn_center(0, turn_radius); + const Vector2f target_radial = target - turn_center; + + const Vector2f start(0, 0); + const Vector2f middle_radial = fabs(turn_radius) * target_radial.normalized(); + const Vector2f middle = turn_center + middle_radial; + const float middle_angle = + atan2(fabs(middle_radial.x()), fabs(middle_radial.y())); + + const Vector2f end(fabs(turn_radius) * sin(end_angle), + turn_radius * (1.0f - cos(end_angle))); + + Vector2f closest_point = start; + if (middle_angle < end_angle && + (closest_point - target).squaredNorm() > + (middle - target).squaredNorm()) { + closest_point = middle; + } + if ((closest_point - target).squaredNorm() > + (end - target).squaredNorm()) { + closest_point = end; + } + return closest_point; +} + +float GetClosestDistance(const PathOption& o, const Vector2f& target) { + const Vector2f closest_point = GetClosestApproach(o, target); + return (target - closest_point).norm(); +} + +void Navigation::ObservePointCloud(const vector& cloud, + double time) { + point_cloud_ = cloud; + t_point_cloud_ = time; + PruneLatencyQueue(); +} + +void Navigation::ObserveImage(cv::Mat image, double time) { + latest_image_ = image; + t_image_ = time; +} + +vector Navigation::GlobalPlan(const Vector2f& initial, + const Vector2f& end) { + auto plan = Plan(initial, end); + std::vector path; + for (auto& node : plan ) { + path.push_back(node.id); + } + return path; +} + +vector Navigation::Plan(const Vector2f& initial, + const Vector2f& end) { + vector path; + static CumulativeFunctionTimer function_timer_(__FUNCTION__); + CumulativeFunctionTimer::Invocation invoke(&function_timer_); + static const bool kVisualize = true; + typedef navigation::GraphDomain Domain; + planning_domain_.ResetDynamicStates(); + const uint64_t start_id = planning_domain_.AddDynamicState(initial); + const uint64_t goal_id = planning_domain_.AddDynamicState(end); + Domain::State start = planning_domain_.states[start_id]; + Domain::State goal = planning_domain_.states[goal_id]; + GraphVisualizer graph_viz(kVisualize); + const bool found_path = + AStar(start, goal, planning_domain_, &graph_viz, &path); + if (found_path) { + CHECK(path.size() > 0); + Vector2f s1 = plan_path_[0].loc; + for (size_t i = 1; i < plan_path_.size(); ++i) { + Vector2f s2 = plan_path_[i].loc; + s1 = s2; + } + } else { + printf("No path found!\n"); + } + + SimpleQueue intermediate_queue; // + unordered_map parent; + unordered_map cost; + + uint32_t mx = 0, my = 0; + costmap_.worldToMap(0, 0, mx, my); + uint32_t robot_index = costmap_.getIndex(mx, my); + intermediate_queue.Push(robot_index, 0); + cost[robot_index] = 0; + + global_plan_path_ = path; + Vector2f intermediate_goal_global = end; + GetGlobalCarrot(intermediate_goal_global); + + + if(path.size() == 0){ + return path; + } + + Vector2f intermediate_goal_local = intermediate_goal_global - robot_loc_; + + int goal_map_x, goal_map_y; + + costmap_.worldToMapEnforceBounds(intermediate_goal_local.x(), intermediate_goal_local.y(), goal_map_x, goal_map_y); + uint32_t goal_index = costmap_.getIndex(goal_map_x, goal_map_y); + double goal_relative_x, goal_relative_y; + costmap_.mapToWorld(goal_map_x, goal_map_y, goal_relative_x, goal_relative_y); + intermediate_goal_ = Vector2f(goal_relative_x, goal_relative_y) + robot_loc_; + + unordered_set visited; + + while(!intermediate_queue.Empty()){ + uint32_t current_index = intermediate_queue.Pop(); + visited.insert(current_index); + + if(current_index == goal_index){ + break; + } + + costmap_.indexToCells(current_index, mx, my); + vector neighbors {-1, 0, 1, 0}; + for(size_t i = 0; i < neighbors.size(); i++){ + int new_row = mx + neighbors[i]; + int new_col = my + neighbors[(i+1)%4]; + uint32_t neighbor_index = costmap_.getIndex(new_row, new_col); + + //TODO: fix when robot is in an obstacle in the costmap + if(new_row >= 0 && new_row < static_cast(costmap_.getSizeInCellsX()) && new_col >= 0 + && new_col < static_cast(costmap_.getSizeInCellsY())) { + + + double wx, wy; + costmap_.mapToWorld(new_row, new_col, wx, wy); + wx += robot_loc_.x(); + wy += robot_loc_.y(); + uint32_t global_mx, global_my; + bool in_global_map = global_costmap_.worldToMap(wx, wy, global_mx, global_my); + unsigned char max_costmap_cost = costmap_.getCost(new_row, new_col); + if(in_global_map){ + max_costmap_cost = std::max(costmap_.getCost(new_row, new_col), global_costmap_.getCost(global_mx, global_my)); + } + float new_cost = cost[current_index] + params_.distance_weight + max_costmap_cost; + if(cost.count(neighbor_index) == 0 || new_cost < cost[neighbor_index]){ + cost[neighbor_index] = new_cost; + parent[neighbor_index] = current_index; + intermediate_queue.Push(neighbor_index, -new_cost); + } + } + } + } + + vector intermediate_vector_path; + vector intermediate_path; + + if(cost.count(goal_index) != 0){ + + uint32_t row, col; + costmap_.indexToCells(robot_index, row, col); + double wx, wy; + costmap_.mapToWorld(row, col, wx, wy); + Vector2f robot_location(wx, wy); + + uint32_t current_index = goal_index; + while(parent.count(current_index) > 0){ + costmap_.indexToCells(current_index, row, col); + costmap_.mapToWorld(row, col, wx, wy); + intermediate_vector_path.push_back(Vector2f(wx, wy) - robot_location); + current_index = parent[current_index]; + } + reverse(intermediate_vector_path.begin(), intermediate_vector_path.end()); + planning_domain_.ResetDynamicStates(); + + for(size_t i = 0; i < intermediate_vector_path.size(); i++){ + Vector2f map_frame_position = intermediate_vector_path[i] + robot_loc_; + const uint64_t path_id = planning_domain_.AddDynamicState(map_frame_position); + intermediate_path.push_back(planning_domain_.states[path_id]); + } + reverse(intermediate_path.begin(), intermediate_path.end()); + intermediate_path_found_ = true; + return intermediate_path; + } + else{ + intermediate_path_found_ = false; + printf("No intermediate planner path found\n"); + } + + + return path; +} + +void Navigation::PlannerTest() { + if (!loc_initialized_) return; + Plan(robot_loc_, nav_goal_loc_); +} + +DEFINE_double(max_plan_deviation, 0.5, + "Maximum premissible deviation from the plan"); +bool Navigation::PlanStillValid() { + if (plan_path_.size() < 2) return false; + for (size_t i = 0; i + 1 < plan_path_.size(); ++i) { + const float dist_from_segment = + geometry::DistanceFromLineSegment(robot_loc_, + plan_path_[i].loc, + plan_path_[i + 1].loc); + if (dist_from_segment < FLAGS_max_plan_deviation) { + return true; + } + } + return false; +} + +bool Navigation::IntermediatePlanStillValid(){ + + if (plan_path_.size() < 2 || !intermediate_path_found_) return false; + + // TODO: Add parameter for when to look for new goal or use different heuristic, may not be necessary with carrot replan + if ((nav_goal_loc_ - plan_path_[0].loc).norm() > sqrt(2 * params_.local_costmap_resolution) / 2 + && (robot_loc_ - plan_path_[0].loc).norm() < 1){ + return false; + } + + Vector2f global_carrot; + GetGlobalCarrot(global_carrot); + if((intermediate_goal_ - global_carrot).norm() > params_.replan_carrot_dist){ + return false; + } + + for (size_t i = 0; i < plan_path_.size(); i++){ + uint32_t mx, my; + Vector2f relative_path_location = plan_path_[i].loc - robot_loc_; + bool in_map = costmap_.worldToMap(relative_path_location.x(), relative_path_location.y(), mx, my); + if(in_map && costmap_.getCost(mx, my) == costmap_2d::LETHAL_OBSTACLE){ + return false; + } + } + return true; +} + +bool Navigation::GetGlobalCarrot(Vector2f& carrot) { + for(float carrot_dist = params_.carrot_dist; carrot_dist > params_.intermediate_carrot_dist; carrot_dist -= 0.5){ + if(GetCarrot(carrot, true, carrot_dist)){ + Vector2f robot_frame_carrot = carrot - robot_loc_; + uint32_t mx, my; + bool in_map = costmap_.worldToMap(robot_frame_carrot.x(), robot_frame_carrot.y(), mx, my); + if(in_map && costmap_.getCost(mx, my) != costmap_2d::LETHAL_OBSTACLE + && costmap_.getCost(mx, my) != costmap_2d::INSCRIBED_INFLATED_OBSTACLE){ + return true; + } + } + } + + return false; + // return GetCarrot(carrot, true, params_.carrot_dist); +} + +bool Navigation::GetIntermediateCarrot(Vector2f& carrot) { + return GetCarrot(carrot, false, params_.intermediate_carrot_dist); +} + +bool Navigation::GetCarrot(Vector2f& carrot, bool global, float carrot_dist) { + vector plan_path = plan_path_; + if(global){ + plan_path = global_plan_path_; + } + const float kSqCarrotDist = Sq(carrot_dist); + + CHECK_GE(plan_path.size(), 2u); + + if ((plan_path[0].loc - robot_loc_).squaredNorm() < kSqCarrotDist) { + // Goal is within the carrot dist. + carrot = plan_path[0].loc; + return true; + } + + // Find closest line segment in plan to current location + float closest_dist = FLT_MAX; + int i0 = 0, i1 = 1; + for (size_t i = 0; i + 1 < plan_path.size(); ++i) { + const Vector2f v0 = plan_path[i].loc; + const Vector2f v1 = plan_path[i + 1].loc; + const float dist_to_segment = geometry::DistanceFromLineSegment( + robot_loc_, v0, v1); + if (dist_to_segment < closest_dist) { + closest_dist = dist_to_segment; + i0 = i; + i1 = i + 1; + } + } + // printf("closest: %d %d %f\n", i0, i1, closest_dist); + + if (closest_dist > kSqCarrotDist) { + // Closest edge on the plan is farther than carrot dist to the robot. + // The carrot will be the projection of the robot loc on to the edge. + const Vector2f v0 = plan_path[i0].loc; + const Vector2f v1 = plan_path[i1].loc; + carrot = geometry::ProjectPointOntoLineSegment(robot_loc_, v0, v1); + return true; + } + + // Iterate from current line segment to goal until the segment intersects + // the circle centered at the robot, of radius kCarrotDist. + // The goal is not within carrot dist of the robot, and the robot is within + // carrot dist of some line segment. Hence, there must exist at least one + // vertex along the plan towards the goal that is out of the carrot dist. + for (int i = i1; i - 1 >= 0; --i) { + i0 = i; + // const Vector2f v0 = plan_path_[i].loc; + const Vector2f v1 = plan_path[i - 1].loc; + if ((v1 - robot_loc_).squaredNorm() > kSqCarrotDist) { + break; + } + } + i1 = i0 - 1; + // printf("i0:%d i1:%d\n", i0, i1); + const Vector2f v0 = plan_path[i0].loc; + const Vector2f v1 = plan_path[i1].loc; + Vector2f r0, r1; + #define V2COMP(v) v.x(), v.y() + // printf("%f,%f %f,%f %f,%f %f\n", + // V2COMP(robot_loc_), V2COMP(v0), V2COMP(v1), (v0 - v1).norm()); + const int num_intersections = geometry::CircleLineIntersection( + robot_loc_, carrot_dist, v0, v1, &r0, &r1); + if (num_intersections == 0) { + fprintf(stderr, "Error obtaining intersections:\n v0: (%f %f), v1: (%f %f), robot_loc_: (%f %f) sq_carrot_dist: (%f) closest_dist: (%f)\n", + v0.x(), v0.y(), v1.x(), v1.y(), robot_loc_.x(), robot_loc_.y(), kSqCarrotDist, closest_dist); + return false; + } + + if (num_intersections == 1 || (r0 - v1).squaredNorm() < (r1 - v1).squaredNorm()) { + carrot = r0; + } else { + carrot = r1; + } + return true; +} + +void Navigation::GetStraightFreePathLength(float* free_path_length, + float* clearance) { + // How much the robot's body extends in front of its base link frame. + const float l = 0.5 * params_.robot_length - params_.base_link_offset + params_.obstacle_margin; + // The robot's half-width. + const float w = 0.5 * params_.robot_width + params_.obstacle_margin; + for (const Vector2f& p : fp_point_cloud_) { + if (fabs(p.y()) > w || p.x() < 0.0f) continue; + *free_path_length = min(*free_path_length, p.x() - l); + } + *clearance = params_.max_clearance; + for (const Vector2f& p : point_cloud_) { + if (p.x() - l > *free_path_length || p.x() < 0.0) continue; + *clearance = min(*clearance, fabs(fabs(p.y() - w))); + } + *clearance = max(0.0f, *clearance); + *free_path_length = max(0.0f, *free_path_length); +} + +Vector2f GetFinalPoint(const PathOption& o) { + if (fabs(o.curvature) < 0.01) { + return Vector2f(o.free_path_length, 0); + } else { + const float r = 1.0f / o.curvature; + const float a = o.free_path_length / fabs(r); + return Vector2f(fabs(r) * sin(a), r * (1.0 - cos(a))); + } +} + +DEFINE_double(tx, 0.4, "Test obstacle point - X"); +DEFINE_double(ty, -0.38, "Test obstacle point - Y"); + +void Navigation::RunObstacleAvoidance(Vector2f& vel_cmd, float& ang_vel_cmd) { + static CumulativeFunctionTimer function_timer_(__FUNCTION__); + CumulativeFunctionTimer::Invocation invoke(&function_timer_); + const bool debug = FLAGS_v > 1; + + if(debug){ + printf("Running obstacle avoidance\n"); + } + + // Handling potential carrot overrides from social nav + Vector2f local_target = local_target_; + if (nav_state_ == NavigationState::kOverride) { + local_target = override_target_; + } + + sampler_->Update(robot_vel_, robot_omega_, local_target, fp_point_cloud_, latest_image_); + evaluator_->Update(robot_loc_, robot_angle_, robot_vel_, robot_omega_, local_target, fp_point_cloud_, latest_image_); + auto paths = sampler_->GetSamples(params_.num_options); + if (debug) { + printf("%lu options\n", paths.size()); + int i = 0; + for (auto p : paths) { + ConstantCurvatureArc arc = + *reinterpret_cast(p.get()); + printf("%3d: %7.5f %7.3f %7.3f\n", + i++, arc.curvature, arc.length, arc.curvature); + } + } + if (paths.size() == 0) { + // No options, just stop. + Halt(vel_cmd, ang_vel_cmd); + if (debug) printf("No paths found\n"); + return; + } + auto best_path = evaluator_->FindBest(paths); + if (best_path == nullptr) { + if (debug) printf("No best path found\n"); + // No valid path found! + // TODO: Change this to rotate instead of halt + TurnInPlace(vel_cmd, ang_vel_cmd); + // Halt(vel_cmd, ang_vel_cmd); + return; + } + ang_vel_cmd = 0; + vel_cmd = {0, 0}; + + float max_map_speed = params_.linear_limits.max_speed; + planning_domain_.GetClearanceAndSpeedFromLoc( + robot_loc_, nullptr, &max_map_speed); + auto linear_limits = params_.linear_limits; + linear_limits.max_speed = min(max_map_speed, params_.linear_limits.max_speed); + best_path->GetControls(linear_limits, + params_.angular_limits, + params_.dt, robot_vel_, + robot_omega_, + vel_cmd, + ang_vel_cmd); + last_options_ = paths; + best_option_ = best_path; +} + +void Navigation::Halt(Vector2f& cmd_vel, float& angular_vel_cmd) { + const float kEpsSpeed = 0.01; + const float velocity = robot_vel_.x(); + float velocity_cmd = 0; + if (fabs(velocity) > kEpsSpeed) { + const float dv = params_.linear_limits.max_deceleration * params_.dt; + if (velocity < -dv) { + velocity_cmd = velocity + dv; + } else if (velocity > dv) { + velocity_cmd = velocity - dv; + } else { + velocity_cmd = 0; + } + } + cmd_vel = {velocity_cmd, 0}; + //TODO: motion profiling for omega + angular_vel_cmd = 0; +} + +void Navigation::TurnInPlace(Vector2f& cmd_vel, float& cmd_angle_vel) { + static const bool kDebug = false; + const float kMaxLinearSpeed = 0.1; + const float velocity = robot_vel_.x(); + cmd_angle_vel = 0; + + + if (fabs(velocity) > kMaxLinearSpeed) { + Halt(cmd_vel, cmd_angle_vel); + return; + } + float dTheta = 0; + if (nav_state_ == NavigationState::kGoto) { + dTheta = atan2(local_target_.y(), local_target_.x()); + } else if (nav_state_ == NavigationState::kOverride) { + dTheta = atan2(override_target_.y(), override_target_.x()); + } else if (nav_state_ == NavigationState::kTurnInPlace) { + dTheta = AngleDiff(nav_goal_angle_, robot_angle_); + } + if (kDebug) printf("dTheta: %f robot_angle: %f\n", RadToDeg(dTheta), RadToDeg(robot_angle_)); + + + const float s = Sign(dTheta); + if (robot_omega_ * dTheta < 0.0f) { + if (kDebug) printf("Wrong way\n"); + const float dv = params_.dt * params_.angular_limits.max_acceleration; + // Turning the wrong way! + if (fabs(robot_omega_) < dv) { + cmd_angle_vel = 0; + } else { + cmd_angle_vel = robot_omega_ - Sign(robot_omega_) * dv; + } + } else { + cmd_angle_vel = s * Run1DTimeOptimalControl( + params_.angular_limits, + 0, + s * robot_omega_, + s * dTheta, + 0, + params_.dt); + cout << "turn in place test5: angle_vel = " << cmd_angle_vel << endl; + } + cmd_vel = {0, 0}; +} + +void Navigation::Pause() { + nav_state_ = NavigationState::kPaused; +} + +void Navigation::SetMaxVel(const float vel) { + params_.linear_limits.max_speed = vel; +} + +void Navigation::SetMaxAccel(const float accel) { + params_.linear_limits.max_acceleration = accel; + return; +} + +void Navigation::SetMaxDecel(const float decel) { + params_.linear_limits.max_deceleration = decel; + return; +} + +void Navigation::SetAngAccel(const float accel) { + params_.angular_limits.max_acceleration = accel; + return; +} + +void Navigation::SetAngVel(const float vel) { + params_.angular_limits.max_speed = vel; + return; +} + +void Navigation::SetObstacleMargin(const float margin) { + params_.obstacle_margin = margin; + return; +} + +void Navigation::SetClearanceWeight(const float weight) { + LinearEvaluator* evaluator = + dynamic_cast(evaluator_.get()); + evaluator->SetClearanceWeight(weight); + return; +} + +void Navigation::SetCarrotDist(const float carrot_dist) { + params_.carrot_dist = carrot_dist; + return; +} + +Eigen::Vector2f Navigation::GetTarget() { + return local_target_; +} + +Eigen::Vector2f Navigation::GetOverrideTarget() { + return override_target_; +} + +Eigen::Vector2f Navigation::GetVelocity() { + return robot_vel_; +} + +float Navigation::GetAngularVelocity() { + return robot_omega_; +} + +string Navigation::GetNavStatus() { + switch (nav_state_) { + case NavigationState::kStopped: { + return "Stopped"; + } break; + case NavigationState::kPaused: { + return "Paused"; + } break; + case NavigationState::kGoto: { + return "Goto"; + } break; + case NavigationState::kOverride: { + return "Override"; + } break; + case NavigationState::kTurnInPlace: { + return "TurnInPlace"; + } break; + default: { + return "Unknown"; + } break; + } +} + +uint8_t Navigation::GetNavStatusUint8() { + return static_cast(nav_state_); +} + +vector Navigation::GetPredictedCloud() { + return fp_point_cloud_; +} + +float Navigation::GetCarrotDist() { + return params_.intermediate_carrot_dist; +} + +float Navigation::GetObstacleMargin() { + return params_.obstacle_margin; +} + +float Navigation::GetRobotWidth() { + return params_.robot_width; +} + +float Navigation::GetRobotLength() { + return params_.robot_length; +} + +vector> Navigation::GetLastPathOptions() { + return last_options_; +} + +const cv::Mat& Navigation::GetVisualizationImage() { + if (params_.evaluator_type == "cost_map") { + return dynamic_cast(evaluator_.get())->latest_vis_image_; + } else { + std::cerr << "No visualization image for linear evaluator" << std::endl; + exit(1); + } +} + +std::shared_ptr Navigation::GetOption() { + return best_option_; +} + +vector Navigation::GetPlanPath() { + return plan_path_; +} + +vector Navigation::GetGlobalPath() { + return global_plan_path_; +} + +vector Navigation::GetCostmapObstacles(){ + return costmap_obstacles_; +} + +vector Navigation::GetGlobalCostmapObstacles(){ + return global_costmap_obstacles_; +} + +Eigen::Vector2f Navigation::GetIntermediateGoal(){ + return intermediate_goal_; +} + + +bool Navigation::Run(const double& time, + Vector2f& cmd_vel, + float& cmd_angle_vel) { + const bool kDebug = FLAGS_v > 0; + if (!initialized_) { + if (kDebug) printf("Parameters and maps not initialized\n"); + return false; + } + if (!odom_initialized_) { + if (kDebug) printf("Odometry not initialized\n"); + return false; + } + + ForwardPredict(time + params_.system_latency); + if (FLAGS_test_toc) { + TrapezoidTest(cmd_vel, cmd_angle_vel); + return true; + } else if (FLAGS_test_obstacle) { + ObstacleTest(cmd_vel, cmd_angle_vel); + return true; + } else if (FLAGS_test_avoidance) { + ObstAvTest(cmd_vel, cmd_angle_vel); + return true; + } else if (FLAGS_test_planner) { + PlannerTest(); + return true; + } else if (FLAGS_test_latency) { + LatencyTest(cmd_vel, cmd_angle_vel); + return true; + } + + int cell_inflation_size = std::ceil(params_.local_costmap_inflation_size/costmap_.getResolution()); + + int x_max = costmap_.getSizeInCellsX(); + int y_max = costmap_.getSizeInCellsY(); + + // Reset map to empty from previous iteration + costmap_.resetMap(0, 0, x_max, y_max); + costmap_obstacles_.clear(); + + // True if distance is less than replan inflation size + // Assign different value for points within inflation size but farther than replan size + unordered_map inflation_cells; + unordered_set obstacle_cells; + + // Map from costmap index to real coordinates relative to robot + unordered_map index_to_obstacle; + + uint32_t robot_mx, robot_my; + costmap_.worldToMap(0, 0, robot_mx, robot_my); + uint32_t robot_index = costmap_.getIndex(robot_mx, robot_my); + uint32_t robot_row, robot_col; + costmap_.indexToCells(robot_index, robot_row, robot_col); + double robot_wx, robot_wy; + costmap_.mapToWorld(robot_row, robot_col, robot_wx, robot_wy); + Vector2f robot_location(robot_wx, robot_wy); + + + // Add new points to costmap + for (size_t i = 0; i < point_cloud_.size(); i++){ + uint32_t unsigned_mx, unsigned_my; + Vector2f relative_location_map_frame = Rotation2Df(robot_angle_) * point_cloud_[i]; + bool in_map = costmap_.worldToMap(relative_location_map_frame.x(), relative_location_map_frame.y(), unsigned_mx, unsigned_my); + + //TODO: change max distance based on lidar to base link transformation + if (in_map && relative_location_map_frame.norm() < (params_.range_max - params_.robot_length) && relative_location_map_frame.norm() > params_.range_min){ + uint32_t index = costmap_.getIndex(unsigned_mx, unsigned_my); + double wx, wy; + costmap_.mapToWorld(unsigned_mx, unsigned_my, wx, wy); + obstacle_cells.insert(index); + + SeenObstacle obs; + obs.location = Vector2f(wx - robot_location.x(), wy - robot_location.y()); + obs.last_seen = std::time(nullptr); + index_to_obstacle[index] = obs; + } + } + + // Point sorting function + struct PointComparison { + const costmap_2d::Costmap2D costmap; + const int robot_mx; + const int robot_my; + + + PointComparison(const costmap_2d::Costmap2D map, const int robot_x, const int robot_y) : costmap(map), robot_mx(robot_x), robot_my(robot_y) {} + + bool operator()(int a, int b) const { + uint32_t row_a, col_a; + costmap.indexToCells(a, row_a, col_a); + uint32_t row_b, col_b; + costmap.indexToCells(b, row_b, col_b); + + double angle_a = atan2(row_a - robot_mx, col_a); + double angle_b = atan2(row_b - robot_my, col_b); + + return angle_a < angle_b; + } + }; + PointComparison pointComparison(costmap_, robot_mx, robot_my); + + // Define set of points to exclude from new costmap + set sorted_obstacle_cells(pointComparison); + unordered_set empty_cells; + + for (const auto& index : obstacle_cells) { + sorted_obstacle_cells.insert(index); + } + + auto iter = sorted_obstacle_cells.begin(); + + while (iter != sorted_obstacle_cells.end() && iter != std::prev(sorted_obstacle_cells.end())) { + costmap_2d::MapLocation point_a; + costmap_.indexToCells(*iter, point_a.x, point_a.y); + costmap_2d::MapLocation point_b; + costmap_.indexToCells(*std::next(iter), point_b.x, point_b.y); + costmap_2d::MapLocation robot_point; + robot_point.x = robot_mx; + robot_point.y = robot_my; + robot_point = robot_point; + vector polygon = {point_a, point_b, robot_point}; + vector fill_cells; + costmap_.convexFillCells(polygon, fill_cells); + + for (size_t i = 0; i < fill_cells.size(); i++){ + empty_cells.insert(costmap_.getIndex(fill_cells[i].x, fill_cells[i].y)); + } + + iter++; + } + + // Add old obstacles that are still in map to new costmap + for (const auto& obs : prev_obstacles_) { + Vector2f point = obs.location; + uint32_t unsigned_mx, unsigned_my; + Vector2f new_relative_point = point + prev_robot_loc_ - robot_loc_; + bool in_map = costmap_.worldToMap(new_relative_point.x(), new_relative_point.y(), unsigned_mx, unsigned_my); + uint32_t index = costmap_.getIndex(unsigned_mx, unsigned_my); + if (in_map && empty_cells.count(index) == 0 && std::time(nullptr) - obs.last_seen < params_.object_lifespan){ + obstacle_cells.insert(index); + if(index_to_obstacle.count(index) == 0){ + SeenObstacle new_obs; + new_obs.location = new_relative_point; + new_obs.last_seen = obs.last_seen; + index_to_obstacle[index] = new_obs; + } + else{ + index_to_obstacle[index].location = new_relative_point; + } + } + } + + for (const auto& index : obstacle_cells) { + uint32_t unsigned_mx, unsigned_my; + costmap_.indexToCells(index, unsigned_mx, unsigned_my); + int mx = static_cast(unsigned_mx); + int my = static_cast(unsigned_my); + for (int j = -cell_inflation_size; j <= cell_inflation_size; j++){ + for (int k = -cell_inflation_size; k <= cell_inflation_size; k++){ + float cell_dist = sqrt(pow(j, 2) + pow(k, 2)); + float dist = cell_dist * costmap_.getResolution(); + if((cell_dist <= cell_inflation_size) && (mx + j >= 0) && (mx + j < x_max) && (my + k >= 0) && (my + k < y_max)){ + unsigned char cost; + if(j == 0 && k == 0){ + cost = costmap_2d::LETHAL_OBSTACLE; + } + else if(dist <= params_.replan_inflation_size){ + cost = costmap_2d::INSCRIBED_INFLATED_OBSTACLE; + } + else{ + cost = std::ceil(std::exp(-1 * params_.inflation_coeff * (dist - params_.replan_inflation_size)) * (costmap_2d::INSCRIBED_INFLATED_OBSTACLE-1)); + } + costmap_.setCost(mx + j, my + k, std::max(cost, costmap_.getCost(mx + j, my + k))); + inflation_cells[costmap_.getIndex(mx + j, my + k)] = std::max(cost, costmap_.getCost(mx + j, my + k)); + } + } + } + } + + for (const auto& pair : inflation_cells) { + uint32_t index = pair.first; + uint32_t mx = 0; + uint32_t my = 0; + costmap_.indexToCells(index, mx, my); + + double wx, wy; + costmap_.mapToWorld(mx, my, wx, wy); + costmap_obstacles_.push_back(ObstacleCost{Vector2f(wx, wy) + robot_loc_, pair.second}); + } + + // Record last seen locations of points + prev_robot_loc_ = robot_loc_; + prev_obstacles_.clear(); + + for (const auto& pair : index_to_obstacle) { + SeenObstacle obs = pair.second; + prev_obstacles_.push_back(obs); + } + + + // Before switching states we need to update the local target. + + if (nav_state_ == NavigationState::kGoto || + nav_state_ == NavigationState::kOverride) { + // Recompute global plan as necessary. + if (!PlanStillValid() || !IntermediatePlanStillValid()) { + if (kDebug) printf("Replanning\n"); + plan_path_ = Plan(robot_loc_, nav_goal_loc_); + } + if (nav_state_ == NavigationState::kGoto) { + // Get Carrot and check if done + Vector2f carrot(0, 0); + bool foundCarrot = GetIntermediateCarrot(carrot); + if (!foundCarrot) { + Halt(cmd_vel, cmd_angle_vel); + return false; + } + // Local Navigation + cout << "set local target" << endl; + local_target_ = Rotation2Df(-robot_angle_) * (carrot - robot_loc_); + } + } + + // Switch between navigation states. + NavigationState prev_state = nav_state_; + do { + prev_state = nav_state_; + if (nav_state_ == NavigationState::kGoto && + local_target_.squaredNorm() < Sq(params_.target_dist_tolerance) && + robot_vel_.squaredNorm() < Sq(params_.target_vel_tolerance)) { + cout << "set turn in place" << endl; + nav_state_ = NavigationState::kTurnInPlace; + } else if (nav_state_ == NavigationState::kTurnInPlace && + AngleDist(robot_angle_, nav_goal_angle_) < + params_.target_angle_tolerance) { + nav_state_ = NavigationState::kStopped; + } + } while (prev_state != nav_state_); + + cout << "before switch" << endl; + + + switch (nav_state_) { + case NavigationState::kStopped: { + if (kDebug) printf("\nNav complete\n"); + } break; + case NavigationState::kPaused: { + if (kDebug) printf("\nNav paused\n"); + } break; + case NavigationState::kGoto: { + if (kDebug) printf("\nNav Goto\n"); + } break; + case NavigationState::kTurnInPlace: { + if (kDebug) printf("\nNav TurnInPlace\n"); + } break; + case NavigationState::kOverride: { + if (kDebug) printf("\nNav override\n"); + } break; + default: { + fprintf(stderr, "ERROR: Unknown nav state %d\n", + static_cast(nav_state_)); + } + } + + if (nav_state_ == NavigationState::kPaused || + nav_state_ == NavigationState::kStopped) { + Halt(cmd_vel, cmd_angle_vel); + return true; + } else if (nav_state_ == NavigationState::kGoto || + nav_state_ == NavigationState::kOverride) { + Vector2f local_target(0, 0); + if (nav_state_ == NavigationState::kGoto) { + // Local Navigation + local_target = local_target_; + } else { + // Running NavigationState::kOverride . + local_target = override_target_; + } + const float theta = atan2(local_target.y(), local_target.x()); + if (local_target.squaredNorm() > params_.intermediate_carrot_dist) { + local_target = params_.intermediate_carrot_dist * local_target.normalized(); + } + if (!FLAGS_no_local) { + if (fabs(theta) > params_.local_fov) { + if (kDebug) printf("TurnInPlace\n"); + TurnInPlace(cmd_vel, cmd_angle_vel); + } else { + if (kDebug) printf("ObstAv\n"); + cout << "running obstacle avoidance original" << endl; + RunObstacleAvoidance(cmd_vel, cmd_angle_vel); + } + } + } else if (nav_state_ == NavigationState::kTurnInPlace) { + if (kDebug) printf("Reached Goal: TurnInPlace\n"); + TurnInPlace(cmd_vel, cmd_angle_vel); + } + + // viz_pub_.publish(local_viz_msg_); + // viz_pub_.publish(global_viz_msg_); + + return true; +} + +} // namespace navigation