door_angle_detector.cpp
Go to the documentation of this file.
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> //quiet_nan
00013 #include <cmath> //atan2
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         //scan->header.stamp.setNow(ros::Time::now());
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       //Now 0,0,0 is at the door axis, 1,0,0 at tha handle in closed position, 0,1,0 at the handle in opened position
00055       //projector_.projectLaser(*scan, *cloud);
00056       //Extract the 1.5m in front of the laser scanner
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;//Important for visualization
00064         visualization_msgs::Marker polygon = visualize_polygon(cropping_polygon_);
00065         vis_pub_.publish(polygon);
00066       }
00067       //inside_cloud.header 
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         //Angle wraps at +90 to -90 degrees, make it -20 to 160
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         //Send Door state msg
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         //Visualize
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         //For debugging, send cropped cloud
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 //Computes the (smaller) angle to the Y axis
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     //Numerically accurate line fitting code from: www.johndcook.com/blog/2008/10/20/comparing-two-ways-to-fit-a-line-to-data/
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);//y and x equivalent, see below
00137     
00138     //Compute axis position if previous door
00139     double new_slope = sts/sum_delta_sq; //Slope is also: y/x
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 //Send out last result
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();//get tf data
00193     ros::spin();
00194     return 0;
00195 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Friends


door_perception
Author(s): Felix Endres
autogenerated on Wed Dec 26 2012 16:03:53