add humble-navigation2
This commit is contained in:
@@ -0,0 +1,419 @@
|
||||
// Copyright (c) 2022 Samsung R&D Institute Russia
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "nav2_collision_monitor/polygon.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <utility>
|
||||
|
||||
#include "geometry_msgs/msg/point.hpp"
|
||||
#include "geometry_msgs/msg/point32.hpp"
|
||||
|
||||
#include "nav2_util/node_utils.hpp"
|
||||
|
||||
#include "nav2_collision_monitor/kinematics.hpp"
|
||||
|
||||
namespace nav2_collision_monitor
|
||||
{
|
||||
|
||||
Polygon::Polygon(
|
||||
const nav2_util::LifecycleNode::WeakPtr & node,
|
||||
const std::string & polygon_name,
|
||||
const std::shared_ptr<tf2_ros::Buffer> tf_buffer,
|
||||
const std::string & base_frame_id,
|
||||
const tf2::Duration & transform_tolerance)
|
||||
: node_(node), polygon_name_(polygon_name), action_type_(DO_NOTHING),
|
||||
slowdown_ratio_(0.0), footprint_sub_(nullptr), tf_buffer_(tf_buffer),
|
||||
base_frame_id_(base_frame_id), transform_tolerance_(transform_tolerance)
|
||||
{
|
||||
RCLCPP_INFO(logger_, "[%s]: Creating Polygon", polygon_name_.c_str());
|
||||
}
|
||||
|
||||
Polygon::~Polygon()
|
||||
{
|
||||
RCLCPP_INFO(logger_, "[%s]: Destroying Polygon", polygon_name_.c_str());
|
||||
poly_.clear();
|
||||
dyn_params_handler_.reset();
|
||||
}
|
||||
|
||||
bool Polygon::configure()
|
||||
{
|
||||
auto node = node_.lock();
|
||||
if (!node) {
|
||||
throw std::runtime_error{"Failed to lock node"};
|
||||
}
|
||||
|
||||
std::string polygon_pub_topic, footprint_topic;
|
||||
|
||||
if (!getParameters(polygon_pub_topic, footprint_topic)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!footprint_topic.empty()) {
|
||||
footprint_sub_ = std::make_unique<nav2_costmap_2d::FootprintSubscriber>(
|
||||
node, footprint_topic, *tf_buffer_,
|
||||
base_frame_id_, tf2::durationToSec(transform_tolerance_));
|
||||
}
|
||||
|
||||
if (visualize_) {
|
||||
// Fill polygon_ points for future usage
|
||||
std::vector<Point> poly;
|
||||
getPolygon(poly);
|
||||
for (const Point & p : poly) {
|
||||
geometry_msgs::msg::Point32 p_s;
|
||||
p_s.x = p.x;
|
||||
p_s.y = p.y;
|
||||
// p_s.z will remain 0.0
|
||||
polygon_.points.push_back(p_s);
|
||||
}
|
||||
|
||||
rclcpp::QoS polygon_qos = rclcpp::SystemDefaultsQoS(); // set to default
|
||||
polygon_pub_ = node->create_publisher<geometry_msgs::msg::PolygonStamped>(
|
||||
polygon_pub_topic, polygon_qos);
|
||||
}
|
||||
|
||||
// Add callback for dynamic parameters
|
||||
dyn_params_handler_ = node->add_on_set_parameters_callback(
|
||||
std::bind(&Polygon::dynamicParametersCallback, this, std::placeholders::_1));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Polygon::activate()
|
||||
{
|
||||
if (visualize_) {
|
||||
polygon_pub_->on_activate();
|
||||
}
|
||||
}
|
||||
|
||||
void Polygon::deactivate()
|
||||
{
|
||||
if (visualize_) {
|
||||
polygon_pub_->on_deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
std::string Polygon::getName() const
|
||||
{
|
||||
return polygon_name_;
|
||||
}
|
||||
|
||||
ActionType Polygon::getActionType() const
|
||||
{
|
||||
return action_type_;
|
||||
}
|
||||
|
||||
bool Polygon::getEnabled() const
|
||||
{
|
||||
return enabled_;
|
||||
}
|
||||
|
||||
int Polygon::getMaxPoints() const
|
||||
{
|
||||
return max_points_;
|
||||
}
|
||||
|
||||
double Polygon::getSlowdownRatio() const
|
||||
{
|
||||
return slowdown_ratio_;
|
||||
}
|
||||
|
||||
double Polygon::getTimeBeforeCollision() const
|
||||
{
|
||||
return time_before_collision_;
|
||||
}
|
||||
|
||||
void Polygon::getPolygon(std::vector<Point> & poly) const
|
||||
{
|
||||
poly = poly_;
|
||||
}
|
||||
|
||||
void Polygon::updatePolygon()
|
||||
{
|
||||
if (footprint_sub_ != nullptr) {
|
||||
// Get latest robot footprint from footprint subscriber
|
||||
std::vector<geometry_msgs::msg::Point> footprint_vec;
|
||||
std_msgs::msg::Header footprint_header;
|
||||
footprint_sub_->getFootprintInRobotFrame(footprint_vec, footprint_header);
|
||||
|
||||
std::size_t new_size = footprint_vec.size();
|
||||
poly_.resize(new_size);
|
||||
polygon_.points.resize(new_size);
|
||||
|
||||
geometry_msgs::msg::Point32 p_s;
|
||||
for (std::size_t i = 0; i < new_size; i++) {
|
||||
poly_[i] = {footprint_vec[i].x, footprint_vec[i].y};
|
||||
p_s.x = footprint_vec[i].x;
|
||||
p_s.y = footprint_vec[i].y;
|
||||
polygon_.points[i] = p_s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Polygon::getPointsInside(const std::vector<Point> & points) const
|
||||
{
|
||||
int num = 0;
|
||||
for (const Point & point : points) {
|
||||
if (isPointInside(point)) {
|
||||
num++;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
double Polygon::getCollisionTime(
|
||||
const std::vector<Point> & collision_points,
|
||||
const Velocity & velocity) const
|
||||
{
|
||||
// Initial robot pose is {0,0} in base_footprint coordinates
|
||||
Pose pose = {0.0, 0.0, 0.0};
|
||||
Velocity vel = velocity;
|
||||
|
||||
// Array of points transformed to the frame concerned with pose on each simulation step
|
||||
std::vector<Point> points_transformed = collision_points;
|
||||
|
||||
// Check static polygon
|
||||
if (getPointsInside(points_transformed) >= max_points_) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Robot movement simulation
|
||||
for (double time = 0.0; time <= time_before_collision_; time += simulation_time_step_) {
|
||||
// Shift the robot pose towards to the vel during simulation_time_step_ time interval
|
||||
// NOTE: vel is changing during the simulation
|
||||
projectState(simulation_time_step_, pose, vel);
|
||||
// Transform collision_points to the frame concerned with current robot pose
|
||||
points_transformed = collision_points;
|
||||
transformPoints(pose, points_transformed);
|
||||
// If the collision occurred on this stage, return the actual time before a collision
|
||||
// as if robot was moved with given velocity
|
||||
if (getPointsInside(points_transformed) > max_points_) {
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
// There is no collision
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
void Polygon::publish() const
|
||||
{
|
||||
if (!visualize_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto node = node_.lock();
|
||||
if (!node) {
|
||||
throw std::runtime_error{"Failed to lock node"};
|
||||
}
|
||||
|
||||
// Fill PolygonStamped struct
|
||||
std::unique_ptr<geometry_msgs::msg::PolygonStamped> poly_s =
|
||||
std::make_unique<geometry_msgs::msg::PolygonStamped>();
|
||||
poly_s->header.stamp = node->now();
|
||||
poly_s->header.frame_id = base_frame_id_;
|
||||
poly_s->polygon = polygon_;
|
||||
|
||||
// Publish polygon
|
||||
polygon_pub_->publish(std::move(poly_s));
|
||||
}
|
||||
|
||||
bool Polygon::getCommonParameters(std::string & polygon_pub_topic)
|
||||
{
|
||||
auto node = node_.lock();
|
||||
if (!node) {
|
||||
throw std::runtime_error{"Failed to lock node"};
|
||||
}
|
||||
|
||||
try {
|
||||
// Get action type.
|
||||
// Leave it not initialized: the will cause an error if it will not set.
|
||||
nav2_util::declare_parameter_if_not_declared(
|
||||
node, polygon_name_ + ".action_type", rclcpp::PARAMETER_STRING);
|
||||
const std::string at_str =
|
||||
node->get_parameter(polygon_name_ + ".action_type").as_string();
|
||||
if (at_str == "stop") {
|
||||
action_type_ = STOP;
|
||||
} else if (at_str == "slowdown") {
|
||||
action_type_ = SLOWDOWN;
|
||||
} else if (at_str == "approach") {
|
||||
action_type_ = APPROACH;
|
||||
} else { // Error if something else
|
||||
RCLCPP_ERROR(logger_, "[%s]: Unknown action type: %s", polygon_name_.c_str(), at_str.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
nav2_util::declare_parameter_if_not_declared(
|
||||
node, polygon_name_ + ".enabled", rclcpp::ParameterValue(true));
|
||||
enabled_ = node->get_parameter(polygon_name_ + ".enabled").as_bool();
|
||||
|
||||
nav2_util::declare_parameter_if_not_declared(
|
||||
node, polygon_name_ + ".max_points", rclcpp::ParameterValue(3));
|
||||
max_points_ = node->get_parameter(polygon_name_ + ".max_points").as_int();
|
||||
|
||||
if (action_type_ == SLOWDOWN) {
|
||||
nav2_util::declare_parameter_if_not_declared(
|
||||
node, polygon_name_ + ".slowdown_ratio", rclcpp::ParameterValue(0.5));
|
||||
slowdown_ratio_ = node->get_parameter(polygon_name_ + ".slowdown_ratio").as_double();
|
||||
}
|
||||
|
||||
if (action_type_ == APPROACH) {
|
||||
nav2_util::declare_parameter_if_not_declared(
|
||||
node, polygon_name_ + ".time_before_collision", rclcpp::ParameterValue(2.0));
|
||||
time_before_collision_ =
|
||||
node->get_parameter(polygon_name_ + ".time_before_collision").as_double();
|
||||
nav2_util::declare_parameter_if_not_declared(
|
||||
node, polygon_name_ + ".simulation_time_step", rclcpp::ParameterValue(0.1));
|
||||
simulation_time_step_ =
|
||||
node->get_parameter(polygon_name_ + ".simulation_time_step").as_double();
|
||||
}
|
||||
|
||||
nav2_util::declare_parameter_if_not_declared(
|
||||
node, polygon_name_ + ".visualize", rclcpp::ParameterValue(false));
|
||||
visualize_ = node->get_parameter(polygon_name_ + ".visualize").as_bool();
|
||||
if (visualize_) {
|
||||
// Get polygon topic parameter in case if it is going to be published
|
||||
nav2_util::declare_parameter_if_not_declared(
|
||||
node, polygon_name_ + ".polygon_pub_topic", rclcpp::ParameterValue(polygon_name_));
|
||||
polygon_pub_topic = node->get_parameter(polygon_name_ + ".polygon_pub_topic").as_string();
|
||||
}
|
||||
} catch (const std::exception & ex) {
|
||||
RCLCPP_ERROR(
|
||||
logger_,
|
||||
"[%s]: Error while getting common polygon parameters: %s",
|
||||
polygon_name_.c_str(), ex.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polygon::getParameters(std::string & polygon_pub_topic, std::string & footprint_topic)
|
||||
{
|
||||
auto node = node_.lock();
|
||||
if (!node) {
|
||||
throw std::runtime_error{"Failed to lock node"};
|
||||
}
|
||||
|
||||
if (!getCommonParameters(polygon_pub_topic)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (action_type_ == APPROACH) {
|
||||
// Obtain the footprint topic to make a footprint subscription for approach polygon
|
||||
nav2_util::declare_parameter_if_not_declared(
|
||||
node, polygon_name_ + ".footprint_topic",
|
||||
rclcpp::ParameterValue("local_costmap/published_footprint"));
|
||||
footprint_topic =
|
||||
node->get_parameter(polygon_name_ + ".footprint_topic").as_string();
|
||||
|
||||
// This is robot footprint: do not need to get polygon points from ROS parameters.
|
||||
// It will be set dynamically later.
|
||||
return true;
|
||||
} else {
|
||||
// Make it empty otherwise
|
||||
footprint_topic.clear();
|
||||
}
|
||||
|
||||
// Leave it not initialized: the will cause an error if it will not set
|
||||
nav2_util::declare_parameter_if_not_declared(
|
||||
node, polygon_name_ + ".points", rclcpp::PARAMETER_DOUBLE_ARRAY);
|
||||
std::vector<double> poly_row =
|
||||
node->get_parameter(polygon_name_ + ".points").as_double_array();
|
||||
// Check for points format correctness
|
||||
if (poly_row.size() <= 6 || poly_row.size() % 2 != 0) {
|
||||
RCLCPP_ERROR(
|
||||
logger_,
|
||||
"[%s]: Polygon has incorrect points description",
|
||||
polygon_name_.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Obtain polygon vertices
|
||||
Point point;
|
||||
bool first = true;
|
||||
for (double val : poly_row) {
|
||||
if (first) {
|
||||
point.x = val;
|
||||
} else {
|
||||
point.y = val;
|
||||
poly_.push_back(point);
|
||||
}
|
||||
first = !first;
|
||||
}
|
||||
} catch (const std::exception & ex) {
|
||||
RCLCPP_ERROR(
|
||||
logger_,
|
||||
"[%s]: Error while getting polygon parameters: %s",
|
||||
polygon_name_.c_str(), ex.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
Polygon::dynamicParametersCallback(
|
||||
std::vector<rclcpp::Parameter> parameters)
|
||||
{
|
||||
rcl_interfaces::msg::SetParametersResult result;
|
||||
|
||||
for (auto parameter : parameters) {
|
||||
const auto & param_type = parameter.get_type();
|
||||
const auto & param_name = parameter.get_name();
|
||||
|
||||
if (param_type == rcl_interfaces::msg::ParameterType::PARAMETER_BOOL) {
|
||||
if (param_name == polygon_name_ + "." + "enabled") {
|
||||
enabled_ = parameter.as_bool();
|
||||
}
|
||||
}
|
||||
}
|
||||
result.successful = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool Polygon::isPointInside(const Point & point) const
|
||||
{
|
||||
// Adaptation of Shimrat, Moshe. "Algorithm 112: position of point relative to polygon."
|
||||
// Communications of the ACM 5.8 (1962): 434.
|
||||
// Implementation of ray crossings algorithm for point in polygon task solving.
|
||||
// Y coordinate is fixed. Moving the ray on X+ axis starting from given point.
|
||||
// Odd number of intersections with polygon boundaries means the point is inside polygon.
|
||||
const int poly_size = poly_.size();
|
||||
int i, j; // Polygon vertex iterators
|
||||
bool res = false; // Final result, initialized with already inverted value
|
||||
|
||||
// Starting from the edge where the last point of polygon is connected to the first
|
||||
i = poly_size - 1;
|
||||
for (j = 0; j < poly_size; j++) {
|
||||
// Checking the edge only if given point is between edge boundaries by Y coordinates.
|
||||
// One of the condition should contain equality in order to exclude the edges
|
||||
// parallel to X+ ray.
|
||||
if ((point.y <= poly_[i].y) == (point.y > poly_[j].y)) {
|
||||
// Calculating the intersection coordinate of X+ ray
|
||||
const double x_inter = poly_[i].x +
|
||||
(point.y - poly_[i].y) * (poly_[j].x - poly_[i].x) /
|
||||
(poly_[j].y - poly_[i].y);
|
||||
// If intersection with checked edge is greater than point.x coordinate, inverting the result
|
||||
if (x_inter > point.x) {
|
||||
res = !res;
|
||||
}
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace nav2_collision_monitor
|
||||
Reference in New Issue
Block a user