scans_2_odom_alg_node.cpp
Go to the documentation of this file.
00001 #include "scans_2_odom_alg_node.h"
00002 
00003 Scans2OdomAlgNode::Scans2OdomAlgNode(void) :
00004 algorithm_base::IriBaseAlgorithm<Scans2OdomAlgorithm>()
00005 {
00006   //init class attributes if necessary
00007   //this->loop_rate_ = 2;//in [Hz]
00008   
00009   // [init publishers]
00010   
00011   // [init subscribers]
00012   this->scan_subscriber_ = this->public_node_handle_.subscribe("scan", 10, &Scans2OdomAlgNode::scan_callback, this);
00013   
00014   // [init services]
00015   this->get_link_server_ = this->public_node_handle_.advertiseService("get_link", &Scans2OdomAlgNode::get_linkCallback, this);
00016   
00017   // [init clients]
00018   get_relative_pose_client_ = this->public_node_handle_.serviceClient<iri_laser_icp::GetRelativePose>("get_relative_pose");
00019   
00020   // [init action servers]
00021   
00022   // [init action clients]
00023 }
00024 
00025 Scans2OdomAlgNode::~Scans2OdomAlgNode(void)
00026 {
00027   // [free dynamic memory]
00028 }
00029 
00030 void Scans2OdomAlgNode::mainNodeThread(void)
00031 {
00032   // [fill msg structures]
00033   
00034   // [fill srv structure and make request to the server]
00035   
00036   // [fill action structure and make request to the action server]
00037   
00038   // [publish messages]
00039 }
00040 
00041 /*  [subscriber callbacks] */
00042 void Scans2OdomAlgNode::scan_callback(const sensor_msgs::LaserScan::ConstPtr& msg)
00043 {
00044   ROS_DEBUG("SCANS 2 ODOM New LaserScan Message Received");
00045   //ROS_INFO_STREAM(msg->header.covariance);
00046   
00047   // LASER SCAN BUFFER
00048   // reserve space if needed
00049   if (discarded_laser_scan_buffer_.size() == discarded_laser_scan_buffer_.capacity())
00050     discarded_laser_scan_buffer_.reserve(int(discarded_laser_scan_buffer_.size()) + 1000);
00051   
00052   discarded_laser_scan_buffer_.push_back(*msg);
00053   
00054   ROS_DEBUG("SCANS 2 ODOM New Laser Scan buffered");
00055   
00056 }
00057 
00058 /*  [service callbacks] */
00059 bool Scans2OdomAlgNode::get_linkCallback(iri_poseslam::GetLink::Request &req, iri_poseslam::GetLink::Response &res)
00060 {
00061   // reserve space if needed
00062   if (laser_scan_buffer_.size() == laser_scan_buffer_.capacity())
00063     laser_scan_buffer_.reserve(int(laser_scan_buffer_.size()) + 1000);
00064   
00065   res.success = false;
00066   res.end = false;
00067   
00068   // First callback (ask for first laser scan header)
00069   if (req.current_step == 0 && req.with_step == 0)
00070   {
00071     if (discarded_laser_scan_buffer_.size() > 0)
00072     {
00073       // give the first laser scan
00074       res.odom.header = discarded_laser_scan_buffer_.front().header;
00075       res.success = true;
00076       
00077       // Add into the laser_scan_buffer_
00078       laser_scan_buffer_.push_back(discarded_laser_scan_buffer_.front());
00079       
00080       // erase the first laser scan
00081       discarded_laser_scan_buffer_.erase(discarded_laser_scan_buffer_.begin(), discarded_laser_scan_buffer_.begin() + 1);
00082       
00083       ROS_INFO("SCANS 2 ODOM first laser scan header given");
00084     }
00085   }
00086   
00087   // ICP betwen 2 states
00088   else
00089   {
00090     //ROS_INFO("SCANS 2 ODOM compute link: %i with %i | laser buffer size = %i", int(req.current_step), int(req.with_step), int(laser_scan_buffer_.size()));
00091     
00092     if (discarded_laser_scan_buffer_.size() == 0)
00093     {
00094       if (laser_scan_buffer_.size() > 0)
00095         ROS_DEBUG("SCANS 2 ODOM there is no more laser scans for now...");
00096       
00097       res.end = true;
00098     }
00099     else
00100     {
00101       // ODOMETRY LINK
00102       if (req.current_step < req.with_step)
00103       {
00104         if (online_mode)
00105           get_relative_pose_srv_.request.scan_sens = discarded_laser_scan_buffer_.back();  // last laser scan
00106         else
00107           get_relative_pose_srv_.request.scan_sens = discarded_laser_scan_buffer_.front();  // next laser scan
00108             
00109         get_relative_pose_srv_.request.scan_ref = laser_scan_buffer_.at(req.current_step); // actual laser scan
00110         get_relative_pose_srv_.request.prior_d.position.x = 0;
00111         get_relative_pose_srv_.request.prior_d.position.y = 0;
00112         get_relative_pose_srv_.request.prior_d.position.z = 0;
00113         get_relative_pose_srv_.request.prior_d.orientation.x = 0;
00114         get_relative_pose_srv_.request.prior_d.orientation.y = 0;
00115         get_relative_pose_srv_.request.prior_d.orientation.z = 0;
00116         
00117         // call ICP
00118         if (get_relative_pose_client_.call(get_relative_pose_srv_))
00119         {
00120           ROS_DEBUG("SCANS 2 ODOM ICP in odometry success!");
00121           geometry_msgs::PoseWithCovarianceStamped d = get_relative_pose_srv_.response.pose_rel;
00122           
00123           // OFFLINE MODE
00124           if (!online_mode)
00125           {
00126             // Erase the computed Laser Scan from discarded_laser_scan_buffer_
00127             discarded_laser_scan_buffer_.erase(discarded_laser_scan_buffer_.begin(), discarded_laser_scan_buffer_.begin() + 1);
00128           }
00129           
00130           // ONLINE MODE
00131           else
00132           {
00133             // Too much covariance
00134             if ((d.pose.covariance.at(0)  > bad_cov_thres ||
00135               d.pose.covariance.at(7)  > bad_cov_thres ||
00136               d.pose.covariance.at(17) > bad_cov_thres))
00137             {
00138               // Try to reconstruct with intermediate discarded laser scans
00139               uint idx_LS = uint(discarded_laser_scan_buffer_.size() - 1);
00140               
00141               ROS_WARN("SCANS 2 ODOM ICP given a too much covariance odometry, trying to reconstruct with the %i discarded poses", idx_LS + 1);
00142               bool good_cov = false;
00143               
00144               while (!good_cov && idx_LS != 0) // iteration half by half of discarded laser scans
00145               {
00146                 // new ICP request in a half distance laser scan
00147                 idx_LS = int(idx_LS / 2);
00148                 get_relative_pose_srv_.request.scan_sens = discarded_laser_scan_buffer_.at(idx_LS);
00149                 
00150                 if (get_relative_pose_client_.call(get_relative_pose_srv_))
00151                 {
00152                   d = get_relative_pose_srv_.response.pose_rel;
00153                   
00154                   if (idx_LS > 0  && (d.pose.covariance.at(0)  > bad_cov_thres
00155                     ||  d.pose.covariance.at(7)  > bad_cov_thres
00156                     ||  d.pose.covariance.at(17) > bad_cov_thres))
00157                   {
00158                     ROS_INFO("SCANS 2 ODOM LaserScan %i still too much covariance - try with number %i", idx_LS + 1, int(idx_LS / 2) + 1);
00159                   }
00160                   else
00161                   {
00162                     if (idx_LS == 0) // while consecutive laser scan and still too much covariance ICP
00163                       ROS_WARN("SCANS 2 ODOM ICP given too much covariance even in consecutive laser scans. Consecutive scans computed.");
00164                     else
00165                       ROS_INFO("SCANS 2 ODOM LaserScan %i works! adding it into the laser scan buffer", idx_LS + 1);
00166                     
00167                     good_cov = true;
00168                     
00169                     // Erase the idx_LS and the previous ones from the discarded_laser_scan_buffer_
00170                     discarded_laser_scan_buffer_.erase(discarded_laser_scan_buffer_.begin(), discarded_laser_scan_buffer_.begin() + idx_LS + 1);
00171                     
00172                     ROS_INFO("SCANS 2 ODOM Removed discarded laser scans until %i. New discarded bufer size: %u", idx_LS + 1, uint(discarded_laser_scan_buffer_.size()));
00173                   }
00174                 }
00175               }
00176             }
00177             else
00178               // Erase all laser scans of discarded_laser_scan_buffer_
00179               discarded_laser_scan_buffer_.clear();
00180           }
00181           
00182           // Results
00183           Vector3d link_ICP = pose_2_vector(d.pose.pose);
00184           Matrix3d link_ICP_cov = covariance_2_matrix(d.pose);
00185           
00186           // CHANGE FRAME TO BASE FOOTPRINT
00187           change_2_base_footprint_frame(link_ICP, link_ICP_cov);
00188 
00189           // COVARIANCE CORRECTION
00190           link_ICP_cov = link_ICP_cov * ICP_covariance_correction_factor;
00191           
00192           res.odom.header = get_relative_pose_srv_.request.scan_sens.header;
00193           res.odom.pose = eigen_2_posewithcovariance(link_ICP, link_ICP_cov);
00194           res.success = true;
00195           
00196           // Add the laser scan in the buffer
00197           laser_scan_buffer_.push_back(get_relative_pose_srv_.request.scan_sens);
00198         }
00199         else
00200         {
00201           ROS_WARN("SCANS 2 ODOM ICP failed in a odometry");
00202           res.success = false;
00203         }
00204       }
00205       
00206       // LOOP CLOSURE LINK
00207       else
00208       {
00209         get_relative_pose_srv_.request.scan_ref = laser_scan_buffer_.at(req.with_step);
00210         get_relative_pose_srv_.request.scan_sens = laser_scan_buffer_.at(req.current_step);
00211         get_relative_pose_srv_.request.prior_d = req.prior_d;
00212         
00213         // call ICP
00214         if (get_relative_pose_client_.call(get_relative_pose_srv_))
00215         {
00216           Vector3d link_ICP = pose_2_vector(get_relative_pose_srv_.response.pose_rel.pose.pose);
00217           Matrix3d link_ICP_cov = covariance_2_matrix(get_relative_pose_srv_.response.pose_rel.pose);
00218           
00219           // CHANGE FRAME TO BASE FOOTPRINT
00220           change_2_base_footprint_frame(link_ICP, link_ICP_cov);
00221           
00222           // COVARIANCE CORRECTION
00223           link_ICP_cov = link_ICP_cov * ICP_covariance_correction_factor;
00224           
00225           res.odom.header = get_relative_pose_srv_.request.scan_sens.header;
00226           res.odom.pose = eigen_2_posewithcovariance(link_ICP, link_ICP_cov);
00227           res.success = true;
00228           ROS_DEBUG("SCANS 2 ODOM ICP in LC result = = %f, %f, %f", res.odom.pose.pose.position.x, res.odom.pose.pose.position.y, tf::getYaw(res.odom.pose.pose.orientation));
00229           
00230         }
00231         else
00232         {
00233           ROS_WARN("SCANS 2 ODOM ICP failed in loop closure");
00234           res.success = false;
00235         }
00236       }
00237     }
00238   }
00239   
00240   return true;
00241 }
00242 
00243 Matrix3d Scans2OdomAlgNode::rotation_matrix(const double &alpha) const
00244 {
00245   Matrix3d rot = MatrixXd::Identity(3,3);
00246   
00247   rot(0,0) = cos(alpha);
00248   rot(0,1) = -sin(alpha);
00249   rot(1,0) = sin(alpha);
00250   rot(1,1) = cos(alpha);
00251   
00252   return rot;
00253 }
00254 
00255 Matrix3d Scans2OdomAlgNode::covariance_2_matrix(const geometry_msgs::PoseWithCovariance &pose) const
00256 {
00257   Matrix3d cov;
00258   
00259   cov(0,0) = pose.covariance[0];
00260   cov(0,1) = pose.covariance[1];
00261   cov(0,2) = pose.covariance[5];
00262   cov(1,0) = pose.covariance[6];
00263   cov(1,1) = pose.covariance[7];
00264   cov(1,2) = pose.covariance[11];
00265   cov(2,0) = pose.covariance[30];
00266   cov(2,1) = pose.covariance[31];
00267   cov(2,2) = pose.covariance[35];
00268   
00269   return cov;
00270 }
00271 
00272 Vector3d Scans2OdomAlgNode::pose_2_vector(const geometry_msgs::Pose &pose) const
00273 {
00274   Vector3d p;
00275   
00276   p(0) = pose.position.x;
00277   p(1) = pose.position.y;
00278   p(2) = tf::getYaw(pose.orientation);
00279   
00280   return p;
00281 }
00282 
00283 geometry_msgs::PoseWithCovariance Scans2OdomAlgNode::eigen_2_posewithcovariance(const Vector3d &p, const Matrix3d &cov) const
00284 {
00285   geometry_msgs::PoseWithCovariance pose;
00286   
00287   pose.pose.position.x = p(0);
00288   pose.pose.position.y = p(1);
00289   pose.pose.position.z = 0;
00290   pose.pose.orientation = tf::createQuaternionMsgFromYaw(p(2));
00291   
00292   pose.covariance[0]  = cov(0,0);
00293   pose.covariance[1]  = cov(0,1);
00294   pose.covariance[5]  = cov(0,2);
00295   pose.covariance[6]  = cov(1,0);
00296   pose.covariance[7]  = cov(1,1);
00297   pose.covariance[11] = cov(1,2);
00298   pose.covariance[30] = cov(2,0);
00299   pose.covariance[31] = cov(2,1);
00300   pose.covariance[35] = cov(2,2);
00301   
00302   return pose;
00303 }
00304 
00305 void Scans2OdomAlgNode::change_2_base_footprint_frame(Vector3d &odom_ICP, Matrix3d &odom_ICP_cov)
00306 {
00307   Matrix3d rot_frame = rotation_matrix(d_base_2_laser(2));
00308   Matrix3d rot_d = rotation_matrix(odom_ICP(2));
00309   odom_ICP = d_base_2_laser + rot_frame * odom_ICP - rot_d * d_base_2_laser;
00310   
00311   Matrix3d J = rotation_matrix(d_base_2_laser(2));
00312   odom_ICP_cov = J * odom_ICP_cov * J.transpose();
00313 }
00314 
00315 /*  [action callbacks] */
00316 
00317 /*  [action requests] */
00318 
00319 void Scans2OdomAlgNode::node_config_update(Config &config, uint32_t level)
00320 {
00321   this->alg_.lock();
00322   
00323   online_mode = config.online_mode;
00324   bad_cov_thres = config.bad_cov_thres;
00325   d_base_2_laser(0) = config.dx_base_2_laser;
00326   d_base_2_laser(1) = config.dy_base_2_laser;
00327   d_base_2_laser(2) = config.dth_base_2_laser;
00328   ICP_covariance_correction_factor = config.ICP_covariance_correction_factor;
00329 
00330   ROS_WARN("SCANS 2 ODOM: Config updated");
00331 
00332   this->alg_.unlock();
00333 }
00334 
00335 void Scans2OdomAlgNode::addNodeDiagnostics(void)
00336 {
00337 }
00338 
00339 /* main function */
00340 int main(int argc,char *argv[])
00341 {
00342   return algorithm_base::main<Scans2OdomAlgNode>(argc, argv, "scans_2_odom_alg_node");
00343 }


iri_poseslam
Author(s): Joan Vallvé
autogenerated on Fri Dec 6 2013 21:21:14