diff --git a/CMakeLists.txt b/CMakeLists.txt index 5809ca1cbc6c2fdc2306a083207c929873ffdf53..de6da3453c013c55a7a375d0dcb14d94ac9575a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ find_package(catkin REQUIRED) # ******************************************************************** # Add catkin additional components here # ******************************************************************** -find_package(catkin REQUIRED COMPONENTS geometry_msgs sensor_msgs iri_base_algorithm tf2_ros laser_geometry iri_ros_tools) +find_package(catkin REQUIRED COMPONENTS geometry_msgs sensor_msgs iri_base_algorithm tf2_ros laser_geometry iri_ros_tools costmap_2d) ## System dependencies are found with CMake's conventions # find_package(Boost REQUIRED COMPONENTS system) @@ -15,7 +15,7 @@ find_package(catkin REQUIRED COMPONENTS geometry_msgs sensor_msgs iri_base_algor # Add system and labrobotica dependencies here # ******************************************************************** find_package(iriutils REQUIRED) -find_package(Eigen3 REQUIRED) +find_package(PCL REQUIRED) # ******************************************************************** # Add topic, service and action definition here @@ -61,7 +61,7 @@ catkin_package( # ******************************************************************** # Add ROS and IRI ROS run time dependencies # ******************************************************************** - CATKIN_DEPENDS geometry_msgs sensor_msgs iri_base_algorithm tf2_ros laser_geometry iri_ros_tools + CATKIN_DEPENDS geometry_msgs sensor_msgs iri_base_algorithm tf2_ros laser_geometry iri_ros_tools costmap_2d # ******************************************************************** # Add system and labrobotica run time dependencies here # ******************************************************************** @@ -78,7 +78,10 @@ catkin_package( include_directories(include) include_directories(${catkin_INCLUDE_DIRS}) include_directories(${iriutils_INCLUDE_DIR}) -include_directories(${EIGEN3_INCLUDE_DIR}) +include_directories(${PCL_INCLUDE_DIRS}) + +link_directories(${PCL_LIBRARY_DIRS}) +add_definitions(${PCL_DEFINITIONS}) ## Declare a cpp library # add_library(${PROJECT_NAME} <list of source files>) @@ -91,6 +94,7 @@ add_executable(${PROJECT_NAME} src/safe_cmd_alg.cpp src/safe_cmd_alg_node.cpp) # ******************************************************************** target_link_libraries(${PROJECT_NAME} ${catkin_LIBRARIES}) target_link_libraries(${PROJECT_NAME} ${iriutils_LIBRARY}) +target_link_libraries(${PROJECT_NAME} ${PCL_LIBRARIES}) # ******************************************************************** # Add message headers dependencies diff --git a/README.md b/README.md index ed6fbc43a60acd5df26e5c6286d8dc5e93f73efd..98eee9bfe5f81e53b1abf2a32b44a94ff44d8394 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,10 @@ The iri_safe_cmd project description # ROS Interface +### Topic publishers + - ~**collision_points** (sensor_msgs/PointCloud2.msg) + - ~**collision_points** (sensor_msgs/PointCloud2.msg) + - ~**collision_points** (sensor_msgs/PointCloud2.msg) ### Topic subscribers - ~**footprint** (geometry_msgs/PolygonStamped.msg) - ~**footprint** (geometry_msgs/PolygonStamped.msg) diff --git a/cfg/SafeCmd.cfg b/cfg/SafeCmd.cfg index 12fe3a3b4c3f7a6bb10656e43d608e3205089bdc..0b16418a6a33df1eafd78e6432d5d706509aa6f2 100755 --- a/cfg/SafeCmd.cfg +++ b/cfg/SafeCmd.cfg @@ -40,8 +40,9 @@ ackermann = gen.add_group("ackermann") # Name Type Reconfiguration level Description Default Min Max gen.add("safety_distance", double_t, 0, "Safety distance to possible obstacles",0.5, 0.0, 2.0) +gen.add("traj_sim_distance", double_t, 0, "Distance for the trajectory computation", 1.0, 0.1, 5.0) gen.add("traj_sim_time", double_t, 0, "Time for the trajectory computation", 1.0, 0.1, 5.0) -gen.add("traj_delta_time", double_t, 0, "Time increment for the trajectory computation", 0.1, 0.01, 0.5) +gen.add("traj_sim_step", double_t, 0, "Distance/Time increment for the trajectory computation", 0.1, 0.01, 0.5) gen.add("max_linear_deacc", double_t, 0, "Maximum linear deacceleration", 1.0, 0.0, 2.0) gen.add("use_front_laser", bool_t, 0, "Whether to use the front laser or not",True) gen.add("front_laser_watchdog_time", double_t, 0, "Maximum time (seconds) between front_laser msgs",1.0, 0.1, 2.0) diff --git a/include/safe_cmd_alg.h b/include/safe_cmd_alg.h index 7bf787e4087071f3601fc2ccbdc8458f6ce33d03..3961baed94e6c54676aeb6c24b29beedfcfeace4 100644 --- a/include/safe_cmd_alg.h +++ b/include/safe_cmd_alg.h @@ -120,11 +120,12 @@ class SafeCmdAlgorithm // here define all safe_cmd_alg interface methods to retrieve and set // the driver parameters - void computer_ackermann_steer_angle_trajectory(double linear_x,double steer_angle,std::vector<TTrajPoint> &traj); - void computer_ackermann_angular_speed_trajectory(double linear_x,double angular_z,std::vector<TTrajPoint> &traj); - void computer_differential_trajectory(double linear_x,double angular_z,std::vector<TTrajPoint> &traj); - void limit_velocities_ackermann(double current_linear_x,double &new_linear_x,double time); - void limit_velocities_differential(double current_linear_x,double current_angular_z,double &new_linear_x,double &new_angular_z,double time); + void compute_trajectory_ackermann_steer_angle(double linear_x,double steer_angle,std::vector<TTrajPoint> &traj); + void compute_trajectory_ackermann_angular_speed(double linear_x,double angular_z,std::vector<TTrajPoint> &traj); + void compute_trajectory_differential(double linear_x,double angular_z,std::vector<TTrajPoint> &traj); + void limit_velocities_ackermann_steer_angle(double current_linear_x,double current_angular_z,double &new_linear_x,double &new_angular_z,unsigned int index); + void limit_velocities_ackermann_angular_speed(double current_linear_x,double current_angular_z,double &new_linear_x,double &new_angular_z,unsigned int index); + void limit_velocities_differential(double current_linear_x,double current_angular_z,double &new_linear_x,double &new_angular_z,unsigned int index); /** * \brief Destructor * diff --git a/include/safe_cmd_alg_node.h b/include/safe_cmd_alg_node.h index 9ad8145bf5ea0549a614e9c0d38e345eaba95a18..4d05b32d057131bef0e051dc228fc1d3d2d347e3 100644 --- a/include/safe_cmd_alg_node.h +++ b/include/safe_cmd_alg_node.h @@ -27,10 +27,13 @@ #include <iri_base_algorithm/iri_base_algorithm.h> #include "safe_cmd_alg.h" +#include <pcl/pcl_base.h> +#include <pcl/point_types.h> #include <iri_ros_tools/watchdog.h> // [publisher subscriber headers] -#include <geometry_msgs/PolygonStamped.h> +#include <sensor_msgs/PointCloud2.h> +#include <geometry_msgs/Point.h> #include <geometry_msgs/Twist.h> #include <sensor_msgs/LaserScan.h> @@ -52,18 +55,14 @@ class SafeCmdAlgNode : public algorithm_base::IriBaseAlgorithm<SafeCmdAlgorithm> { private: // [publisher attributes] + ros::Publisher collision_points_publisher_; + sensor_msgs::PointCloud2 collision_points_PointCloud2_msg_; + ros::Publisher cmd_vel_safe_publisher_; geometry_msgs::Twist Twist_msg_; + geometry_msgs::Twist safe_Twist_msg_; // [subscriber attributes] - ros::Subscriber footprint_subscriber_; - void footprint_callback(const geometry_msgs::PolygonStamped::ConstPtr& msg); - pthread_mutex_t footprint_mutex_; - void footprint_mutex_enter(void); - void footprint_mutex_exit(void); - geometry_msgs::PolygonStamped footprint; - double robot_length; - ros::Subscriber cmd_vel_subscriber_; void cmd_vel_callback(const geometry_msgs::Twist::ConstPtr& msg); CMutex cmd_vel_mutex_; @@ -73,20 +72,21 @@ class SafeCmdAlgNode : public algorithm_base::IriBaseAlgorithm<SafeCmdAlgorithm> ros::Subscriber rear_laser_subscriber_; void rear_laser_callback(const sensor_msgs::LaserScan::ConstPtr& msg); CMutex rear_laser_mutex_; - sensor_msgs::LaserScan rear_laser_scan; - bool new_rear_laser_; + pcl::PointCloud<pcl::PointXYZ> points_rear; + bool new_rear_points_; CROSWatchdog rear_laser_watchdog; ros::Subscriber front_laser_subscriber_; void front_laser_callback(const sensor_msgs::LaserScan::ConstPtr& msg); CMutex front_laser_mutex_; - sensor_msgs::LaserScan front_laser_scan; - bool new_front_laser_; + pcl::PointCloud<pcl::PointXYZ> points_front; + bool new_front_points_; CROSWatchdog front_laser_watchdog; laser_geometry::LaserProjection laser_projector_; tf2_ros::Buffer tf2_buffer; tf2_ros::TransformListener tf2_listener; + std::vector<geometry_msgs::Point> footprint; // [service attributes] @@ -95,8 +95,12 @@ class SafeCmdAlgNode : public algorithm_base::IriBaseAlgorithm<SafeCmdAlgorithm> // [action server attributes] // [action client attributes] - void get_footprint_at(geometry_msgs::PolygonStamped &footprint_in,TTrajPoint &point,geometry_msgs::PolygonStamped &footprint_out); - bool check_collision(std::vector<geometry_msgs::Point32> &scan_points,geometry_msgs::PolygonStamped &footprint,std::vector<geometry_msgs::Point32> &collision_points); + void get_footprint_at(std::vector<geometry_msgs::Point> &footprint_in,TTrajPoint &point,std::vector<geometry_msgs::Point> &footprint_out); + bool check_collision(pcl::PointCloud<pcl::PointXYZ>::Ptr &scan_points,std::vector<geometry_msgs::Point> &footprint,pcl::PointCloud<pcl::PointXYZ> &collision_points); + bool on_segment(geometry_msgs::Point &p,geometry_msgs::Point &q,geometry_msgs::Point &r); + int orientation(geometry_msgs::Point &p,geometry_msgs::Point &q,geometry_msgs::Point &r); + bool intersect(geometry_msgs::Point &p1,geometry_msgs::Point &q1,geometry_msgs::Point &p2,geometry_msgs::Point &q2); + bool inside(std::vector<geometry_msgs::Point> &footprint,pcl::PointXYZ &point); /** * \brief config variable * diff --git a/launch/node.launch b/launch/node.launch index ca4a95e29c6d70c8f9e184e80e4e8791b3c125c5..2a8bb7aa2b88427086a445f6da64d04ef720a0c3 100644 --- a/launch/node.launch +++ b/launch/node.launch @@ -2,28 +2,27 @@ <!-- --> <launch> - <arg name="node_name" default="iri_safe_cmd" /> - <arg name="output" default="screen" /> - <arg name="launch_prefix" default="" /> - <arg name="config_file" default="$(find iri_safe_cmd)/param/sample.yaml" /> - <arg name="safe_cmd_vel_topic" default="safe_cmd_vel" /> - <arg name="cmd_vel_topic" default="cmd_vel" /> - <arg name="rear_laser_topic" default="rear_scan" /> - <arg name="front_laser_topic" default="front_scan" /> - <arg name="footprint_topic" default="footprint" /> + <arg name="node_name" default="iri_safe_cmd" /> + <arg name="output" default="screen" /> + <arg name="launch_prefix" default="" /> + <arg name="config_file" default="$(find iri_safe_cmd)/param/sample.yaml" /> + <arg name="safe_cmd_vel_topic" default="safe_cmd_vel" /> + <arg name="cmd_vel_topic" default="cmd_vel" /> + <arg name="rear_laser_topic" default="rear_scan" /> + <arg name="front_laser_topic" default="front_scan" /> + <arg name="collision_points_topic" default="collision_points" /> - <node pkg ="iri_safe_cmd" type="iri_safe_cmd" name="$(arg node_name)" output="$(arg output)" launch-prefix="$(arg launch_prefix)"> <rosparam file="$(arg config_file)" command="load" /> - <remap from="~cmd_vel_safe" to="$(arg safe_cmd_vel_topic)"/> - <remap from="~cmd_vel" to="$(arg cmd_vel_topic)"/> - <remap from="~rear_laser" to="$(arg rear_laser_topic)"/> - <remap from="~front_laser" to="$(arg front_laser_topic)"/> - <remap from="~footprint" to="$(arg footprint_topic)"/> + <remap from="~cmd_vel_safe" to="$(arg safe_cmd_vel_topic)"/> + <remap from="~cmd_vel" to="$(arg cmd_vel_topic)"/> + <remap from="~rear_laser" to="$(arg rear_laser_topic)"/> + <remap from="~front_laser" to="$(arg front_laser_topic)"/> + <remap from="~collision_points" to="$(arg collision_points_topic)"/> </node> </launch> diff --git a/package.xml b/package.xml index a9698e6b912c1df28a530ee2517f3a463cf369cc..a8ec67bff01d2ae2558ab0ee7b95f57e6c85b552 100644 --- a/package.xml +++ b/package.xml @@ -58,6 +58,7 @@ <depend>tf2_ros</depend> <depend>laser_geometry</depend> <depend>iri_ros_tools</depend> + <depend>costmap_2d</depend> <!-- The export tag contains other, unspecified, tags --> <export> diff --git a/param/sample.yaml b/param/sample.yaml index 097d0cfd07b933c49e55478d51f3c6d0f3fb5429..8523c2182b8fba49278d020c5566f6ff862633a7 100644 --- a/param/sample.yaml +++ b/param/sample.yaml @@ -1,6 +1,8 @@ -safety_distance: 0.5 -traj_sim_time: 1.5 -traj_delta_time: 0.1 +footprint: [[0.7,0.15],[0.7,-0.15],[-0.35,-0.15],[-0.35,0.15]] +safety_distance: 0.2 +traj_sim_distance: 2.0 +traj_sim_time: 3.0 +traj_sim_step: 0.1 max_linear_deacc: 1.0 use_front_lase: true front_laser_watchdog_time: 1.0 diff --git a/src/safe_cmd_alg.cpp b/src/safe_cmd_alg.cpp index 603ae5b937dc920bf14fe84c75dedbff049e87ec..bbe90281409efcf6799973f6f3325cdae352bfae 100644 --- a/src/safe_cmd_alg.cpp +++ b/src/safe_cmd_alg.cpp @@ -14,14 +14,19 @@ void SafeCmdAlgorithm::config_update(Config& new_cfg, uint32_t level) this->lock(); // save the current configuration + if(new_cfg.safety_distance<new_cfg.traj_sim_step) + new_cfg.traj_sim_step=new_cfg.safety_distance; this->config_=new_cfg; - this->num_samples=this->config_.traj_sim_time/this->config_.traj_delta_time; + if(this->config_.architecture==0) + this->num_samples=this->config_.traj_sim_distance/this->config_.traj_sim_step; + else + this->num_samples=this->config_.traj_sim_time/this->config_.traj_sim_step; this->unlock(); } // SafeCmdAlgorithm Public API -void SafeCmdAlgorithm::computer_ackermann_steer_angle_trajectory(double linear_x,double steer_angle,std::vector<TTrajPoint> &traj) +void SafeCmdAlgorithm::compute_trajectory_ackermann_steer_angle(double linear_x,double steer_angle,std::vector<TTrajPoint> &traj) { double radius; @@ -38,21 +43,26 @@ void SafeCmdAlgorithm::computer_ackermann_steer_angle_trajectory(double linear_x { if(radius!=std::numeric_limits<double>::max()) { - traj[i].theta=i*linear_x*this->config_.traj_sim_time/(radius*this->num_samples); + if(linear_x>0.0) + traj[i].theta=traj[i-1].theta+this->config_.traj_sim_step/radius; + else + traj[i].theta=traj[i-1].theta-this->config_.traj_sim_step/radius; traj[i].x=radius*sin(traj[i].theta); traj[i].y=radius*(1.0-cos(traj[i].theta)); } else { traj[i].theta=traj[i-1].theta; - traj[i].x=traj[i-1].x+linear_x*this->config_.traj_delta_time; + if(linear_x>0.0) + traj[i].x=traj[i-1].x+this->config_.traj_sim_step; + else + traj[i].x=traj[i-1].x-this->config_.traj_sim_step; traj[i].y=traj[i-1].y; } - std::cout << traj[i].x << "," << traj[i].y << std::endl; } } -void SafeCmdAlgorithm::computer_ackermann_angular_speed_trajectory(double linear_x,double angular_z,std::vector<TTrajPoint> &traj) +void SafeCmdAlgorithm::compute_trajectory_ackermann_angular_speed(double linear_x,double angular_z,std::vector<TTrajPoint> &traj) { traj.resize(this->num_samples); // estimate car motion at maximum speed @@ -61,19 +71,40 @@ void SafeCmdAlgorithm::computer_ackermann_angular_speed_trajectory(double linear traj[0].y=0.0; for(unsigned int i=1;i<this->num_samples;i++) { - traj[i].theta=traj[i-1].theta+angular_z*this->config_.traj_delta_time/2.0; - traj[i].x=traj[i-1].x+linear_x*this->config_.traj_delta_time*cos(traj[i].theta); - traj[i].y=traj[i-1].y+linear_x*this->config_.traj_delta_time*sin(traj[i].theta); - traj[i].theta+=angular_z*this->config_.traj_delta_time/2.0; + traj[i].theta=traj[i-1].theta+angular_z*this->config_.traj_sim_step/2.0; + traj[i].x=traj[i-1].x+linear_x*this->config_.traj_sim_step*cos(traj[i].theta); + traj[i].y=traj[i-1].y+linear_x*this->config_.traj_sim_step*sin(traj[i].theta); + traj[i].theta+=angular_z*this->config_.traj_sim_step/2.0; } } -void SafeCmdAlgorithm::computer_differential_trajectory(double linear_x,double angular_z,std::vector<TTrajPoint> &traj) +void SafeCmdAlgorithm::compute_trajectory_differential(double linear_x,double angular_z,std::vector<TTrajPoint> &traj) { } -void SafeCmdAlgorithm::limit_velocities_ackermann(double current_linear_x,double &new_linear_x,double time) +void SafeCmdAlgorithm::limit_velocities_ackermann_steer_angle(double current_linear_x,double current_angular_z,double &new_linear_x,double &new_angular_z,unsigned int index) { + double brake_distance,collision_distance; + + brake_distance=pow(current_linear_x,2.0)/(this->config_.max_linear_deacc*2.0); + collision_distance=index*this->config_.traj_sim_step; + if(collision_distance<brake_distance) + { + if(current_linear_x>0.0) + new_linear_x=sqrt(2.0*this->config_.max_linear_deacc*collision_distance); + else + new_linear_x=-sqrt(2.0*this->config_.max_linear_deacc*collision_distance); + } + else + new_linear_x=current_linear_x; + new_angular_z=current_angular_z; +} + +void SafeCmdAlgorithm::limit_velocities_ackermann_angular_speed(double current_linear_x,double current_angular_z,double &new_linear_x,double &new_angular_z,unsigned int index) +{ + double time; + + time=index*this->config_.traj_sim_step; if(this->config_.max_linear_deacc*time<fabs(current_linear_x)) { if(current_linear_x>0.0) @@ -83,11 +114,10 @@ void SafeCmdAlgorithm::limit_velocities_ackermann(double current_linear_x,double } else new_linear_x=current_linear_x; - std::cout << "current speed: " << current_linear_x << "-> new speed: " << new_linear_x << std::endl; + new_angular_z=current_angular_z; } -void SafeCmdAlgorithm::limit_velocities_differential(double current_linear_x,double current_angular_z,double &new_linear_x,double &new_angular_z,double time) +void SafeCmdAlgorithm::limit_velocities_differential(double current_linear_x,double current_angular_z,double &new_linear_x,double &new_angular_z,unsigned int index) { } - diff --git a/src/safe_cmd_alg_node.cpp b/src/safe_cmd_alg_node.cpp index 510f3f3348f6281b5bc386f25b5ffad2be1f68ff..94cdf2e0c26cfea1a4402c1845ccf04a48c89557 100644 --- a/src/safe_cmd_alg_node.cpp +++ b/src/safe_cmd_alg_node.cpp @@ -1,10 +1,13 @@ #include "safe_cmd_alg_node.h" +#include <pcl/filters/extract_indices.h> +#include <pcl_conversions/pcl_conversions.h> +#include <costmap_2d/footprint.h> #include <limits> SafeCmdAlgNode::SafeCmdAlgNode(void) : algorithm_base::IriBaseAlgorithm<SafeCmdAlgorithm>(), - new_front_laser_(false), - new_rear_laser_(false), + new_front_points_(false), + new_rear_points_(false), new_cmd_vel(false), tf2_listener(tf2_buffer) { @@ -12,15 +15,16 @@ SafeCmdAlgNode::SafeCmdAlgNode(void) : this->setRate(30);//in [Hz] // [init publishers] - cmd_vel_safe_publisher_ = public_node_handle_.advertise<geometry_msgs::Twist>("cmd_vel_safe", 1); + this->collision_points_publisher_ = this->private_node_handle_.advertise<sensor_msgs::PointCloud2>("collision_points", 1); + this->cmd_vel_safe_publisher_ = this->private_node_handle_.advertise<geometry_msgs::Twist>("cmd_vel_safe", 1); // [init subscribers] - this->footprint_subscriber_ = this->private_node_handle_.subscribe("footprint", 1, &SafeCmdAlgNode::footprint_callback, this); - pthread_mutex_init(&this->footprint_mutex_,NULL); + this->cmd_vel_subscriber_ = this->private_node_handle_.subscribe("cmd_vel", 1, &SafeCmdAlgNode::cmd_vel_callback,this); + this->rear_laser_subscriber_ = this->private_node_handle_.subscribe("rear_laser", 1, &SafeCmdAlgNode::rear_laser_callback, this); + this->front_laser_subscriber_ = this->private_node_handle_.subscribe("front_laser", 1, &SafeCmdAlgNode::front_laser_callback, this); - cmd_vel_subscriber_ = public_node_handle_.subscribe("cmd_vel", 1, &SafeCmdAlgNode::cmd_vel_callback,this); - rear_laser_subscriber_ = public_node_handle_.subscribe("rear_laser", 1, &SafeCmdAlgNode::rear_laser_callback, this); - front_laser_subscriber_ = public_node_handle_.subscribe("front_laser", 1, &SafeCmdAlgNode::front_laser_callback, this); + // read the safety footprint + this->footprint=costmap_2d::makeFootprintFromParams(this->private_node_handle_); // [init services] @@ -34,84 +38,106 @@ SafeCmdAlgNode::SafeCmdAlgNode(void) : SafeCmdAlgNode::~SafeCmdAlgNode(void) { // [free dynamic memory] - pthread_mutex_destroy(&this->footprint_mutex_); } void SafeCmdAlgNode::mainNodeThread(void) { + pcl::PointCloud<pcl::PointXYZ>::Ptr points(new pcl::PointCloud<pcl::PointXYZ>()); + bool new_data=false; + this->alg_.lock(); - if(this->new_cmd_vel) { this->new_cmd_vel=false; if(this->config.use_front_laser && this->front_laser_watchdog.is_active()) { - ROS_ERROR_DELAYED_THROTTLE(5,"SafeCmdAlgNode::mainNodeThread: Front laser watchdog timeout!"); - Twist_msg_.linear.x = 0.0; - Twist_msg_.angular.z = 0.0; + ROS_ERROR("SafeCmdAlgNode::mainNodeThread: Front laser watchdog timeout!"); + safe_Twist_msg_.linear.x = 0.0; + safe_Twist_msg_.linear.y = 0.0; + safe_Twist_msg_.linear.z = 0.0; + safe_Twist_msg_.angular.x = 0.0; + safe_Twist_msg_.angular.y = 0.0; + safe_Twist_msg_.angular.z = 0.0; cmd_vel_safe_publisher_.publish(Twist_msg_); } else if(this->config.use_rear_laser && this->rear_laser_watchdog.is_active()) { - ROS_ERROR_DELAYED_THROTTLE(5,"SafeCmdAlgNode::mainNodeThread: Rear laser watchdog timeout!"); - Twist_msg_.linear.x = 0.0; - Twist_msg_.angular.z = 0.0; + ROS_ERROR("SafeCmdAlgNode::mainNodeThread: Rear laser watchdog timeout!"); + safe_Twist_msg_.linear.x = 0.0; + safe_Twist_msg_.linear.y = 0.0; + safe_Twist_msg_.linear.z = 0.0; + safe_Twist_msg_.angular.x = 0.0; + safe_Twist_msg_.angular.y = 0.0; + safe_Twist_msg_.angular.z = 0.0; cmd_vel_safe_publisher_.publish(Twist_msg_); } else { - std::vector<geometry_msgs::Point32> points; - sensor_msgs::PointCloud2 cloud_front; - sensor_msgs::PointCloud2 cloud_rear; - try{ - if(this->new_front_laser_) + if(this->new_front_points_) + { + new_data=true; + this->new_front_points_=false; + (*points)=this->points_front; + if(this->new_rear_points_) { - this->laser_projector_.transformLaserScanToPointCloud(this->config.base_frame, this->front_laser_scan, cloud_front, this->tf2_buffer); - this->new_front_laser_=false; + this->new_rear_points_=false; + (*points)+=this->points_rear; } - if(this->new_rear_laser_) + } + else + { + if(this->new_rear_points_) { - this->laser_projector_.transformLaserScanToPointCloud(this->config.base_frame, this->rear_laser_scan, cloud_rear, this->tf2_buffer); - this->new_rear_laser_=false; + new_data=true; + this->new_rear_points_=false; + (*points)=this->points_rear; } - points = cloud_front.points; - points.insert(points.end(), cloud_rear.points.begin(), cloud_rear.points.end()); - }catch (tf::TransformException &ex){ - ROS_ERROR_DELAYED_THROTTLE(5,"SafeCmdAlgNode:: %s",ex.what()); } - geometry_msgs::PolygonStamped traj_footprint; - std::vector<geometry_msgs::Point32> collision_points; - double collision_length=0.0,collision_time=0.0; - bool first_time=true; - for(unsigned int i=0;i<this->traj.size();i++) + if(new_data) { - this->get_footprint_at(this->footprint,this->traj[i],traj_footprint); - if(this->check_collision(points,traj_footprint,collision_points)) + std::vector<geometry_msgs::Point> traj_footprint; + pcl::PointCloud<pcl::PointXYZ> collision_points,total_collision_points; + double collision_length=0.0; + unsigned int collision_index=0; + bool first_index=true; + for(unsigned int i=0;i<this->traj.size();i++) { - collision_length+=this->Twist_msg_.linear.x*this->config.traj_delta_time; - if(first_time) + this->get_footprint_at(this->footprint,this->traj[i],traj_footprint); + if(this->check_collision(points,traj_footprint,collision_points)) { - first_time=false; - collision_time=i*this->config.traj_delta_time; + if(first_index) + { + first_index=false; + collision_index=i; + } + if(collision_points.points.size()>total_collision_points.points.size()) + total_collision_points=collision_points; + collision_length+=this->config.traj_sim_step; + if(collision_length>this->config.safety_distance) + break; } - } - else + } + if(collision_length>=this->config.safety_distance) { - collision_length=0.0; - collision_time=0.0; - first_time=true; + if(this->config.architecture==0) + this->alg_.limit_velocities_ackermann_steer_angle(this->Twist_msg_.linear.x,this->Twist_msg_.angular.z,this->safe_Twist_msg_.linear.x,this->safe_Twist_msg_.angular.z,collision_index); + else if(this->config.architecture==1) + this->alg_.limit_velocities_ackermann_angular_speed(this->Twist_msg_.linear.x,this->Twist_msg_.angular.z,this->safe_Twist_msg_.linear.x,this->safe_Twist_msg_.angular.z,collision_index); + else if(this->config.architecture==2) + this->alg_.limit_velocities_differential(this->Twist_msg_.linear.x,this->Twist_msg_.angular.z,this->safe_Twist_msg_.linear.x,this->safe_Twist_msg_.angular.z,collision_index); + else + ROS_WARN("Unknown robot architecture"); } - } - if(collision_length>=this->robot_length/2.0) - { - if(this->config.architecture==0 || this->config.architecture==1)//ackermann - this->alg_.limit_velocities_ackermann(this->Twist_msg_.linear.x,this->Twist_msg_.linear.x,i*this->config.traj_delta_time); - else if(this->config.architecture==2) - this->alg_.limit_velocities_differential(this->Twist_msg_.linear.x,this->Twist_msg_.angular.z,this->Twist_msg_.linear.x,this->Twist_msg_.angular.z,i*this->config.traj_delta_time); else - ROS_WARN("Unknown robot architecture"); + this->safe_Twist_msg_=this->Twist_msg_; + this->cmd_vel_safe_publisher_.publish(this->safe_Twist_msg_); + pcl::toROSMsg(total_collision_points,this->collision_points_PointCloud2_msg_); + this->collision_points_PointCloud2_msg_.header.stamp=ros::Time::now(); + this->collision_points_PointCloud2_msg_.header.frame_id=this->config.base_frame; + this->collision_points_publisher_.publish(this->collision_points_PointCloud2_msg_); } - this->cmd_vel_safe_publisher_.publish(this->Twist_msg_); + else + this->cmd_vel_safe_publisher_.publish(this->safe_Twist_msg_); } } @@ -127,72 +153,6 @@ void SafeCmdAlgNode::mainNodeThread(void) } /* [subscriber callbacks] */ -void SafeCmdAlgNode::footprint_callback(const geometry_msgs::PolygonStamped::ConstPtr& msg) -{ - //ROS_INFO("SafeCmdAlgNode::footprint_callback: New Message Received"); - - //use appropiate mutex to shared variables if necessary - this->alg_.lock(); - //this->footprint_mutex_enter(); - // transform the footprint to the base frame - geometry_msgs::TransformStamped transform; - bool tf2_exists; - try{ - this->alg_.unlock(); - tf2_exists=this->tf2_buffer.canTransform(msg->header.frame_id,this->config.base_frame,msg->header.stamp,ros::Duration(0.1)); - this->alg_.lock(); - transform = this->tf2_buffer.lookupTransform(msg->header.frame_id,this->config.base_frame,msg->header.stamp); - if(tf2_exists) - { - geometry_msgs::PointStamped point_in,point_out; - this->footprint.header=msg->header; - this->footprint.polygon.points.resize(msg->polygon.points.size()); - point_in.header=msg->header; - for(unsigned int i=0;i<msg->polygon.points.size();i++) - { - point_in.point.x=msg->polygon.points[i].x; - point_in.point.y=msg->polygon.points[i].y; - point_in.point.z=msg->polygon.points[i].z; - tf2::doTransform(point_in,point_out,transform); - this->footprint.polygon.points[i].x=point_out.point.x; - this->footprint.polygon.points[i].y=point_out.point.y; - this->footprint.polygon.points[i].z=point_out.point.z; - } - // compute the robot length - double max_x=-std::numeric_limits<double>::max,min_x=std::numeric_limits<double>::max(); - for(unsigned int i=0;i<this->footprint.polygon.points.size();i++) - { - if(this->footprint.polygon.points[i].x>max_x) - max_x=this->footprint.polygon.points.x; - else if(this->footprint.polygon.points.x<min_x) - min_x=this->footprint.polygon.points.x; - } - this->robot_length=max_x-min_x; - this->footprint_subscriber_.shutdown(); - } - else - { - ROS_WARN_STREAM("No transform found from " << this->config.base_frame << " to " << msg->header.frame_id); - } - }catch (tf2::TransformException &ex){ - ROS_ERROR_STREAM("TF2 Exception: " << ex.what()); - } - //std::cout << msg->data << std::endl; - //unlock previously blocked shared variables - this->alg_.unlock(); - //this->footprint_mutex_exit(); -} - -void SafeCmdAlgNode::footprint_mutex_enter(void) -{ - pthread_mutex_lock(&this->footprint_mutex_); -} - -void SafeCmdAlgNode::footprint_mutex_exit(void) -{ - pthread_mutex_unlock(&this->footprint_mutex_); -} - void SafeCmdAlgNode::cmd_vel_callback(const geometry_msgs::Twist::ConstPtr& msg) { //ROS_INFO("SafeCmdAlgNode::cmd_vel_callback: New Message Received"); @@ -202,11 +162,11 @@ void SafeCmdAlgNode::cmd_vel_callback(const geometry_msgs::Twist::ConstPtr& msg) this->Twist_msg_=*msg; // compute the robot trajectory depending on the architecture if(this->config.architecture==0)// ackermann using steering angle - this->alg_.computer_ackermann_steer_angle_trajectory(msg->linear.x,msg->angular.z,this->traj); + this->alg_.compute_trajectory_ackermann_steer_angle(msg->linear.x,msg->angular.z,this->traj); else if(this->config.architecture==1)// ackermann using angular velocity - this->alg_.computer_ackermann_angular_speed_trajectory(msg->linear.x,msg->angular.z,this->traj); + this->alg_.compute_trajectory_ackermann_angular_speed(msg->linear.x,msg->angular.z,this->traj); else if(this->config.architecture==2)// differential - this->alg_.computer_differential_trajectory(msg->linear.x,msg->angular.z,this->traj); + this->alg_.compute_trajectory_differential(msg->linear.x,msg->angular.z,this->traj); else { ROS_WARN("Unknown robot architecture"); @@ -219,13 +179,20 @@ void SafeCmdAlgNode::cmd_vel_callback(const geometry_msgs::Twist::ConstPtr& msg) } void SafeCmdAlgNode::rear_laser_callback(const sensor_msgs::LaserScan::ConstPtr& msg) { + sensor_msgs::PointCloud2 cloud; + //ROS_INFO("SafeCmdAlgNode::rear_laser_callback: New Message Received"); this->alg_.lock(); //rear_laser_mutex_.enter(); - this->rear_laser_scan = *msg; this->rear_laser_watchdog.reset(ros::Duration(this->config.rear_laser_watchdog_time)); - this->new_rear_laser_ = true; + try{ + this->laser_projector_.transformLaserScanToPointCloud(this->config.base_frame, *msg, cloud, this->tf2_buffer); + }catch (tf::TransformException &ex){ + ROS_ERROR_STREAM("SafeCmdAlgNode:: " << ex.what()); + } + pcl::fromROSMsg(cloud, this->points_rear); + this->new_rear_points_ = true; //ROS_INFO("Max vel r: %f",max_vel_rear_); //rear_laser_mutex_.exit(); @@ -233,13 +200,20 @@ void SafeCmdAlgNode::rear_laser_callback(const sensor_msgs::LaserScan::ConstPtr& } void SafeCmdAlgNode::front_laser_callback(const sensor_msgs::LaserScan::ConstPtr& msg) { + sensor_msgs::PointCloud2 cloud; + //ROS_INFO("SafeCmdAlgNode::front_laser_callback: New Message Received"); this->alg_.lock(); //front_laser_mutex_.enter(); - this->front_laser_scan = *msg; this->front_laser_watchdog.reset(ros::Duration(this->config.front_laser_watchdog_time)); - this->new_front_laser_ = true; + try{ + this->laser_projector_.transformLaserScanToPointCloud(this->config.base_frame, *msg, cloud, this->tf2_buffer); + }catch (tf::TransformException &ex){ + ROS_ERROR_STREAM("SafeCmdAlgNode:: " << ex.what()); + } + pcl::fromROSMsg(cloud, this->points_front); + this->new_front_points_ = true; //ROS_INFO("Max vel f: %f",max_vel_front_); //front_laser_mutex_.exit(); @@ -263,41 +237,103 @@ void SafeCmdAlgNode::addNodeDiagnostics(void) { } -void SafeCmdAlgNode::get_footprint_at(geometry_msgs::PolygonStamped &footprint_in,TTrajPoint &point,geometry_msgs::PolygonStamped &footprint_out) +void SafeCmdAlgNode::get_footprint_at(std::vector<geometry_msgs::Point> &footprint_in,TTrajPoint &point,std::vector<geometry_msgs::Point> &footprint_out) { footprint_out=footprint_in; - std::cout << "point: " << point.x << "," << point.y << "," << point.theta << std::endl; - for(unsigned int i=0;i<footprint_in.polygon.points.size();i++) + + for(unsigned int i=0;i<footprint_in.size();i++) { - footprint_out.polygon.points[i].x=footprint_in.polygon.points[i].x*cos(point.theta)-footprint_in.polygon.points[i].y*sin(point.theta)+point.x; - footprint_out.polygon.points[i].y=footprint_in.polygon.points[i].x*sin(point.theta)+footprint_in.polygon.points[i].y*cos(point.theta)+point.y; - std::cout << "fotprint: " << footprint_out.polygon.points[i].x << "," << footprint_out.polygon.points[i].y << std::endl; + footprint_out[i].x=footprint_in[i].x*cos(point.theta)-footprint_in[i].y*sin(point.theta)+point.x; + footprint_out[i].y=footprint_in[i].x*sin(point.theta)+footprint_in[i].y*cos(point.theta)+point.y; } } -bool SafeCmdAlgNode::check_collision(std::vector<geometry_msgs::Point32> &scan_points,geometry_msgs::PolygonStamped &footprint,std::vector<geometry_msgs::Point32> &collision_points) +bool SafeCmdAlgNode::check_collision(pcl::PointCloud<pcl::PointXYZ>::Ptr &scan_points,std::vector<geometry_msgs::Point> &footprint,pcl::PointCloud<pcl::PointXYZ> &collision_points) { double distance; bool collision=false; + pcl::ExtractIndices<pcl::PointXYZ> extract; + pcl::PointIndices::Ptr inliers(new pcl::PointIndices()); + if(footprint.size()<3) + return true; collision_points.clear(); - for(unsigned int i=0;i<scan_points.size();i++) + for(unsigned int i=0;i<scan_points->points.size();i++) { - for(unsigned int j=0;j<footprint.polygon.points.size();j++) + if(this->inside(footprint,scan_points->points[i])) { - distance=sqrt(pow(scan_points[i].x-footprint.polygon.points[j].x,2.0)+pow(scan_points[i].y-footprint.polygon.points[j].y,2.0)); - if(distance<this->config.safety_distance) - { - collision=true; - collision_points.push_back(scan_points[i]); - std::cout << "Point: " << scan_points[i].x << "," << scan_points[i].y << " in collision" << std::endl; - } + collision=true; + inliers->indices.push_back(i); } } + extract.setInputCloud(scan_points); + extract.setIndices(inliers); + extract.setNegative(false); + extract.filter(collision_points); return collision; } +bool SafeCmdAlgNode::on_segment(geometry_msgs::Point &p,geometry_msgs::Point &q,geometry_msgs::Point &r) +{ + if (q.x <= std::max(p.x, r.x)+this->config.traj_sim_step && q.x >= std::min(p.x, r.x)-this->config.traj_sim_step && + q.y <= std::max(p.y, r.y)+this->config.traj_sim_step && q.y >= std::min(p.y, r.y)-this->config.traj_sim_step) + return true; + return false; +} + +int SafeCmdAlgNode::orientation(geometry_msgs::Point &p,geometry_msgs::Point &q,geometry_msgs::Point &r) +{ + int val = (q.y - p.y) * (r.x - q.x) - + (q.x - p.x) * (r.y - q.y); + + if (val == 0) return 0; + return (val > 0)? 1: 2; +} + +bool SafeCmdAlgNode::intersect(geometry_msgs::Point &p1,geometry_msgs::Point &q1,geometry_msgs::Point &p2,geometry_msgs::Point &q2) +{ + int o1=this->orientation(p1,q1,p2); + int o2=this->orientation(p1,q1,q2); + int o3=this->orientation(p2,q2,p1); + int o4=this->orientation(p2,q2,q1); + + if(o1 != o2 && o3 != o4) + return true; + + // Special Cases + if(o1==0 && this->on_segment(p1,p2,q1)) return true; + if(o2==0 && this->on_segment(p1,q2,q1)) return true; + if(o3==0 && this->on_segment(p2,p1,q2)) return true; + if(o4==0 && this->on_segment(p2,q1,q2)) return true; + + return false; // Doesn't fall in any of the above cases +} + +bool SafeCmdAlgNode::inside(std::vector<geometry_msgs::Point> &footprint,pcl::PointXYZ &point) +{ + int count=0,i=0; + geometry_msgs::Point extreme,p; + + extreme.x=std::numeric_limits<double>::infinity(); + extreme.y=point.y; + p.x=point.x; + p.y=point.y; + do + { + int next=(i+1)%footprint.size(); + if(this->intersect(footprint[i],footprint[next],p,extreme)) + { +// if(this->orientation(footprint[i],p,footprint[next])==0) +// return this->on_segment(footprint[i],p,footprint[next]); + count++; + } + i=next; + }while(i!=0); + + return count&1; +} + /* main function */ int main(int argc,char *argv[]) {