diff --git a/CMakeLists.txt b/CMakeLists.txt index ee9a34e27595817bc17a67455cddf813baa48a90..289bc81c16cc455482c8223f8c61ca4aee923984 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) +find_package(catkin REQUIRED COMPONENTS geometry_msgs sensor_msgs iri_base_algorithm tf laser_geometry) ## System dependencies are found with CMake's conventions # find_package(Boost REQUIRED COMPONENTS system) @@ -15,6 +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) # ******************************************************************** # Add topic, service and action definition here @@ -60,7 +61,7 @@ catkin_package( # ******************************************************************** # Add ROS and IRI ROS run time dependencies # ******************************************************************** - CATKIN_DEPENDS geometry_msgs sensor_msgs iri_base_algorithm + CATKIN_DEPENDS geometry_msgs sensor_msgs iri_base_algorithm tf laser_geometry # ******************************************************************** # Add system and labrobotica run time dependencies here # ******************************************************************** @@ -77,6 +78,7 @@ catkin_package( include_directories(include) include_directories(${catkin_INCLUDE_DIRS}) include_directories(${iriutils_INCLUDE_DIR}) +include_directories(${EIGEN3_INCLUDE_DIR}) ## Declare a cpp library # add_library(${PROJECT_NAME} <list of source files>) @@ -94,6 +96,7 @@ target_link_libraries(${PROJECT_NAME} ${iriutils_LIBRARY}) # Add message headers dependencies # ******************************************************************** # add_dependencies(${PROJECT_NAME} <msg_package_name>_generate_messages_cpp) +add_dependencies(${PROJECT_NAME} sensor_msgs_generate_messages_cpp) # ******************************************************************** # Add dynamic reconfigure dependencies # ******************************************************************** diff --git a/cfg/SafeCmd.cfg b/cfg/SafeCmd.cfg index 2e543d2d84140c1dc8cfc805706c60f313c17c63..3a7ab11db3e1edc68755ece3c6c882ba7a45524d 100755 --- a/cfg/SafeCmd.cfg +++ b/cfg/SafeCmd.cfg @@ -39,11 +39,21 @@ gen = ParameterGenerator() # Name Type Reconfiguration level Description Default Min Max gen.add("unsafe", bool_t, 0, "Not use the safe module", False) -gen.add("min_dist",double_t, 0, "distance in meters where to stop",0.4, 0.0, 2.0) +gen.add("min_dist",double_t, 0, "distance in meters where to stop",0.5, 0.0, 2.0) gen.add("collision_time", double_t, 0, "time in seconds to compute collision", 1.0, 0.1, 5.0) gen.add("limit_vel_front", double_t, 0, "max speed in meters per second forward", 1.0, 0.0, 2.0) -gen.add("limit_vel_rear", double_t, 0, "max speed in meters per second backward", 1.0, 0.0, 2.0) +gen.add("limit_vel_rear", double_t, 0, "max speed in meters per second backward", -1.0, -2.0, 0.0) gen.add("front_laser_watchdog_time", double_t, 0, "Maximum time (seconds) between front_laser msgs",1.0, 0.1, 2.0) gen.add("rear_laser_watchdog_time", double_t, 0, "Maximum time (seconds) between rear_laser msgs",1.0, 0.1, 2.0) +gen.add("base_frame", str_t, 0, "Frame id to transform scans to", "base_link") +gen.add("unsafe_joy_button_id", int_t, 0, "joy id button to switch unsafe mode", 4, 0, 20) + +mode_enum = gen.enum([ + gen.const("front_rear", int_t, 0, "Check front and rear lasers independently, limiting front and rear velocity respectively. min_dist referred to laser frame, relates to laser point range"), + gen.const("frame_check", int_t, 1, "Transform laser points to base_frame, limiting front or rear velocity depending on x coordinate sign. min_dist acts like a robot radius")], + "mode") + +gen.add("mode", int_t, 0, "Selected mode", 1, edit_method=mode_enum) + exit(gen.generate(PACKAGE, "SafeCmdAlgorithm", "SafeCmd")) diff --git a/include/safe_cmd_alg_node.h b/include/safe_cmd_alg_node.h index d8ddaaf910ae1708910f45d74a369ba767f63767..6f8458d3cbc679262219581618538952661534e8 100644 --- a/include/safe_cmd_alg_node.h +++ b/include/safe_cmd_alg_node.h @@ -29,6 +29,7 @@ #include "safe_cmd_alg.h" // [publisher subscriber headers] +#include <sensor_msgs/Joy.h> #include <geometry_msgs/Twist.h> #include <sensor_msgs/LaserScan.h> @@ -38,6 +39,9 @@ // [action server client headers] +#include <laser_geometry/laser_geometry.h> +#include <tf/transform_listener.h> + /** * \brief IRI ROS Specific Algorithm Class * @@ -51,6 +55,12 @@ class SafeCmdAlgNode : public algorithm_base::IriBaseAlgorithm<SafeCmdAlgorithm> geometry_msgs::Twist last_twist_; // [subscriber attributes] + ros::Subscriber joy_subscriber_; + void joy_callback(const sensor_msgs::Joy::ConstPtr& msg); + pthread_mutex_t joy_mutex_; + void joy_mutex_enter(void); + void joy_mutex_exit(void); + ros::Subscriber cmd_vel_subscriber_; void cmd_vel_callback(const geometry_msgs::Twist::ConstPtr& msg); CMutex cmd_vel_mutex_; @@ -64,6 +74,13 @@ class SafeCmdAlgNode : public algorithm_base::IriBaseAlgorithm<SafeCmdAlgorithm> bool front_laser_received_; bool rear_laser_received_; bool new_cmd_vel; + + sensor_msgs::LaserScan front_laser_scan; + sensor_msgs::LaserScan rear_laser_scan; + laser_geometry::LaserProjection laser_projector_; + tf::TransformListener tf_listener_; + + std::vector<int> joy_previous_buttons; // [service attributes] @@ -78,7 +95,8 @@ class SafeCmdAlgNode : public algorithm_base::IriBaseAlgorithm<SafeCmdAlgorithm> float max_vel_rear_; float limit_vel_front_; float limit_vel_rear_; - float compute_max_velocity_(const sensor_msgs::LaserScan::ConstPtr& scan) const; + float compute_max_velocity_(const sensor_msgs::LaserScan::ConstPtr& scan); + void compute_max_velocities_(const std::vector<geometry_msgs::Point32> points, float & max_vel_front, float & max_vel_rear); /** * \brief config variable diff --git a/launch/node.launch b/launch/node.launch new file mode 100644 index 0000000000000000000000000000000000000000..a3a410ab45937bdb4ddc55a15e192d47c2566f88 --- /dev/null +++ b/launch/node.launch @@ -0,0 +1,26 @@ +<?xml version="1.0"?> +<!-- --> +<launch> + + <arg name="ns" default="$(optenv ROBOT myrobot)"/> + <arg name="config_file" default="$(find iri_safe_cmd)/param/sample.yaml" /> + <arg name="node_name" default="iri_safe_cmd" /> + <arg name="output" default="screen" /> + <arg name="launch_prefix" default="" /> + + <node pkg ="iri_safe_cmd" + type="iri_safe_cmd" + name="$(arg node_name)" + ns="$(arg ns)" + output="$(arg output)" + launch-prefix="$(arg launch_prefix)"> + <rosparam file="$(arg config_file)" command="load" /> + <param name="base_frame" value="$(arg ns)/base_link" /> + <remap from="~cmd_vel_safe" to="/$(arg ns)/cmd_vel"/> + <remap from="~cmd_vel" to="/$(arg ns)/cmd_vel_unsafe"/> + <remap from="~rear_laser" to="/$(arg ns)/sensors/rear_laser_scan"/> + <remap from="~front_laser" to="/$(arg ns)/sensors/front_laser_scan"/> + <remap from="~joy" to="/$(arg ns)/joy"/> + </node> + +</launch> \ No newline at end of file diff --git a/package.xml b/package.xml index 12e961b157d7fd9d252c0214810d700838ebf970..43bebf3670d65acbb26229616af5f2fd246e73b5 100644 --- a/package.xml +++ b/package.xml @@ -30,6 +30,8 @@ <build_depend>iri_base_algorithm</build_depend> <build_depend>geometry_msgs</build_depend> <build_depend>sensor_msgs</build_depend> + <build_depend>tf</build_depend> + <build_depend>laser_geometry</build_depend> <!-- ****************************************************************** --> <!-- Place run dependencies here --> @@ -38,6 +40,8 @@ <run_depend>iri_base_algorithm</run_depend> <run_depend>geometry_msgs</run_depend> <run_depend>sensor_msgs</run_depend> + <run_depend>tf</run_depend> + <run_depend>laser_geometry</run_depend> <!-- Use test_depend for packages you need only for testing: --> <!-- <test_depend>gtest</test_depend> --> diff --git a/param/sample.yaml b/param/sample.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ffe4a9274f6e689de490cfff17df6393a60b5f74 --- /dev/null +++ b/param/sample.yaml @@ -0,0 +1,10 @@ +unsafe: false +min_dist: 0.5 +collision_time: 1.5 +limit_vel_front: 1.0 +limit_vel_rear: -1.0 +front_laser_watchdog_time: 1.0 +rear_laser_watchdog_time: 1.0 +#base_frame: "/myrobot/base_link" #passed as param in launch /ns/base_link +unsafe_joy_button_id: 4 +mode: 1 #available modes: 0: front_rear, 1: frame_check \ No newline at end of file diff --git a/src/safe_cmd_alg_node.cpp b/src/safe_cmd_alg_node.cpp index babd0f65e5d6e6980ac60b4519a02cdce4a33e3d..57afb81dc30a6fcd1d416131c7c53141d90d99f4 100644 --- a/src/safe_cmd_alg_node.cpp +++ b/src/safe_cmd_alg_node.cpp @@ -10,15 +10,19 @@ SafeCmdAlgNode::SafeCmdAlgNode(void) : limit_vel_rear_(0.0), front_laser_received_(false), rear_laser_received_(false), - new_cmd_vel(false) + new_cmd_vel(false), + tf_listener_(ros::Duration(10.f)) { //init class attributes if necessary - loop_rate_ = 20;//in [Hz] + loop_rate_ = 30;//in [Hz] // [init publishers] cmd_vel_safe_publisher_ = public_node_handle_.advertise<geometry_msgs::Twist>("cmd_vel_safe", 100); // [init subscribers] + this->joy_subscriber_ = this->private_node_handle_.subscribe("joy", 1, &SafeCmdAlgNode::joy_callback, this); + pthread_mutex_init(&this->joy_mutex_,NULL); + cmd_vel_subscriber_ = public_node_handle_.subscribe("cmd_vel", 100, &SafeCmdAlgNode::cmd_vel_callback,this); rear_laser_subscriber_ = public_node_handle_.subscribe("rear_laser", 100, &SafeCmdAlgNode::rear_laser_callback, this); front_laser_subscriber_ = public_node_handle_.subscribe("front_laser", 100, &SafeCmdAlgNode::front_laser_callback, this); @@ -30,6 +34,10 @@ SafeCmdAlgNode::SafeCmdAlgNode(void) : // [init action servers] // [init action clients] + + this->config.front_laser_watchdog_time = 1.0; + this->config.rear_laser_watchdog_time = 1.0; + this->reset_front_laser_watchdog(); this->reset_rear_laser_watchdog(); } @@ -37,14 +45,60 @@ SafeCmdAlgNode::SafeCmdAlgNode(void) : SafeCmdAlgNode::~SafeCmdAlgNode(void) { // [free dynamic memory] + pthread_mutex_destroy(&this->joy_mutex_); } void SafeCmdAlgNode::mainNodeThread(void) { this->alg_.lock(); - if(!alg_.config_.unsafe) + if(!this->config.unsafe) { + if(this->config.mode==1) + { + if(this->front_laser_received_ || this->rear_laser_received_) + { + std::vector<geometry_msgs::Point32> points; + + try + { + float cutoff=1.5; + sensor_msgs::PointCloud cloud_front; + if(this->front_laser_received_) + { + this->laser_projector_.transformLaserScanToPointCloud(this->config.base_frame, this->front_laser_scan, cloud_front, tf_listener_, cutoff); + this->front_laser_received_=false; + } + + sensor_msgs::PointCloud cloud_rear; + if(this->rear_laser_received_) + { + this->laser_projector_.transformLaserScanToPointCloud(this->config.base_frame, this->rear_laser_scan, cloud_rear, tf_listener_, cutoff); + this->rear_laser_received_=false; + } + + 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()); + } + + + this->compute_max_velocities_(points, max_vel_front_, max_vel_rear_); + + max_vel_front_ = std::min(max_vel_front_, limit_vel_front_); + max_vel_rear_ = std::max(max_vel_rear_, limit_vel_rear_); + + //ROS_INFO("front=%f, rear=%f",max_vel_front_, max_vel_rear_); + + } + } + + //ROS_INFO("max_vel_front = %f, max_vel_rear = %f", max_vel_front_, max_vel_rear_); + this->update_front_laser_watchdog(); this->update_rear_laser_watchdog(); if(this->front_laser_watchdog_active() || this->rear_laser_watchdog_active()) @@ -62,18 +116,18 @@ void SafeCmdAlgNode::mainNodeThread(void) } else { - if(Twist_msg_.linear.x > fabs(max_vel_front_)) + if(Twist_msg_.linear.x > max_vel_front_) { - ROS_INFO_STREAM_THROTTLE(1,"SafeCmdAlgNode::mainNodeThread: Reducing forward velocity from "<< Twist_msg_.linear.x << " to "<<fabs(max_vel_front_)); - Twist_msg_.linear.x = fabs(max_vel_front_); + //ROS_INFO_STREAM_THROTTLE(1,"SafeCmdAlgNode::mainNodeThread: Reducing forward velocity from "<< Twist_msg_.linear.x << " to "<<fabs(max_vel_front_)); + Twist_msg_.linear.x = max_vel_front_; if(max_vel_front_==0) Twist_msg_.angular.z = 0; } - if(Twist_msg_.linear.x < -fabs(max_vel_rear_)) + if(Twist_msg_.linear.x < max_vel_rear_) { - ROS_INFO_STREAM_THROTTLE(1,"SafeCmdAlgNode::mainNodeThread: Reducing backward velocity from "<< Twist_msg_.linear.x << " to "<<-fabs(max_vel_rear_)); - Twist_msg_.linear.x = -fabs(max_vel_rear_); + //ROS_INFO_STREAM_THROTTLE(1,"SafeCmdAlgNode::mainNodeThread: Reducing backward velocity from "<< Twist_msg_.linear.x << " to "<<-fabs(max_vel_rear_)); + Twist_msg_.linear.x = max_vel_rear_; if(max_vel_rear_==0) Twist_msg_.angular.z = 0; } @@ -98,6 +152,46 @@ void SafeCmdAlgNode::mainNodeThread(void) } /* [subscriber callbacks] */ +void SafeCmdAlgNode::joy_callback(const sensor_msgs::Joy::ConstPtr& msg) +{ + //ROS_INFO("SafeCmdAlgNode::joy_callback: New Message Received"); + + //use appropiate mutex to shared variables if necessary + this->alg_.lock(); + //this->joy_mutex_enter(); + static bool first=true; + if(first) + { + this->joy_previous_buttons.resize(msg->buttons.size()); + first=false; + } + + if(msg->buttons[this->config.unsafe_joy_button_id]==1) // && this->joy_previous_buttons[this->config.unsafe_joy_button_id]==0) + { + ROS_WARN_STREAM_THROTTLE(1, "SafeCmdAlgNode: joy enabled unsafe mode!"); + this->config.unsafe = true; //!this->config.unsafe; + } + else + this->config.unsafe = false; + + this->joy_previous_buttons=msg->buttons; + this->alg_.unlock(); + //std::cout << msg->data << std::endl; + //unlock previously blocked shared variables + //this->alg_.unlock(); + //this->joy_mutex_exit(); +} + +void SafeCmdAlgNode::joy_mutex_enter(void) +{ + pthread_mutex_lock(&this->joy_mutex_); +} + +void SafeCmdAlgNode::joy_mutex_exit(void) +{ + pthread_mutex_unlock(&this->joy_mutex_); +} + void SafeCmdAlgNode::cmd_vel_callback(const geometry_msgs::Twist::ConstPtr& msg) { //ROS_INFO("SafeCmdAlgNode::cmd_vel_callback: New Message Received"); @@ -117,8 +211,11 @@ void SafeCmdAlgNode::rear_laser_callback(const sensor_msgs::LaserScan::ConstPtr& //rear_laser_mutex_.enter(); this->reset_rear_laser_watchdog(); - max_vel_rear_ = std::min(compute_max_velocity_(msg),limit_vel_rear_); - rear_laser_received_ = true; + this->rear_laser_scan = *msg; + + if(this->config.mode == 0) + this->max_vel_rear_ = std::max(-compute_max_velocity_(msg),limit_vel_rear_); + this->rear_laser_received_ = true; //ROS_INFO("Max vel r: %f",max_vel_rear_); //rear_laser_mutex_.exit(); @@ -132,8 +229,11 @@ void SafeCmdAlgNode::front_laser_callback(const sensor_msgs::LaserScan::ConstPtr //front_laser_mutex_.enter(); this->reset_front_laser_watchdog(); - max_vel_front_ = std::min(compute_max_velocity_(msg),limit_vel_front_); - front_laser_received_ = true; + this->front_laser_scan = *msg; + + if(this->config.mode == 0) + this->max_vel_front_ = std::min(compute_max_velocity_(msg),limit_vel_front_); + this->front_laser_received_ = true; //ROS_INFO("Max vel f: %f",max_vel_front_); //front_laser_mutex_.exit(); @@ -178,11 +278,12 @@ bool min_test_(float i, float j) return true; return i<j; } -float SafeCmdAlgNode::compute_max_velocity_(const sensor_msgs::LaserScan::ConstPtr& scan) const + +float SafeCmdAlgNode::compute_max_velocity_(const sensor_msgs::LaserScan::ConstPtr& scan) { float min_range = *std::min_element( scan->ranges.begin(), scan->ranges.end(),min_test_); int min_pos = distance(scan->ranges.begin(),std::min_element( scan->ranges.begin(), scan->ranges.end() )); - float max_velocity = 0; + float max_velocity = 0.0; //float x = scan->ranges[min_pos]*cos(scan->angle_min + min_pos*scan->angle_increment); //float y = scan->ranges[min_pos]*sin(scan->angle_min + min_pos*scan->angle_increment); @@ -195,6 +296,38 @@ float SafeCmdAlgNode::compute_max_velocity_(const sensor_msgs::LaserScan::ConstP return max_velocity; } +void SafeCmdAlgNode::compute_max_velocities_(const std::vector<geometry_msgs::Point32> points, float & max_vel_front, float & max_vel_rear) +{ + float min_range_front=10.0; + float min_range_rear =10.0; + for(unsigned int i=0; i<points.size(); i++) + { + float x = points[i].x; + float y = points[i].y; + float range = sqrt(pow(x,2)+pow(y,2)); + if(x > 0) + { + if(range<min_range_front) + min_range_front=range; + } + else if(x < 0) + { + if(range<min_range_rear) + min_range_rear=range; + } + } + + if(min_range_front > min_dist_) + max_vel_front = min_range_front / collision_time_; + else + max_vel_front = 0.0; + + if(min_range_rear > min_dist_) + max_vel_rear = - min_range_rear / collision_time_; + else + max_vel_rear = 0.0; +} + void SafeCmdAlgNode::reset_front_laser_watchdog(void) { this->front_laser_watchdog_access.enter();