mrpt_ekf_slam_3d_wrapper.cpp
Go to the documentation of this file.
00001 /*
00002  * File: mrpt_ekf_slam_3d_wrapper.cpp
00003  * Author: Vladislav Tananaev
00004  *
00005  */
00006 
00007 #include "mrpt_ekf_slam_3d/mrpt_ekf_slam_3d_wrapper.h"
00008 
00009 #include <mrpt/version.h>
00010 #if MRPT_VERSION >= 0x199
00011 #include <mrpt/serialization/CArchive.h>
00012 #endif
00013 
00014 EKFslamWrapper::EKFslamWrapper() {
00015   rawlog_play_ = false;
00016   mrpt_bridge::convert(ros::Time(0), timeLastUpdate_);
00017 }
00018 EKFslamWrapper::~EKFslamWrapper() {}
00019 bool EKFslamWrapper::is_file_exists(const std::string &name) {
00020   std::ifstream f(name.c_str());
00021   return f.good();
00022 }
00023 
00024 void EKFslamWrapper::get_param() {
00025   ROS_INFO("READ PARAM FROM LAUNCH FILE");
00026   n_.param<double>("ellipse_scale", ellipse_scale_, 1);
00027   ROS_INFO("ellipse_scale: %f", ellipse_scale_);
00028 
00029   n_.param<double>("rawlog_play_delay", rawlog_play_delay, 0.1);
00030   ROS_INFO("rawlog_play_delay: %f", rawlog_play_delay);
00031 
00032   n_.getParam("rawlog_filename", rawlog_filename);
00033   ROS_INFO("rawlog_filename: %s", rawlog_filename.c_str());
00034 
00035   n_.getParam("ini_filename", ini_filename);
00036   ROS_INFO("ini_filename: %s", ini_filename.c_str());
00037 
00038   n_.param<std::string>("global_frame_id", global_frame_id, "map");
00039   ROS_INFO("global_frame_id: %s", global_frame_id.c_str());
00040 
00041   n_.param<std::string>("odom_frame_id", odom_frame_id, "odom");
00042   ROS_INFO("odom_frame_id: %s", odom_frame_id.c_str());
00043 
00044   n_.param<std::string>("base_frame_id", base_frame_id, "base_link");
00045   ROS_INFO("base_frame_id: %s", base_frame_id.c_str());
00046 
00047   n_.param<std::string>("sensor_source", sensor_source, "scan");
00048   ROS_INFO("sensor_source: %s", sensor_source.c_str());
00049 }
00050 void EKFslamWrapper::init() {
00051   // get parameters from ini file
00052   if (!is_file_exists(ini_filename)) {
00053     ROS_ERROR_STREAM("CAN'T READ INI FILE");
00054     return;
00055   }
00056 
00057   EKFslam::read_iniFile(ini_filename);
00058   // read rawlog file if it  exists
00059   if (is_file_exists(rawlog_filename)) {
00060     ROS_WARN_STREAM("PLAY FROM RAWLOG FILE: " << rawlog_filename.c_str());
00061     rawlog_play_ = true;
00062   }
00063 
00064   state_viz_pub_ =
00065           n_.advertise<visualization_msgs::MarkerArray>("/state_viz", 1); // map
00066   data_association_viz_pub_ = n_.advertise<visualization_msgs::MarkerArray>(
00067           "/data_association_viz", 1); // data_association
00068 
00069   // read sensor topics
00070   std::vector<std::string> lstSources;
00071   mrpt::system::tokenize(sensor_source, " ,\t\n", lstSources);
00072   ROS_ASSERT_MSG(!lstSources.empty(),
00073                                  "*Fatal*: At least one sensor source must be provided in "
00074                                  "~sensor_sources (e.g. "
00075                                  "\"scan\" or \"beacon\")");
00076 
00078   sensorSub_.resize(lstSources.size());
00079   for (size_t i = 0; i < lstSources.size(); i++) {
00080         if (lstSources[i].find("landmark") != std::string::npos) {
00081           sensorSub_[i] = n_.subscribe(lstSources[i], 1,
00082                                                                    &EKFslamWrapper::landmarkCallback, this);
00083         } else {
00084           ROS_ERROR("Can't find the sensor topics. The sensor topics should "
00085                                 "contain the word \"landmark\" in the name");
00086     }
00087   }
00088 
00089   init3Dwindow();
00090 }
00091 
00092 void EKFslamWrapper::odometryForCallback(CObservationOdometry::Ptr &_odometry,
00093                                                                                  const std_msgs::Header &_msg_header) {
00094   mrpt::poses::CPose3D poseOdom;
00095   if (this->waitForTransform(poseOdom, odom_frame_id, base_frame_id,
00096                                                          _msg_header.stamp, ros::Duration(1))) {
00097     _odometry = CObservationOdometry::Create();
00098     _odometry->sensorLabel = odom_frame_id;
00099     _odometry->hasEncodersInfo = false;
00100     _odometry->hasVelocities = false;
00101     _odometry->odometry.x() = poseOdom.x();
00102     _odometry->odometry.y() = poseOdom.y();
00103     _odometry->odometry.phi() = poseOdom.yaw();
00104   }
00105 }
00106 
00107 void EKFslamWrapper::updateSensorPose(std::string _frame_id) {
00108   CPose3D pose;
00109   tf::StampedTransform transform;
00110   try {
00111         listenerTF_.lookupTransform(base_frame_id, _frame_id, ros::Time(0),
00112                                                                 transform);
00113 
00114     tf::Vector3 translation = transform.getOrigin();
00115     tf::Quaternion quat = transform.getRotation();
00116     pose.x() = translation.x();
00117     pose.y() = translation.y();
00118     pose.z() = translation.z();
00119     tf::Matrix3x3 Rsrc(quat);
00120     CMatrixDouble33 Rdes;
00121     for (int c = 0; c < 3; c++)
00122       for (int r = 0; r < 3; r++)
00123         Rdes(r, c) = Rsrc.getRow(r)[c];
00124     pose.setRotationMatrix(Rdes);
00125     landmark_poses_[_frame_id] = pose;
00126   } catch (tf::TransformException ex) {
00127     ROS_ERROR("%s", ex.what());
00128     ros::Duration(1.0).sleep();
00129   }
00130 }
00131 
00132 bool EKFslamWrapper::waitForTransform(
00133         mrpt::poses::CPose3D &des, const std::string &target_frame,
00134         const std::string &source_frame, const ros::Time &time,
00135         const ros::Duration &timeout, const ros::Duration &polling_sleep_duration) {
00136   tf::StampedTransform transform;
00137   try {
00138         listenerTF_.waitForTransform(target_frame, source_frame, time,
00139                                                                  polling_sleep_duration);
00140     listenerTF_.lookupTransform(target_frame, source_frame, time, transform);
00141   } catch (tf::TransformException) {
00142         ROS_INFO("Failed to get transform target_frame (%s) to source_frame (%s)",
00143                          target_frame.c_str(), source_frame.c_str());
00144     return false;
00145   }
00146   mrpt_bridge::convert(transform, des);
00147   return true;
00148 }
00149 
00150 void EKFslamWrapper::landmarkCallback(
00151         const mrpt_msgs::ObservationRangeBearing &_msg) {
00152 #if MRPT_VERSION >= 0x130
00153   using namespace mrpt::maps;
00154   using namespace mrpt::obs;
00155 #else
00156   using namespace mrpt::slam;
00157 #endif
00158   CObservationBearingRange::Ptr landmark = CObservationBearingRange::Create();
00159 
00160   if (landmark_poses_.find(_msg.header.frame_id) == landmark_poses_.end()) {
00161     updateSensorPose(_msg.header.frame_id);
00162   } else {
00163     mrpt::poses::CPose3D pose = landmark_poses_[_msg.header.frame_id];
00164         mrpt_bridge::convert(_msg, landmark_poses_[_msg.header.frame_id],
00165                                                  *landmark);
00166 
00167     sf = CSensoryFrame::Create();
00168     CObservationOdometry::Ptr odometry;
00169     odometryForCallback(odometry, _msg.header);
00170 
00171     CObservation::Ptr obs = CObservation::Ptr(landmark);
00172     sf->insert(obs);
00173     observation(sf, odometry);
00174     timeLastUpdate_ = sf->getObservationByIndex(0)->timestamp;
00175 
00176     tictac.Tic();
00177     mapping.processActionObservation(action, sf);
00178     t_exec = tictac.Tac();
00179     ROS_INFO("Map building executed in %.03fms", 1000.0f * t_exec);
00180     ros::Duration(rawlog_play_delay).sleep();
00181     mapping.getCurrentState(robotPose_, LMs_, LM_IDs_, fullState_, fullCov_);
00182     viz_state();
00183     viz_dataAssociation();
00184     run3Dwindow();
00185     publishTF();
00186   }
00187 }
00188 
00189 bool EKFslamWrapper::rawlogPlay() {
00190   if (rawlog_play_ == false) {
00191     return false;
00192   } else {
00193     size_t rawlogEntry = 0;
00194 #if MRPT_VERSION >= 0x199
00195         CFileGZInputStream f(rawlog_filename);
00196         auto rawlogFile = mrpt::serialization::archiveFrom(f);
00197 #else
00198         CFileGZInputStream rawlogFile(rawlog_filename);
00199 #endif
00200 
00201     CActionCollection::Ptr action;
00202     CSensoryFrame::Ptr observations;
00203 
00204         for (;;) {
00205           if (ros::ok()) {
00206                 if (!CRawlog::readActionObservationPair(rawlogFile, action,
00207                                                                                                 observations, rawlogEntry)) {
00208                   break; // file EOF
00209         }
00210         tictac.Tic();
00211         mapping.processActionObservation(action, observations);
00212         t_exec = tictac.Tac();
00213         ROS_INFO("Map building executed in %.03fms", 1000.0f * t_exec);
00214         ros::Duration(rawlog_play_delay).sleep();
00215                 mapping.getCurrentState(robotPose_, LMs_, LM_IDs_, fullState_,
00216                                                                 fullCov_);
00217         // ros::spinOnce();
00218         viz_state();
00219         viz_dataAssociation();
00220         run3Dwindow();
00221       }
00222     }
00223         if (win3d) {
00224       cout << "\n Close the 3D window to quit the application.\n";
00225       win3d->waitForKey();
00226     }
00227     return true;
00228   }
00229 }
00230 
00231 // Local function to force the axis to be right handed for 3D. Taken from
00232 // ecl_statistics
00233 void EKFslamWrapper::makeRightHanded(Eigen::Matrix3d &eigenvectors,
00234                                                                          Eigen::Vector3d &eigenvalues) {
00235   // Note that sorting of eigenvalues may end up with left-hand coordinate
00236   // system. So here we correctly sort it so that it does end up being
00237   // righ-handed and normalised.
00238   Eigen::Vector3d c0 = eigenvectors.block<3, 1>(0, 0);
00239   c0.normalize();
00240   Eigen::Vector3d c1 = eigenvectors.block<3, 1>(0, 1);
00241   c1.normalize();
00242   Eigen::Vector3d c2 = eigenvectors.block<3, 1>(0, 2);
00243   c2.normalize();
00244   Eigen::Vector3d cc = c0.cross(c1);
00245   if (cc.dot(c2) < 0) {
00246     eigenvectors << c1, c0, c2;
00247     double e = eigenvalues[0];
00248     eigenvalues[0] = eigenvalues[1];
00249     eigenvalues[1] = e;
00250   } else {
00251     eigenvectors << c0, c1, c2;
00252   }
00253 }
00254 
00255 void EKFslamWrapper::computeEllipseOrientationScale(
00256         tf::Quaternion &orientation, Eigen::Vector3d &scale,
00257         const mrpt::math::CMatrixDouble covariance) {
00258   // initialize variables and empty matrices for eigen vectors and eigen values
00259   tf::Matrix3x3 tf3d;
00260   Eigen::Vector3d eigenvalues(Eigen::Vector3d::Identity());
00261   Eigen::Matrix3d eigenvectors(Eigen::Matrix3d::Zero());
00262 
00263   // compute eigen vectors and eigen values from covariance matrix
00264   Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eigensolver(
00265 #if MRPT_VERSION >= 0x199
00266           covariance.asEigen()
00267 #else
00268           covariance
00269 #endif
00270   );
00271 
00272   // Compute eigenvectors and eigenvalues
00273   if (eigensolver.info() == Eigen::Success) {
00274     eigenvalues = eigensolver.eigenvalues();
00275     eigenvectors = eigensolver.eigenvectors();
00276   } else {
00277         ROS_ERROR_STREAM("failed to compute eigen vectors/values for position. Is "
00278                                          "the covariance matrix correct?");
00279         eigenvalues = Eigen::Vector3d::Zero(); // Setting the scale to zero will
00280                                                                                    // hide it on the screen
00281     eigenvectors = Eigen::Matrix3d::Identity();
00282   }
00283 
00284   // Be sure we have a right-handed orientation system
00285   makeRightHanded(eigenvectors, eigenvalues);
00286 
00287   // Rotation matrix
00288   tf3d.setValue(eigenvectors(0, 0), eigenvectors(0, 1), eigenvectors(0, 2),
00289                                 eigenvectors(1, 0), eigenvectors(1, 1), eigenvectors(1, 2),
00290                                 eigenvectors(2, 0), eigenvectors(2, 1), eigenvectors(2, 2));
00291 
00292   // get orientation from rotation matrix
00293   tf3d.getRotation(orientation);
00294   // get scale
00295   scale[0] = eigenvalues[0];
00296   scale[1] = eigenvalues[1];
00297   scale[2] = eigenvalues[2];
00298 }
00299 void EKFslamWrapper::viz_dataAssociation() {
00300   // robot pose
00301   mrpt::poses::CPose3D robotPose;
00302   robotPose = CPose3D(robotPose_.mean);
00303   geometry_msgs::Point pointRobotPose;
00304   pointRobotPose.z = robotPose.z();
00305   pointRobotPose.x = robotPose.x();
00306   pointRobotPose.y = robotPose.y();
00307 
00308   // visualization of the data association
00309   visualization_msgs::MarkerArray ma;
00310   visualization_msgs::Marker line_strip;
00311 
00312   line_strip.header.frame_id = "/map";
00313   line_strip.header.stamp = ros::Time::now();
00314 
00315   line_strip.id = 0;
00316   line_strip.type = visualization_msgs::Marker::LINE_STRIP;
00317   line_strip.action = visualization_msgs::Marker::ADD;
00318 
00319   line_strip.lifetime = ros::Duration(0.1);
00320   line_strip.pose.position.x = 0;
00321   line_strip.pose.position.y = 0;
00322   line_strip.pose.position.z = 0;
00323   line_strip.pose.orientation.x = 0.0;
00324   line_strip.pose.orientation.y = 0.0;
00325   line_strip.pose.orientation.z = 0.0;
00326   line_strip.pose.orientation.w = 1.0;
00327   line_strip.scale.x = 0.02; // line uses only x component
00328   line_strip.scale.y = 0.0;
00329   line_strip.scale.z = 0.0;
00330   line_strip.color.a = 1.0;
00331   line_strip.color.r = 1.0;
00332   line_strip.color.g = 1.0;
00333   line_strip.color.b = 1.0;
00334 
00335   // Draw latest data association:
00336   const CRangeBearingKFSLAM::TDataAssocInfo &da =
00337           mapping.getLastDataAssociation();
00338 
00339   for (auto it = da.results.associations.begin();
00340            it != da.results.associations.end(); ++it) {
00341     const prediction_index_t idxPred = it->second;
00342     // This index must match the internal list of features in the map:
00343     CRangeBearingKFSLAM::KFArray_FEAT featMean;
00344     mapping.getLandmarkMean(idxPred, featMean);
00345 
00346     line_strip.points.clear();
00347     line_strip.points.push_back(pointRobotPose);
00348     geometry_msgs::Point pointLm;
00349     pointLm.z = featMean[2];
00350     pointLm.x = featMean[0];
00351     pointLm.y = featMean[1];
00352     line_strip.points.push_back(pointLm);
00353     ma.markers.push_back(line_strip);
00354     line_strip.id++;
00355   }
00356 
00357   data_association_viz_pub_.publish(ma);
00358 }
00359 
00360 void EKFslamWrapper::viz_state() {
00361   visualization_msgs::MarkerArray ma;
00362   visualization_msgs::Marker marker;
00363   marker.header.frame_id = "/map";
00364   marker.id = 0;
00365   marker.type = visualization_msgs::Marker::SPHERE;
00366   marker.action = visualization_msgs::Marker::ADD;
00367   marker.lifetime = ros::Duration(0);
00368 
00369   // get the covariance matrix 3x3 for each ellipsoid including robot pose
00370   mrpt::opengl::CSetOfObjects::Ptr objs;
00371   objs = mrpt::opengl::CSetOfObjects::Create();
00372   mapping.getAs3DObject(objs);
00373 
00374   // Count the number of landmarks + robot
00375   unsigned int objs_counter = 0;
00376   while (objs->getByClass<mrpt::opengl::CEllipsoid>(objs_counter)) {
00377     objs_counter++;
00378   }
00379 
00380   mrpt::opengl::CEllipsoid::Ptr landmark;
00381 
00382   for (size_t i = 0; i < objs_counter; i++) {
00383     landmark = objs->getByClass<mrpt::opengl::CEllipsoid>(i);
00384 
00385         float quantiles =
00386                 landmark->getQuantiles(); // the scale of ellipse covariance
00387                                                                   // visualization (usually 3  sigma)
00388     mrpt::math::CMatrixDouble covariance = landmark->getCovMatrix();
00389 
00390     // the landmark (or robot) mean position
00391         CPose3D pose = mrpt::poses::CPose3D(landmark->getPose());
00392         // For visualization of the covariance ellipses we need the size of the axis
00393         // and orientation
00394 
00395         Eigen::Vector3d scale; // size of axis of the ellipse
00396     tf::Quaternion orientation;
00397     computeEllipseOrientationScale(orientation, scale, covariance);
00398 
00399     marker.id++;
00400     marker.color.a = 1.0;
00401         if (i == 0) { // robot position
00402       marker.color.r = 1.0;
00403       marker.color.g = 0.0;
00404       marker.color.b = 0.0;
00405         } else {
00406       marker.color.r = 0.0;
00407       marker.color.g = 0.0;
00408       marker.color.b = 1.0;
00409     }
00410     marker.type = visualization_msgs::Marker::SPHERE;
00411     marker.pose.position.x = pose.x();
00412     marker.pose.position.y = pose.y();
00413     marker.pose.position.z = pose.z();
00414     marker.pose.orientation.x = orientation.x();
00415     marker.pose.orientation.y = orientation.y();
00416     marker.pose.orientation.z = orientation.z();
00417     marker.pose.orientation.w = orientation.w();
00418     marker.scale.x = ellipse_scale_ * quantiles * std::sqrt(scale[0]);
00419     marker.scale.y = ellipse_scale_ * quantiles * std::sqrt(scale[1]);
00420     marker.scale.z = ellipse_scale_ * quantiles * std::sqrt(scale[2]);
00421     ma.markers.push_back(marker);
00422 
00423     marker.id++;
00424     marker.type = visualization_msgs::Marker::TEXT_VIEW_FACING;
00425         if (i == 0) {
00426       marker.text = "robot";
00427         } else {
00428       marker.text = std::to_string(LM_IDs_[i]);
00429     }
00430     marker.pose.position.x = pose.x();
00431     marker.pose.position.y = pose.y();
00432     marker.pose.position.z = pose.z() + marker.scale.z + 0.1;
00433     marker.color.r = 1.0;
00434     marker.color.g = 1.0;
00435     marker.color.b = 1.0;
00436     marker.scale.x = 0.3;
00437     marker.scale.y = 0.3;
00438     marker.scale.z = 0.3;
00439     ma.markers.push_back(marker);
00440   }
00441 
00442   state_viz_pub_.publish(ma);
00443 }
00444 
00445 void EKFslamWrapper::publishTF() {
00446   mapping.getCurrentState(robotPose_, LMs_, LM_IDs_, fullState_, fullCov_);
00447   // Most of this code was copy and pase from ros::amcl
00448   mrpt::poses::CPose3D robotPose;
00449 
00450   robotPose = CPose3D(robotPose_.mean);
00451   // mapBuilder->mapPDF.getEstimatedPosePDF(curPDF);
00452 
00453   // curPDF.getMean(robotPose);
00454 
00455   tf::Stamped<tf::Pose> odom_to_map;
00456   tf::Transform tmp_tf;
00457   ros::Time stamp;
00458   mrpt_bridge::convert(timeLastUpdate_, stamp);
00459   mrpt_bridge::convert(robotPose, tmp_tf);
00460 
00461   try {
00462         tf::Stamped<tf::Pose> tmp_tf_stamped(tmp_tf.inverse(), stamp,
00463                                                                                  base_frame_id);
00464     listenerTF_.transformPose(odom_frame_id, tmp_tf_stamped, odom_to_map);
00465   } catch (tf::TransformException) {
00466         ROS_INFO("Failed to subtract global_frame (%s) from odom_frame (%s)",
00467                          global_frame_id.c_str(), odom_frame_id.c_str());
00468     return;
00469   }
00470 
00471   tf::Transform latest_tf_ =
00472           tf::Transform(tf::Quaternion(odom_to_map.getRotation()),
00473                                         tf::Point(odom_to_map.getOrigin()));
00474 
00475   // We want to send a transform that is good up until a
00476   // tolerance time so that odom can be used
00477 
00478   ros::Duration transform_tolerance_(0.1);
00479   ros::Time transform_expiration = (stamp + transform_tolerance_);
00480   tf::StampedTransform tmp_tf_stamped(latest_tf_.inverse(),
00481                                                                           transform_expiration, global_frame_id,
00482                                                                           odom_frame_id);
00483   tf_broadcaster_.sendTransform(tmp_tf_stamped);
00484 }


mrpt_ekf_slam_3d
Author(s): Jose Luis, Vladislav Tananaev
autogenerated on Thu Jun 6 2019 21:40:23