00001 #include "door_angle_detector.h"
00002
00003 #include "polygon_filter.h"
00004 #include "visualize_polygon.hpp"
00005
00006 #include <std_msgs/Float64.h>
00007 #include <std_msgs/ColorRGBA.h>
00008
00009 #include <pcl/filters/passthrough.h>
00010 #include <pcl/ros/conversions.h>
00011
00012 #include <limits>
00013 #include <cmath>
00014
00015
00016 DoorAngleDetector::DoorAngleDetector() : door_frame_("/door")
00017 {
00018 ros::NodeHandle nh("~");
00019 nh.param<std::string>("door_frame", door_frame_, door_frame_);
00020 ROS_DEBUG("Using door frame: %s", door_frame_.c_str());
00021
00022 scan_sub_.subscribe(node_, "/base_scan", 10);
00023 tf_filter_ = new tf::MessageFilter<sensor_msgs::LaserScan>(scan_sub_, tfListener_, door_frame_, 10);
00024 tf_filter_->registerCallback( boost::bind(&DoorAngleDetector::scanCallback, this, _1) );
00025
00026 point_cloud_publisher_ = node_.advertise<sensor_msgs::PointCloud2> ("door_scan_cloud", 10, false);
00027 door_state_publisher_ = node_.advertise<door_perception_msgs::DoorState> ("door_state", 10, false);
00028 vis_pub_ = node_.advertise<visualization_msgs::Marker>( "door_state_visualization", 2 );
00029 angle_pub_ = node_.advertise<std_msgs::Float64>( "door_angle", 2 );
00030 service = node_.advertiseService("detect_door_state", &DoorAngleDetector::serviceCallback, this);
00031 intercept = slope = std::numeric_limits<typeof(intercept)>::quiet_NaN();
00032 angle = std::numeric_limits<typeof(angle)>::quiet_NaN();
00033
00034 cropping_polygon_.header.frame_id = door_frame_;
00035 PointType vertex;
00036 vertex.x = 0.20; vertex.y = 0.15; cropping_polygon_.points.push_back(vertex);
00037 vertex.x = -0.05; vertex.y = 0.15; cropping_polygon_.points.push_back(vertex);
00038 vertex.x = -0.50; vertex.y = 1.00; cropping_polygon_.points.push_back(vertex);
00039 vertex.x = 0.95; vertex.y = 1.00; cropping_polygon_.points.push_back(vertex);
00040 vertex.x = 0.95; vertex.y = -0.10; cropping_polygon_.points.push_back(vertex);
00041 vertex.x = 0.20; vertex.y = -0.10; cropping_polygon_.points.push_back(vertex);
00042 }
00043
00044
00045 void DoorAngleDetector::scanCallback(const sensor_msgs::LaserScan::ConstPtr& scan){
00046
00047 sensor_msgs::PointCloud2::Ptr cloud(new sensor_msgs::PointCloud2);
00048 last_scan_ = *scan;
00049
00050 try {
00051 ros::Time end_time = scan->header.stamp + ros::Duration ().fromSec (scan->ranges.size () * scan->time_increment);
00052 tfListener_.waitForTransform("/door", scan->header.frame_id, end_time, ros::Duration(0.1), ros::Duration(0.02));
00053 projector_.transformLaserScanToPointCloud(door_frame_,*scan, *cloud, tfListener_);
00054
00055
00056
00057 PointCloudType pcl_cloud;
00058 pcl::fromROSMsg(*cloud,pcl_cloud);
00059 cropping_polygon_.header = cloud->header;
00060
00061 PointCloudType inside_cloud = crop_by_polygon(cropping_polygon_, pcl_cloud, 0, 1);
00062 if(vis_pub_.getNumSubscribers() > 0){
00063 cropping_polygon_.header.stamp = scan->header.stamp;
00064 visualization_msgs::Marker polygon = visualize_polygon(cropping_polygon_);
00065 vis_pub_.publish(polygon);
00066 }
00067
00068
00069 if(inside_cloud.size() == 0){
00070 door_found = false;
00071 }
00072 else
00073 {
00074 geometry_msgs::Point door_axis, handle_approx;
00075 estimateDoorState(inside_cloud, angle, door_axis);
00076
00077 if(angle < -20.0/180.0*3.1415927){
00078 angle += 3.1415927;
00079 }
00080 door_found = true;
00081 door_axis.x = 0.0;
00082 door_axis.y = 0.0;
00083 handle_approx.x = door_axis.x + cos(angle);
00084 handle_approx.y = door_axis.y + sin(angle);
00085
00086 if(door_state_publisher_.getNumSubscribers() > 0){
00087 door_perception_msgs::DoorState ds;
00088 ds.header = cloud->header;;
00089 ds.door_found = true;
00090 ds.angle = angle;
00091 door_state_publisher_.publish(ds);
00092 }
00093
00094 ROS_DEBUG_THROTTLE(1, "Computed door state: %.1f degree to X axis (of %s)", angle*180.0/3.1415927, scan->header.frame_id.c_str());
00095 ROS_DEBUG_THROTTLE(1, "Current door axis estimate: (%.2f, %.2f)", door_axis.x, door_axis.y);
00096 ROS_DEBUG_THROTTLE(1, "Current door handle estimate: (%.2f, %.2f)", handle_approx.x, handle_approx.y);
00097
00098 if(vis_pub_.getNumSubscribers() > 0){
00099 visualization_msgs::Marker door = visualize_door(door_axis, handle_approx, cloud->header);
00100 vis_pub_.publish(door);
00101 }
00102
00103 if(point_cloud_publisher_.getNumSubscribers() > 0){
00104 pcl::toROSMsg(inside_cloud, *cloud);
00105 point_cloud_publisher_.publish(*cloud);
00106 }
00107 }
00108 }
00109 catch (tf::TransformException ex){
00110 ROS_WARN("%s",ex.what());
00111 }
00112 }
00113
00114
00115 void DoorAngleDetector::estimateDoorState(const PointCloudType & pcl_cloud , double& angle_out, geometry_msgs::Point& axis_position_out){
00116 if(pcl_cloud.size() == 0){
00117 ROS_INFO("Cannot estimate door angle from empty cloud. Open?");
00118 return;
00119 }
00120
00121
00122 double sumx = 0.0, sumy = 0.0, sum_delta_sq = 0.0, sts = 0.0;
00123 int n = pcl_cloud.size();
00124 for (int i = 0; i < n; ++i)
00125 {
00126 sumx += pcl_cloud.points[i].x;
00127 sumy += pcl_cloud.points[i].y;
00128 }
00129 for (int i = 0; i < n; ++i)
00130 {
00131 double delta_x_from_mean = pcl_cloud.points[i].x - sumx/n;
00132 sum_delta_sq += delta_x_from_mean*delta_x_from_mean;
00133 sts += delta_x_from_mean*pcl_cloud.points[i].y;
00134 }
00135
00136 angle_out = atan2(sts, sum_delta_sq);
00137
00138
00139 double new_slope = sts/sum_delta_sq;
00140 double new_intercept = (sumy - sumx*new_slope)/n;
00141 if(!isnan(slope) && !isnan(intercept)){
00142 double dslope = slope - new_slope;
00143 double dintercept = new_intercept - intercept;
00144 if(dslope != 0){
00145 axis_position_out.x = dintercept/dslope;
00146 axis_position_out.y = new_slope*axis_position_out.x + new_intercept;
00147 }
00148 }
00149 else{
00150 slope = new_slope;
00151 intercept = new_intercept;
00152 }
00153 }
00154
00155
00156 bool DoorAngleDetector::serviceCallback(door_perception_msgs::DoorStateSrv::Request& request, door_perception_msgs::DoorStateSrv::Response& response){
00157 response.state.door_found =door_found;
00158 response.state.angle = angle;
00159 return true;
00160 }
00161
00162 visualization_msgs::Marker visualize_door(geometry_msgs::Point p1, geometry_msgs::Point p2, std_msgs::Header header)
00163 {
00164 visualization_msgs::Marker door;
00165 door.header = header;
00166 door.id = 2;
00167 door.type = visualization_msgs::Marker::LINE_LIST;
00168 door.action = visualization_msgs::Marker::ADD;
00169 door.pose.orientation.w = 1.0;
00170 door.scale.x = 0.02;
00171 door.color.a = 0.5;
00172 door.color.r = 0.5;
00173 door.color.g = 0.5;
00174
00175 std_msgs::ColorRGBA c1, c2;
00176 c1.a = c2.a = 0.5;
00177 c1.r = 1.0;
00178 c2.b = 1.0;
00179 door.colors.push_back(c1);
00180 door.colors.push_back(c2);
00181
00182 door.points.push_back(p1);
00183 door.points.push_back(p2);
00184 return door;
00185 }
00186
00187
00188 int main(int argc, char** argv)
00189 {
00190 ros::init(argc, argv, "door_state");
00191 DoorAngleDetector doorAngleDetector;
00192 ros::Duration(1.0).sleep();
00193 ros::spin();
00194 return 0;
00195 }