diff --git a/cfg/LidarObstacleDetector.cfg b/cfg/LidarObstacleDetector.cfg
index 37d422711edc3608c48ae2466ebc810c9da63835..4085c6b784454b7c95b245f4dafc34893a1123bf 100755
--- a/cfg/LidarObstacleDetector.cfg
+++ b/cfg/LidarObstacleDetector.cfg
@@ -50,6 +50,7 @@ gen.add("max_inc",                 double_t,  0,                               "
 gen.add("min_inc",                 double_t,  0,                               "minimum inclination to take into account",0.05,0,0.5)
 gen.add("inc_area_filter",         int_t,     0,                               "Minimum inclination area to take into account",10,1,1000)
 gen.add("max_ring",                int_t,     0,                               "Maximum ring to process"      ,  10,       0,    15)
+gen.add("angle_resolution",        double_t,  0,                               "Angle resolution for the free space pointcloud",0.001,0.0001,0.1)
 # farthest point computation
 gen.add("farthest_enable",         bool_t,    0,                               "Enable the computation of the farthest point",True)
 gen.add("farthest_num_rings",      int_t,     0,                               "Number of rings to use in the farthest point computation",9,1,16)
diff --git a/config/default_config.yaml b/config/default_config.yaml
index bf059a19747afa22406cbf7f717bae5843d8869b..857af7acc2ff67e0bb14a9222beaa118bb57a071 100755
--- a/config/default_config.yaml
+++ b/config/default_config.yaml
@@ -1,5 +1,5 @@
 # sensor information
-#base_frame_id: "ana/base_footprint"
+base_frame_id: "base_footprint"
 min_range: 0.1
 max_range: 50.0
 sensor_height: 0.3
diff --git a/include/lidar_obstacle_detector_alg.h b/include/lidar_obstacle_detector_alg.h
index 57d624a66b251d70b1c4ca7b4729ce6d565f5b22..70507f31438d1479deb179c05503ceef6ae350e5 100644
--- a/include/lidar_obstacle_detector_alg.h
+++ b/include/lidar_obstacle_detector_alg.h
@@ -131,6 +131,7 @@ class LidarObstacleDetectorAlgorithm
 
     // here define all lidar_obstacle_detector_alg interface methods to retrieve and set
     // the driver parameters
+    std::vector<std::string> get_strings(const ros::NodeHandle& nh, const std::string& param_name);
 
    /**
     * \brief Destructor
diff --git a/include/lidar_obstacle_detector_alg_node.h b/include/lidar_obstacle_detector_alg_node.h
index 7b36575ca4903797dc2345a9cef41bd2b0dbeda8..0140d7c65373cf92bc672993400b630e89cbedd2 100644
--- a/include/lidar_obstacle_detector_alg_node.h
+++ b/include/lidar_obstacle_detector_alg_node.h
@@ -55,6 +55,11 @@ typedef struct
   bool init;
 }TLidarData;
 
+typedef struct{
+  double max;
+  double min;
+}TFilterRange;
+
 /**
  * \brief IRI ROS Specific Algorithm Class
  *
@@ -68,8 +73,10 @@ class LidarObstacleDetectorAlgNode : public algorithm_base::IriBaseAlgorithm<Lid
     ros::Publisher obstacles_img_publisher_;
     sensor_msgs::Image obstacles_img_Image_msg_;
 
-    ros::Publisher scan_out_publisher_;
-    sensor_msgs::LaserScan scan_out_LaserScan_msg_;
+    ros::Publisher obstacles_publisher_;
+    sensor_msgs::PointCloud2 obstacles_PointCloud2_msg_;
+    ros::Publisher free_space_publisher_;
+    sensor_msgs::PointCloud2 free_space_PointCloud2_msg_;
 
     // [subscriber attributes]
     ros::Subscriber pointcloud_in_subscriber_;
@@ -82,6 +89,7 @@ class LidarObstacleDetectorAlgNode : public algorithm_base::IriBaseAlgorithm<Lid
     pcl::PointCloud<pcl::PointXYZ>::Ptr transformed_input;
     bool new_point_cloud;
 
+
     // [service attributes]
 
     // [client attributes]
@@ -92,9 +100,11 @@ class LidarObstacleDetectorAlgNode : public algorithm_base::IriBaseAlgorithm<Lid
 
     tf::TransformListener listener;
 
+    unsigned int max_col_;
     cv::Mat inc_image;
     cv::Mat label_image;
     std::vector<TLidarData> lidar_data;
+    std::vector<TFilterRange> ranges;
 
    /**
     * \brief config variable
@@ -157,8 +167,13 @@ class LidarObstacleDetectorAlgNode : public algorithm_base::IriBaseAlgorithm<Lid
     */
     void addNodeDiagnostics(void);
 
+    pcl::PointCloud<pcl::PointXYZ>::Ptr transform_pointcloud(pcl::PointCloud<pcl::PointXYZ>::Ptr &input);
     bool get_point(unsigned int ring, unsigned int col,pcl::PointXYZ &point, double &range);
     double get_max_inclination(double current_angle,pcl::PointXYZ &point1,double range1,pcl::PointXYZ &point2,double range2);
+    void init_free_space(std::vector<bool> &free_space);
+    void compute_inclination(void);
+    void virtual_obstacles(std::vector<pcl::PointXYZ> &points,std::vector<bool> &free_space);
+    void farthest_obstacles(std::vector<pcl::PointXYZ> &points,std::vector<bool> &free_space);
 
     // [diagnostic functions]
     
diff --git a/launch/node.launch b/launch/node.launch
index 52a0653e55f2111e226d5e38f37f606e844fde98..85165f9dbca67ca3e55f89b5f5f5318e6ac13e4a 100644
--- a/launch/node.launch
+++ b/launch/node.launch
@@ -4,10 +4,10 @@
   <arg name="ns" default="ana"/>
   <arg name="node_name" default="lidar_obstacle_detector"/>
   <arg name="config_file" default="$(find iri_lidar_obstacle_detector)/config/default_config.yaml"/>
+  <arg name="ranges_file" default="$(find iri_lidar_obstacle_detector)/config/default_ranges.yaml"/>
   <arg name="output" default="screen"/>
   <arg name="launch_prefix" default=""/>
   <arg name="cloud_in" default="~pointcloud_in"/>
-  <arg name="base_frame_id" default="$(arg ns)/base_footprint"/>
 
   <group ns="$(arg ns)">
 
@@ -18,7 +18,7 @@
           launch-prefix="$(arg launch_prefix)">
       <remap from="~pointcloud_in" to="$(arg cloud_in)"/>
       <rosparam file="$(arg config_file)" command="load"/>
-      <param name="base_frame_id" value="$(arg base_frame_id)"/>
+      <rosparam file="$(arg ranges_file)" command="load"/>
     </node>
 
   </group>
diff --git a/launch/nodelet.launch b/launch/nodelet.launch
index dbdc16d2dc3afa178e1b1e47b4a3d70db9348e36..260193ed8076386b42b2a851806d988aad37fc20 100644
--- a/launch/nodelet.launch
+++ b/launch/nodelet.launch
@@ -5,10 +5,10 @@
   <arg name="node_name" default="lidar_obstacle_detector"/>
   <arg name="lidar_nodelet_manager" default="lidar_nodelet_manager"/>
   <arg name="config_file" default="$(find iri_lidar_obstacle_detector)/config/default_config.yaml"/>
+  <arg name="ranges_file" default="$(find iri_lidar_obstacle_detector)/config/default_ranges.yaml"/>
   <arg name="output" default="screen"/>
   <arg name="launch_prefix" default=""/>
   <arg name="cloud_in" default="~pointcloud_in"/>
-  <arg name="base_frame_id" default="$(arg ns)/base_footprint"/>
 
   <group ns="$(arg ns)">
 
@@ -19,7 +19,7 @@
           output="$(arg output)">
       <remap from="~pointcloud_in" to="$(arg cloud_in)"/>
       <rosparam file="$(arg config_file)" command="load"/>
-      <param name="base_frame_id" value="$(arg base_frame_id)"/>
+      <rosparam file="$(arg ranges_file)" command="load"/>
     </node>
 
   </group>
diff --git a/src/lidar_obstacle_detector_alg.cpp b/src/lidar_obstacle_detector_alg.cpp
index ae5be7e2542ff0ee7bb9854cd53b99147aafb2a0..4db9103ea9189412bb4f4fa2aa47cd810025975f 100644
--- a/src/lidar_obstacle_detector_alg.cpp
+++ b/src/lidar_obstacle_detector_alg.cpp
@@ -10,6 +10,33 @@ LidarObstacleDetectorAlgorithm::~LidarObstacleDetectorAlgorithm(void)
   pthread_mutex_destroy(&this->access_);
 }
 
+std::vector<std::string> LidarObstacleDetectorAlgorithm::get_strings(const ros::NodeHandle& nh, const std::string& param_name)
+{
+  XmlRpc::XmlRpcValue xml_array;
+  if(!nh.getParam(param_name,xml_array))
+  {
+    ROS_ERROR_STREAM("PointCloudAngleFilterAlg::get_strings: Could not find '" << param_name << "' parameter (namespace: " << nh.getNamespace() << ").");
+    return std::vector<std::string>();
+  }
+  if(xml_array.getType()!=XmlRpc::XmlRpcValue::TypeArray)
+  {
+    ROS_ERROR_STREAM("PointCloudAngleFilterAlg::get_strings: The '" << param_name << "' parameter is not an array (namespace: " << nh.getNamespace() << ").");
+    return std::vector<std::string>();
+  }
+
+  std::vector<std::string> out;
+  for(int i=0;i<xml_array.size();++i)
+  {
+    if(xml_array[i].getType()!=XmlRpc::XmlRpcValue::TypeString)
+    {
+      ROS_ERROR_STREAM("PointCloudAngleFilterAlg::get_strings: The '" << param_name << "' parameter contains a non-string element (namespace: " << nh.getNamespace() << ").");
+      return std::vector<std::string>();
+    }
+    out.push_back(static_cast<std::string>(xml_array[i]));
+  }
+  return out;
+}
+
 void LidarObstacleDetectorAlgorithm::config_update(Config& config, uint32_t level)
 {
   this->lock();
diff --git a/src/lidar_obstacle_detector_alg_node.cpp b/src/lidar_obstacle_detector_alg_node.cpp
index f7f25270c7d2617c5ec822f2f8548ee1d06e1a34..e1a7173ad2bd0ea2cb7c77bbd338ff503a0f16d8 100644
--- a/src/lidar_obstacle_detector_alg_node.cpp
+++ b/src/lidar_obstacle_detector_alg_node.cpp
@@ -15,8 +15,43 @@ LidarObstacleDetectorAlgNode::LidarObstacleDetectorAlgNode(const ros::NodeHandle
 
   // [init publishers]
   this->obstacles_img_publisher_ = this->public_node_handle_.advertise<sensor_msgs::Image>("obstacles_img/image_raw", 1);
-  this->scan_out_publisher_ = this->public_node_handle_.advertise<sensor_msgs::LaserScan>("scan_out", 1);
-  
+  this->obstacles_publisher_ = this->public_node_handle_.advertise<sensor_msgs::PointCloud2>("obstacles", 1);
+  this->obstacles_PointCloud2_msg_.height=1;
+  this->obstacles_PointCloud2_msg_.fields.resize(3);
+  this->obstacles_PointCloud2_msg_.fields[0].name="x";
+  this->obstacles_PointCloud2_msg_.fields[0].offset=0;
+  this->obstacles_PointCloud2_msg_.fields[0].datatype=7;
+  this->obstacles_PointCloud2_msg_.fields[0].count=1;
+  this->obstacles_PointCloud2_msg_.fields[1].name="y";
+  this->obstacles_PointCloud2_msg_.fields[1].offset=sizeof(float);
+  this->obstacles_PointCloud2_msg_.fields[1].datatype=7;
+  this->obstacles_PointCloud2_msg_.fields[1].count=1;
+  this->obstacles_PointCloud2_msg_.fields[2].name="z";
+  this->obstacles_PointCloud2_msg_.fields[2].offset=2*sizeof(float);
+  this->obstacles_PointCloud2_msg_.fields[2].datatype=7;
+  this->obstacles_PointCloud2_msg_.fields[2].count=1;
+  this->obstacles_PointCloud2_msg_.is_bigendian=true;
+  this->obstacles_PointCloud2_msg_.point_step=3*sizeof(float);
+  this->obstacles_PointCloud2_msg_.is_dense=true;
+  this->free_space_publisher_ = this->public_node_handle_.advertise<sensor_msgs::PointCloud2>("free_space", 1);
+  this->free_space_PointCloud2_msg_.height=1;
+  this->free_space_PointCloud2_msg_.fields.resize(3);
+  this->free_space_PointCloud2_msg_.fields[0].name="x";
+  this->free_space_PointCloud2_msg_.fields[0].offset=0;
+  this->free_space_PointCloud2_msg_.fields[0].datatype=7;
+  this->free_space_PointCloud2_msg_.fields[0].count=1;
+  this->free_space_PointCloud2_msg_.fields[1].name="y";
+  this->free_space_PointCloud2_msg_.fields[1].offset=sizeof(float);
+  this->free_space_PointCloud2_msg_.fields[1].datatype=7;
+  this->free_space_PointCloud2_msg_.fields[1].count=1;
+  this->free_space_PointCloud2_msg_.fields[2].name="z";
+  this->free_space_PointCloud2_msg_.fields[2].offset=2*sizeof(float);
+  this->free_space_PointCloud2_msg_.fields[2].datatype=7;
+  this->free_space_PointCloud2_msg_.fields[2].count=1;
+  this->free_space_PointCloud2_msg_.is_bigendian=true;
+  this->free_space_PointCloud2_msg_.point_step=3*sizeof(float);
+  this->free_space_PointCloud2_msg_.is_dense=false;
+
   // [init subscribers]
   this->pointcloud_in_subscriber_ = this->public_node_handle_.subscribe("pointcloud_in", 1, &LidarObstacleDetectorAlgNode::pointcloud_in_callback, this);
   pthread_mutex_init(&this->pointcloud_in_mutex_,NULL);
@@ -37,6 +72,33 @@ LidarObstacleDetectorAlgNode::~LidarObstacleDetectorAlgNode(void)
   pthread_mutex_destroy(&this->pointcloud_in_mutex_);
 }
 
+pcl::PointCloud<pcl::PointXYZ>::Ptr LidarObstacleDetectorAlgNode::transform_pointcloud(pcl::PointCloud<pcl::PointXYZ>::Ptr &input)
+{
+  bool success;
+
+  try{
+    if(this->input_header.frame_id!=this->config_.base_frame_id)
+    {
+      this->alg_.unlock();
+      success=this->listener.waitForTransform(this->input_header.frame_id,this->config_.base_frame_id,this->input_header.stamp,ros::Duration(3));
+      this->alg_.lock();
+      if(success)
+        pcl_ros::transformPointCloud(this->config_.base_frame_id,*this->input,*this->transformed_input,this->listener);
+      else
+      {
+        ROS_ERROR_STREAM("Impossible to transform pointcloud to " << this->config_.base_frame_id);
+        return NULL;
+      }
+      return this->transformed_input;
+    }
+    else
+      return this->input;
+  }catch (tf::TransformException ex){
+    ROS_ERROR("%s",ex.what());
+    return NULL;
+  }
+}
+
 bool LidarObstacleDetectorAlgNode::get_point(unsigned int ring, unsigned int col,pcl::PointXYZ &point, double &range)
 {
   if(this->lidar_data[col*this->config_.max_ring+ring].init)
@@ -97,339 +159,347 @@ double LidarObstacleDetectorAlgNode::get_max_inclination(double current_angle,pc
   return inc;
 }
 
-void LidarObstacleDetectorAlgNode::mainNodeThread(void)
+void LidarObstacleDetectorAlgNode::init_free_space(std::vector<bool> &free_space)
 {
-  bool ring_ordered = false;
-  static bool success;
-  static std::vector<float> farthest_ranges,virtual_ranges;
-  static double farthest_range,virtual_range;
-  static double new_x,new_y;
-  static unsigned int prev_col,next_col,prev_ring;
-  static unsigned int last_valid_ring,last_valid_col;
-  static int top,left,bottom,right;
-  static double start_angle=0.0,old_start_angle=0.0,start_angle_inc=0.0;
-  double angle=0.0,old_angle=0.0,angle_inc=0.0;
-  double current_angle;
-  unsigned int max_col;
-  // current ring points
-  static pcl::PointXYZ prev_ring_point,next_ring_point,point;
-  static double prev_ring_range,next_ring_range,range;
-  static bool point_valid,prev_ring_point_valid,next_ring_point_valid;
-  static double ring_inc1,ring_inc2;
-  // current col points
-  static pcl::PointXYZ prev_col_point,next_col_point;
-  static double prev_col_range,next_col_range,col_range;
-  static bool prev_col_point_valid,next_col_point_valid;
-  static double col_inc1,col_inc2;
-  // image variables
-  static cv::Mat traversable_space;
-  static cv::Mat positive_slope;
-  static cv::Mat stats_image;
-  static cv::Mat centroid_image;
-  static cv::Mat threshold;
-  static std::vector<std::vector<cv::Point> > contours;
-  static std::vector<cv::Vec4i> hierarchy;
-
-  this->alg_.lock();
-  if(this->new_point_cloud && this->scan_out_publisher_.getNumSubscribers()>0)
+  free_space.resize((2.0*3.14159)/this->config_.angle_resolution,true);
+  for(unsigned int i=0;i<this->ranges.size();i++)
   {
-    this->new_point_cloud=false;
-    //tf transform from sensor frame to robot base frame
-    try{
-      if(this->input_header.frame_id!=this->config_.base_frame_id)
-      {
-        this->alg_.unlock();
-        success=this->listener.waitForTransform(this->input_header.frame_id,this->config_.base_frame_id,this->input_header.stamp,ros::Duration(3));
-        this->alg_.lock();
-        if(success)
-          pcl_ros::transformPointCloud(this->config_.base_frame_id,*this->input,*this->transformed_input,this->listener);
-        else
-        {
-          ROS_ERROR_STREAM("Impossible to transform pointcloud to " << this->config_.base_frame_id);
-          this->alg_.unlock();
-          return;
-        }
-      }
-    }catch (tf::TransformException ex){
-      ROS_ERROR("%s",ex.what());
-      this->alg_.unlock();
-      return;
-    }
-    if (this->transformed_input->width > this->transformed_input->height)
-      ring_ordered = true;
-    if(this->transformed_input->height==1 || this->transformed_input->width==1)
+    for(unsigned int j=0;j<(2.0*3.14159)/this->config_.angle_resolution;j++)
     {
-      ROS_WARN("Droping pointcloud because it is not organized");
-      this->alg_.unlock();
-      return;
+      if((j*this->config_.angle_resolution-3.14159)<this->ranges[i].max && (j*this->config_.angle_resolution-3.14159)>this->ranges[i].min)
+        free_space[j]=false;
     }
+  }
+}
+
+void LidarObstacleDetectorAlgNode::compute_inclination(void)
+{
+  unsigned int prev_col,prev_ring,next_col;
+  double current_angle;
+  // previous point data
+  pcl::PointXYZ prev_ring_point,prev_col_point;
+  double prev_ring_range,prev_col_range;
+  bool prev_ring_point_valid,prev_col_point_valid;
+  // current point data
+  pcl::PointXYZ point;
+  double range,col_range;
+  bool point_valid;
+  double ring_inc1,ring_inc2;
+  double col_inc1,col_inc2;
+  // next point data
+  bool next_ring_point_valid,next_col_point_valid;
+  pcl::PointXYZ next_ring_point,next_col_point;
+  double next_ring_range,next_col_range;
+
+  for(unsigned int col=0;col<this->max_col_;col++)
+  {
+    if(col==0)
+      prev_col=this->max_col_-1;
+    else
+      prev_col=col-1;
+    if(col==this->max_col_-1)
+      next_col=0;
     else
+      next_col=col+1;
+    // robot position
+    prev_ring_point.x=0.0;
+    prev_ring_point.y=0.0;
+    prev_ring_point.z=0.0;
+    prev_ring_range=0.0;
+    prev_ring=-1;
+    prev_ring_point_valid=true;
+
+    for(unsigned int ring=0;ring<this->config_.max_ring-1;ring++)
     {
-      point=this->transformed_input->at(0,0);
-      if(!isnan(point.x) && !isnan(point.y) && !isnan(point.z))
-      {
-        old_start_angle=start_angle;
-        start_angle=atan2(point.y,point.x);
-        start_angle_inc=start_angle-old_start_angle;
-        if(start_angle_inc>3.14159)
-          start_angle_inc-=2.0*3.14159;
-      }
-      else
-      {
-        old_start_angle=start_angle;
-        start_angle+=start_angle_inc;
-      }
-      current_angle=start_angle;
-      // initialize sensor messages
-      this->scan_out_LaserScan_msg_.header=this->input_header;
-      this->scan_out_LaserScan_msg_.angle_min=-3.14159;
-      this->scan_out_LaserScan_msg_.angle_max=3.14159;
-      this->scan_out_LaserScan_msg_.angle_increment=(2.0*3.14159-start_angle_inc)/(ring_ordered? this->transformed_input->width: this->transformed_input->height);
-      this->scan_out_LaserScan_msg_.ranges.clear();
-      this->scan_out_LaserScan_msg_.range_min=this->config_.min_range;
-      this->scan_out_LaserScan_msg_.range_max=this->config_.max_range;
-      max_col=(unsigned int)((2.0*3.14159)/this->scan_out_LaserScan_msg_.angle_increment);
-      if(max_col>(ring_ordered? this->transformed_input->width: this->transformed_input->height))
-        max_col=(ring_ordered? this->transformed_input->width: this->transformed_input->height);
-
-      this->inc_image.create(this->config_.max_ring,max_col,CV_8UC1);
-      this->label_image.create(this->config_.max_ring,max_col,CV_32SC1);
-      this->lidar_data.clear();
-      this->lidar_data.resize(max_col*this->config_.max_ring);    
-      // mark all point as not initialized yet
-      for(unsigned int i=0;i<max_col*this->config_.max_ring;i++)
+      // current point
+      point_valid=this->get_point(ring,col,point,range);
+      // next ring
+      next_ring_point_valid=this->get_point(ring+1,col,next_ring_point,next_ring_range);
+      if(point_valid)
       {
-        this->lidar_data[i].init=false;
-        this->lidar_data[i].valid=false;
-      }
-      farthest_ranges.clear();
-      farthest_ranges.resize(max_col,this->config_.max_range);
-      virtual_ranges.clear();
-      virtual_ranges.resize(max_col,this->config_.max_range);
-
-      for(unsigned int col=0;col<max_col;col++)
-      {
-        if(col==0)
-          prev_col=max_col-1;
-        else
-          prev_col=col-1;
-        if(col==max_col-1)
-          next_col=0;
-        else
-          next_col=col+1;
-        // robot position
-        prev_ring_point.x=0.0;
-        prev_ring_point.y=0.0;
-        prev_ring_point.z=0.0;
-        prev_ring_range=0.0;
-        prev_ring=-1;
-        prev_ring_point_valid=true;
-
-        if (ring_ordered)
-          point=this->transformed_input->at(col,0);
-        else
-          point=this->transformed_input->at(0,col);
-        if(!isnan(point.x) && !isnan(point.y) && !isnan(point.z))
+        current_angle=atan2(point.y,point.x);
+        // previous column
+        if((prev_col_point_valid=this->get_point(ring,prev_col,prev_col_point,prev_col_range))==true)
         {
-          old_angle=angle;
-          angle=atan2(point.y,point.x);
-          if((angle-old_angle)>3.14159)
-            angle_inc+=(angle-old_angle)-2.0*3.14159;
-          else if((angle-old_angle)<-3.14159)
-            angle_inc+=(angle-old_angle)+2.0*3.14159;
-          else
-            angle_inc+=angle-old_angle;
+          col_range=sqrt(pow(prev_col_point.x-point.x,2.0)+pow(prev_col_point.y-point.y,2.0));
+          this->lidar_data[prev_col*this->config_.max_ring+ring].col_range=col_range;
+        }
+        // next column
+        if((next_col_point_valid=this->get_point(ring,next_col,next_col_point,next_col_range))==true)
+        {
+          col_range=sqrt(pow(point.x-next_col_point.x,2.0)+pow(point.y-next_col_point.y,2.0));
+          this->lidar_data[next_col*this->config_.max_ring+ring].col_range=col_range;
         }
-        for(unsigned int ring=0;ring<this->config_.max_ring-1;ring++)
+        if(prev_ring==-1 || prev_ring_point_valid)
         {
-          // current point
-          point_valid=this->get_point(ring,col,point,range);
-          // next ring
-          next_ring_point_valid=this->get_point(ring+1,col,next_ring_point,next_ring_range);
-          if(this->lidar_data[col*this->config_.max_ring+ring].valid)
+          ring_inc1=this->get_max_inclination(current_angle,point,range,prev_ring_point,prev_ring_range);
+          if(next_ring_point_valid)
           {
-            // previous column
-            if((prev_col_point_valid=this->get_point(ring,prev_col,prev_col_point,prev_col_range))==true)
-            {
-              col_range=sqrt(pow(prev_col_point.x-point.x,2.0)+pow(prev_col_point.y-point.y,2.0)); 
-              this->lidar_data[prev_col*this->config_.max_ring+ring].col_range=col_range;
-            }
-            // next column
-            if((next_col_point_valid=this->get_point(ring,next_col,next_col_point,next_col_range))==true)
-            {
-              col_range=sqrt(pow(point.x-next_col_point.x,2.0)+pow(point.y-next_col_point.y,2.0)); 
-              this->lidar_data[col*this->config_.max_ring+ring].col_range=col_range;//next_col????????????????
-            }
-            if(prev_ring==-1 || prev_ring_point_valid)
-            {
-              ring_inc1=this->get_max_inclination(current_angle,point,range,prev_ring_point,prev_ring_range);
-              if(next_ring_point_valid)
-              {
-                ring_inc2=this->get_max_inclination(current_angle,next_ring_point,next_ring_range,point,range);
-                if(fabs(ring_inc1)<fabs(ring_inc2))
-                  ring_inc1=ring_inc2;
-              }
-            }
-            else
-            {
-              if(next_ring_point_valid)
-                ring_inc1=this->get_max_inclination(current_angle,next_ring_point,next_ring_range,point,range);
-              else
-                ring_inc1=0.0;
-            }
-            if(prev_col_point_valid)
-            {
-              col_inc1=atan2(point.z-prev_col_point.z,prev_col_range)*180/3.14159;
-              if(next_col_point_valid)
-              {
-                col_inc2=atan2(next_col_point.z-point.z,col_range)*180/3.14159;
-                if(fabs(col_inc1)<fabs(col_inc2))
-                  col_inc1=col_inc2;
-              }
-            }
-            else
-            {
-              if(next_col_point_valid)
-                col_inc1=atan2(next_col_point.z-point.z,col_range)*180/3.14159;
-              else
-                col_inc1=0.0;
-            }
-            this->lidar_data[col*this->config_.max_ring+ring].col_inc=col_inc1;
-            this->lidar_data[col*this->config_.max_ring+ring].ring_inc=ring_inc1;
-            if(fabs(col_inc1)>fabs(ring_inc1))
-              this->inc_image.at<unsigned char>(ring,col,0)=((signed char)col_inc1)+128;
-            else
-              this->inc_image.at<unsigned char>(ring,col,0)=((signed char)ring_inc1)+128;
+            ring_inc2=this->get_max_inclination(current_angle,next_ring_point,next_ring_range,point,range);
+            if(fabs(ring_inc1)<fabs(ring_inc2))
+              ring_inc1=ring_inc2;
           }
-          else
-            this->inc_image.at<unsigned char>(ring,col,0)=0;
-          // next iteration
-          prev_ring_point=point;
-          prev_ring_range=range;
-          prev_ring++;
-          prev_ring_point_valid=point_valid;
         }
-        current_angle+=(2.0*3.14159)/max_col;
-      }
-
-      if(this->config_.virtual_enable)
-      {
-        cv::inRange(this->inc_image,cv::Scalar(128-((unsigned char)(this->config_.max_inc*180.0/3.14159))),cv::Scalar(128+((unsigned char)(this->config_.max_inc*180.0/3.14159))),traversable_space);
-        cv::morphologyEx(traversable_space,traversable_space,cv::MORPH_CLOSE,cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3)));
-      }
-      if(this->config_.farthest_enable)
-      {
-        cv::inRange(this->inc_image,cv::Scalar(131),cv::Scalar(255),positive_slope);
-        cv::morphologyEx(positive_slope,positive_slope,cv::MORPH_OPEN,cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3)));
-      }
-      for(unsigned int col=0;col<max_col;col++)
-      {
-        last_valid_col=col;
-        if(this->config_.farthest_enable)
+        else
         {
-          farthest_range=0.0;
-          for(unsigned int ring=0;ring<this->config_.farthest_num_rings;ring++)
-          { 
-            if(this->lidar_data[col*this->config_.max_ring+ring].valid)
-            {
-              farthest_range=this->lidar_data[col*this->config_.max_ring+ring].range;
-              last_valid_ring=ring;
-            }
-          }
-          if(farthest_range==0.0)
-            farthest_ranges[col]=this->config_.max_range;
-          else if(positive_slope.at<unsigned int>(last_valid_ring,last_valid_col,0)!=0)
-            farthest_ranges[col]=this->config_.max_range;
+          if(next_ring_point_valid)
+            ring_inc1=this->get_max_inclination(current_angle,next_ring_point,next_ring_range,point,range);
           else
-            farthest_ranges[col]=farthest_range;
+            ring_inc1=0.0;
         }
-        if(this->config_.virtual_enable)
+        if(prev_col_point_valid)
         {
-          for(unsigned int ring=0;ring<this->config_.max_ring-1;ring++)
+          col_inc1=atan2(point.z-prev_col_point.z,prev_col_range)*180/3.14159;
+          if(next_col_point_valid)
           {
-            if(this->lidar_data[col*this->config_.max_ring+ring].valid)
-            {
-              if(this->lidar_data[col*this->config_.max_ring+ring].point.z<-this->config_.virtual_floor_threshold)
-              { 
-                if(traversable_space.at<unsigned int>(ring,col,0)==0)
-                {
-                  new_x=this->lidar_data[col*this->config_.max_ring+ring].point.x*this->config_.sensor_height/(this->config_.sensor_height-this->lidar_data[col*this->config_.max_ring+ring].point.z);
-                  new_y=this->lidar_data[col*this->config_.max_ring+ring].point.y*new_x/this->lidar_data[col*this->config_.max_ring+ring].point.x;
-                  virtual_range=sqrt(pow(new_x,2.0)+pow(new_y,2.0));
-                  if(virtual_range<virtual_ranges[col])
-                    virtual_ranges[col]=virtual_range;
-                }
-              }
-            }
+            col_inc2=atan2(next_col_point.z-point.z,col_range)*180/3.14159;
+            if(fabs(col_inc1)<fabs(col_inc2))
+              col_inc1=col_inc2;
           }
-        } 
-      }
-
-      if(this->config_.farthest_enable)
-        this->scan_out_LaserScan_msg_.ranges=farthest_ranges;
-      else
-        this->scan_out_LaserScan_msg_.ranges.resize(max_col,this->config_.max_range);
-      if(this->config_.virtual_enable)
-      {
-        for(unsigned int i=0;i<this->scan_out_LaserScan_msg_.ranges.size();i++)
+        }
+        else
         {
-          if(this->scan_out_LaserScan_msg_.ranges[i]>virtual_ranges[i])
-            this->scan_out_LaserScan_msg_.ranges[i]=virtual_ranges[i];
+          if(next_col_point_valid)
+            col_inc1=atan2(next_col_point.z-point.z,col_range)*180/3.14159;
+          else
+            col_inc1=0.0;
         }
+        this->lidar_data[col*this->config_.max_ring+ring].col_inc=col_inc1;
+        this->lidar_data[col*this->config_.max_ring+ring].ring_inc=ring_inc1;
+        if(fabs(col_inc1)>fabs(ring_inc1))
+          this->inc_image.at<unsigned char>(ring,col,0)=((signed char)col_inc1)+128;
+        else
+          this->inc_image.at<unsigned char>(ring,col,0)=((signed char)ring_inc1)+128;
       }
-      cv::inRange(this->inc_image,cv::Scalar(128+((unsigned char)(this->config_.max_inc*180.0/3.14159))),cv::Scalar(255),threshold);
-      cv::morphologyEx(threshold,threshold,cv::MORPH_CLOSE,cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3)));
-      cv::connectedComponentsWithStats(threshold,this->label_image,stats_image,centroid_image,8);
-      for(int i=1;i<stats_image.rows;i++)
+      else
+        this->inc_image.at<unsigned char>(ring,col,0)=0;
+      // next iteration
+      prev_ring_point=point;
+      prev_ring_range=range;
+      prev_ring++;
+      prev_ring_point_valid=point_valid;
+    }
+  }
+}
+
+void LidarObstacleDetectorAlgNode::virtual_obstacles(std::vector<pcl::PointXYZ> &points,std::vector<bool> &free_space)
+{
+  cv::Mat traversable_space;
+  unsigned int last_valid_col;
+
+  cv::inRange(this->inc_image,cv::Scalar(128-((unsigned char)(this->config_.min_inc*180.0/3.14159))),cv::Scalar(128+((unsigned char)(this->config_.min_inc*180.0/3.14159))),traversable_space);
+  cv::morphologyEx(traversable_space,traversable_space,cv::MORPH_CLOSE,cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3)));
+
+  for(unsigned int col=0;col<this->max_col_;col++)
+  {
+    last_valid_col=col;
+    for(unsigned int ring=0;ring<this->config_.max_ring-1;ring++)
+    {
+      if(this->lidar_data[col*this->config_.max_ring+ring].valid)
       {
-        if(stats_image.at<int>(i,cv::CC_STAT_AREA)>this->config_.inc_area_filter)
+        if(this->lidar_data[col*this->config_.max_ring+ring].point.z<-this->config_.virtual_floor_threshold)
         {
-          top=stats_image.at<int>(i,cv::CC_STAT_TOP);
-          left=stats_image.at<int>(i,cv::CC_STAT_LEFT);
-          bottom=top+stats_image.at<int>(i,cv::CC_STAT_HEIGHT);
-          right=left+stats_image.at<int>(i,cv::CC_STAT_WIDTH);
-          cv::findContours(threshold(cv::Range(top,bottom),cv::Range(left,right)),contours,hierarchy,CV_RETR_TREE,CV_CHAIN_APPROX_NONE);
-          for(unsigned int j=0;j<contours.size();j++)
+          if(traversable_space.at<unsigned int>(ring,col,0)!=0)
           {
-            for(unsigned int k=0;k<contours[j].size();k++)
-            {
-              contours[j][k].x+=left;
-              contours[j][k].y+=top;
-              if(this->scan_out_LaserScan_msg_.ranges[contours[j][k].x]>this->lidar_data[contours[j][k].x*this->config_.max_ring+contours[j][k].y].range)
-                this->scan_out_LaserScan_msg_.ranges[contours[j][k].x]=this->lidar_data[contours[j][k].x*this->config_.max_ring+contours[j][k].y].range;
-            }
+            pcl::PointXYZ point;
+            point.x=this->lidar_data[col*this->config_.max_ring+ring].point.x*this->config_.sensor_height/(this->config_.sensor_height-this->lidar_data[col*this->config_.max_ring+ring].point.z);
+            point.y=this->lidar_data[col*this->config_.max_ring+ring].point.y*point.x/this->lidar_data[col*this->config_.max_ring+ring].point.x;
+            point.z=this->config_.sensor_height;
+            points.push_back(point);
+            break;
           }
         }
       }
-      for(unsigned int i=0;i<this->scan_out_LaserScan_msg_.ranges.size();i++)
+    }
+  }
+  if(this->config_.debug && this->obstacles_img_publisher_.getNumSubscribers()>0)
+  {
+    this->cv_image_=cv_bridge::CvImage(this->input_header, sensor_msgs::image_encodings::MONO8,traversable_space);
+    this->cv_image_.toImageMsg(this->obstacles_img_Image_msg_); // from cv_bridge to sensor_msgs::Image
+    this->obstacles_img_publisher_.publish(this->obstacles_img_Image_msg_);
+  }
+}
+
+void LidarObstacleDetectorAlgNode::farthest_obstacles(std::vector<pcl::PointXYZ> &points,std::vector<bool> &free_space)
+{
+  cv::Mat positive_slope;
+  unsigned int last_valid_col,last_valid_ring;
+  double farthest_range;
+
+  cv::inRange(this->inc_image,cv::Scalar(131),cv::Scalar(255),positive_slope);
+  cv::morphologyEx(positive_slope,positive_slope,cv::MORPH_OPEN,cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3)));
+
+  for(unsigned int col=0;col<this->max_col_;col++)
+  {
+    last_valid_col=col;
+    farthest_range=0.0;
+    for(unsigned int ring=0;ring<this->config_.farthest_num_rings;ring++)
+    {
+      if(this->lidar_data[col*this->config_.max_ring+ring].valid)
       {
-        if(this->scan_out_LaserScan_msg_.ranges[i]>this->config_.max_range)
-          this->scan_out_LaserScan_msg_.ranges[i]=this->config_.max_range;
+        farthest_range=this->lidar_data[col*this->config_.max_ring+ring].range;
+        last_valid_ring=ring;
       }
-      if(angle_inc<0.0)
+    }
+    if(farthest_range!=0.0 && positive_slope.at<unsigned int>(last_valid_ring,last_valid_col,0)==0)
+      points.push_back(this->lidar_data[last_valid_col*this->config_.max_ring+last_valid_ring].point);
+    // TO DO: clear free space accordingly
+  }
+  /*
+  if(this->config_.debug && this->obstacles_img_publisher_.getNumSubscribers()>0)
+  {
+    this->cv_image_=cv_bridge::CvImage(this->input_header, sensor_msgs::image_encodings::MONO8,positive_slope);
+    this->cv_image_.toImageMsg(this->obstacles_img_Image_msg_); // from cv_bridge to sensor_msgs::Image
+    this->obstacles_img_publisher_.publish(this->obstacles_img_Image_msg_);
+  }
+  */
+}
+
+void LidarObstacleDetectorAlgNode::mainNodeThread(void)
+{
+  bool ring_ordered = false;
+  int top,left,bottom,right;
+  double current_angle;
+  // image variables
+  cv::Mat stats_image;
+  cv::Mat centroid_image;
+  cv::Mat threshold;
+  cv::Mat label_image;
+  std::vector<std::vector<cv::Point> > contours;
+  std::vector<cv::Vec4i> hierarchy;
+
+  this->alg_.lock();
+  if(this->new_point_cloud && this->obstacles_publisher_.getNumSubscribers()>0)
+  {
+    this->new_point_cloud=false;
+    //tf transform from sensor frame to robot base frame
+    if((this->transformed_input=this->transform_pointcloud(this->input))==NULL)
+    {
+      this->alg_.unlock();
+      return;
+    }
+    // check input format
+    if(this->transformed_input->height==1 || this->transformed_input->width==1)
+    {
+      ROS_WARN("Droping pointcloud because it is not organized");
+      this->alg_.unlock();
+      return;
+    }
+    if(this->transformed_input->width > this->transformed_input->height)
+      ring_ordered = true;
+    // initialize local variables
+    if(ring_ordered)
+      this->max_col_=this->transformed_input->width;
+    else 
+      this->max_col_=this->transformed_input->height;
+    this->inc_image.create(this->config_.max_ring,this->max_col_,CV_8UC1);
+    this->label_image.create(this->config_.max_ring,this->max_col_,CV_32SC1);
+    this->lidar_data.clear();
+    this->lidar_data.resize(this->max_col_*this->config_.max_ring);    
+    // mark all point as not initialized yet
+    for(unsigned int i=0;i<this->max_col_*this->config_.max_ring;i++)
+    {
+      this->lidar_data[i].init=false;
+      this->lidar_data[i].valid=false;
+    }
+    /* initialize free space */ 
+    std::vector<bool> free_space;
+    this->init_free_space(free_space);
+
+    // compute inclinations
+    this->compute_inclination();
+
+    cv::inRange(this->inc_image,cv::Scalar(128+((unsigned char)(this->config_.max_inc*180.0/3.14159))),cv::Scalar(255),threshold);
+    cv::morphologyEx(threshold,threshold,cv::MORPH_CLOSE,cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3)));
+    cv::connectedComponentsWithStats(threshold,this->label_image,stats_image,centroid_image,8);
+
+    /*
+    if(this->config_.debug && this->obstacles_img_publisher_.getNumSubscribers()>0)
+    {
+      this->cv_image_=cv_bridge::CvImage(this->input_header, sensor_msgs::image_encodings::MONO8,threshold);
+      this->cv_image_.toImageMsg(this->obstacles_img_Image_msg_); // from cv_bridge to sensor_msgs::Image
+      this->obstacles_img_publisher_.publish(this->obstacles_img_Image_msg_);
+    }
+    */
+    std::vector<pcl::PointXYZ> points;
+    for(int i=1;i<stats_image.rows;i++)
+    {
+      if(stats_image.at<int>(i,cv::CC_STAT_AREA)>this->config_.inc_area_filter)
       {
-        this->scan_out_LaserScan_msg_.angle_min=0.0;
-        this->scan_out_LaserScan_msg_.angle_max=2.0*3.14159;
-        std::vector<float> ranges=this->scan_out_LaserScan_msg_.ranges;
-        this->scan_out_LaserScan_msg_.ranges.clear();
-        this->scan_out_LaserScan_msg_.ranges.resize(max_col);
-        unsigned int num_swap_samples=((start_angle+3.14159)/this->scan_out_LaserScan_msg_.angle_increment);
-        for(unsigned int i=0;i<ranges.size();i++)
+        top=stats_image.at<int>(i,cv::CC_STAT_TOP);
+        left=stats_image.at<int>(i,cv::CC_STAT_LEFT);
+        bottom=top+stats_image.at<int>(i,cv::CC_STAT_HEIGHT);
+        right=left+stats_image.at<int>(i,cv::CC_STAT_WIDTH);
+        cv::findContours(threshold(cv::Range(top,bottom),cv::Range(left,right)),contours,hierarchy,CV_RETR_TREE,CV_CHAIN_APPROX_NONE);
+        double max_angle=-std::numeric_limits<double>::max();
+        double min_angle=std::numeric_limits<double>::max();
+        for(unsigned int j=0;j<contours.size();j++)
         {
-          if(i<=num_swap_samples)
-            this->scan_out_LaserScan_msg_.ranges[i]=ranges[num_swap_samples-i];
-          else
-            this->scan_out_LaserScan_msg_.ranges[i]=ranges[max_col-i+num_swap_samples];
+          for(unsigned int k=0;k<contours[j].size();k++)
+          {
+            contours[j][k].x+=left;
+            contours[j][k].y+=top;
+            points.push_back(this->lidar_data[contours[j][k].x*this->config_.max_ring+contours[j][k].y].point);
+            current_angle=atan2(this->lidar_data[contours[j][k].x*this->config_.max_ring+contours[j][k].y].point.y,this->lidar_data[contours[j][k].x*this->config_.max_ring+contours[j][k].y].point.x);
+            if(current_angle>max_angle)
+              max_angle=current_angle;
+            if(current_angle<min_angle)
+              min_angle=current_angle;
+          }
         }
+        for(unsigned int i=((min_angle+3.14159)/this->config_.angle_resolution);i<((max_angle+3.14159)/this->config_.angle_resolution);i++)
+          free_space[i]=false;
       }
-      if(this->config_.debug && this->obstacles_img_publisher_.getNumSubscribers()>0)
+    }
+    if(this->config_.virtual_enable)
+      this->virtual_obstacles(points,free_space);
+    if(this->config_.farthest_enable)
+      this->farthest_obstacles(points,free_space);
+    this->obstacles_PointCloud2_msg_.header.frame_id=this->config_.base_frame_id;
+    this->obstacles_PointCloud2_msg_.header.stamp=this->input_header.stamp;
+    this->obstacles_PointCloud2_msg_.width=points.size();
+    this->obstacles_PointCloud2_msg_.row_step=this->obstacles_PointCloud2_msg_.width*this->obstacles_PointCloud2_msg_.point_step;
+    this->obstacles_PointCloud2_msg_.data.resize(this->obstacles_PointCloud2_msg_.row_step);
+    for(unsigned int i=0;i<points.size();i++)
+    {
+      memcpy(&(this->obstacles_PointCloud2_msg_.data.data()[i*this->obstacles_PointCloud2_msg_.point_step]),(unsigned char *)&points[i].x,sizeof(float));
+      memcpy(&(this->obstacles_PointCloud2_msg_.data.data()[i*this->obstacles_PointCloud2_msg_.point_step+sizeof(float)]),(unsigned char *)&points[i].y,sizeof(float));
+      memcpy(&(this->obstacles_PointCloud2_msg_.data.data()[i*this->obstacles_PointCloud2_msg_.point_step+2*sizeof(float)]),(unsigned char *)&points[i].z,sizeof(float));
+    }
+
+    points.clear();
+    for(unsigned int i=0;i<(2.0*3.14159)/this->config_.angle_resolution;i++)
+    {
+      if(free_space[i])
       {
-        this->cv_image_=cv_bridge::CvImage(this->input_header, sensor_msgs::image_encodings::MONO8,positive_slope);
-        this->cv_image_.toImageMsg(this->obstacles_img_Image_msg_); // from cv_bridge to sensor_msgs::Image
-        this->obstacles_img_publisher_.publish(this->obstacles_img_Image_msg_);
+        pcl::PointXYZ point;
+        point.x=this->config_.max_range*cos(i*this->config_.angle_resolution-3.14159);
+        point.y=this->config_.max_range*sin(i*this->config_.angle_resolution-3.14159);
+        point.z=this->config_.sensor_height;
+        points.push_back(point);
       }
-      this->scan_out_publisher_.publish(this->scan_out_LaserScan_msg_);
     }
+    this->free_space_PointCloud2_msg_.header.frame_id=this->config_.base_frame_id;
+    this->free_space_PointCloud2_msg_.header.stamp=this->input_header.stamp;
+    this->free_space_PointCloud2_msg_.width=points.size();
+    this->free_space_PointCloud2_msg_.row_step=this->free_space_PointCloud2_msg_.width*this->free_space_PointCloud2_msg_.point_step;
+    this->free_space_PointCloud2_msg_.data.resize(this->free_space_PointCloud2_msg_.row_step);
+    for(unsigned int i=0;i<points.size();i++)
+    {
+      memcpy(&(this->free_space_PointCloud2_msg_.data.data()[i*this->free_space_PointCloud2_msg_.point_step]),(unsigned char *)&points[i].x,sizeof(float));
+      memcpy(&(this->free_space_PointCloud2_msg_.data.data()[i*this->free_space_PointCloud2_msg_.point_step+sizeof(float)]),(unsigned char *)&points[i].y,sizeof(float));
+      memcpy(&(this->free_space_PointCloud2_msg_.data.data()[i*this->free_space_PointCloud2_msg_.point_step+2*sizeof(float)]),(unsigned char *)&points[i].z,sizeof(float));
+    }
+
+    /*
+    for(unsigned int i=0;i<this->scan_out_LaserScan_msg_.ranges.size();i++)
+    {
+      if(this->scan_out_LaserScan_msg_.ranges[i]>this->config_.max_range)
+        this->scan_out_LaserScan_msg_.ranges[i]=this->config_.max_range;
+    }
+    */
+    this->obstacles_publisher_.publish(this->obstacles_PointCloud2_msg_);
+    this->free_space_publisher_.publish(this->free_space_PointCloud2_msg_);
   }
 
   this->alg_.unlock();
@@ -477,7 +547,28 @@ void LidarObstacleDetectorAlgNode::pointcloud_in_mutex_exit(void)
 
 void LidarObstacleDetectorAlgNode::node_config_update(Config &config, uint32_t level)
 {
+  std::vector<std::string> range_names;
+  TFilterRange new_range;
+  static bool first=true;
+  double max,min;
+
   this->alg_.lock();
+  if(first)
+  {
+    first=false;
+    this->ranges.clear();
+    range_names=this->alg_.get_strings(this->public_node_handle_,"ranges");
+    for(unsigned int i=0;i<range_names.size();i++)
+    {
+      ros::NodeHandle range_nh(this->public_node_handle_,range_names[i]);
+      new_range.max=0;
+      new_range.min=0;
+      range_nh.getParam("max",new_range.max);
+      range_nh.getParam("min",new_range.min);
+      this->ranges.push_back(new_range);
+    }
+  }
+
   this->config_=config;
   this->alg_.unlock();
 }