00001
00002
00003
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
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
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);
00066 data_association_viz_pub_ = n_.advertise<visualization_msgs::MarkerArray>(
00067 "/data_association_viz", 1);
00068
00069
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;
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
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
00232
00233 void EKFslamWrapper::makeRightHanded(Eigen::Matrix3d &eigenvectors,
00234 Eigen::Vector3d &eigenvalues) {
00235
00236
00237
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
00259 tf::Matrix3x3 tf3d;
00260 Eigen::Vector3d eigenvalues(Eigen::Vector3d::Identity());
00261 Eigen::Matrix3d eigenvectors(Eigen::Matrix3d::Zero());
00262
00263
00264 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eigensolver(
00265 #if MRPT_VERSION >= 0x199
00266 covariance.asEigen()
00267 #else
00268 covariance
00269 #endif
00270 );
00271
00272
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();
00280
00281 eigenvectors = Eigen::Matrix3d::Identity();
00282 }
00283
00284
00285 makeRightHanded(eigenvectors, eigenvalues);
00286
00287
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
00293 tf3d.getRotation(orientation);
00294
00295 scale[0] = eigenvalues[0];
00296 scale[1] = eigenvalues[1];
00297 scale[2] = eigenvalues[2];
00298 }
00299 void EKFslamWrapper::viz_dataAssociation() {
00300
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
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;
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
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
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
00370 mrpt::opengl::CSetOfObjects::Ptr objs;
00371 objs = mrpt::opengl::CSetOfObjects::Create();
00372 mapping.getAs3DObject(objs);
00373
00374
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();
00387
00388 mrpt::math::CMatrixDouble covariance = landmark->getCovMatrix();
00389
00390
00391 CPose3D pose = mrpt::poses::CPose3D(landmark->getPose());
00392
00393
00394
00395 Eigen::Vector3d scale;
00396 tf::Quaternion orientation;
00397 computeEllipseOrientationScale(orientation, scale, covariance);
00398
00399 marker.id++;
00400 marker.color.a = 1.0;
00401 if (i == 0) {
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
00448 mrpt::poses::CPose3D robotPose;
00449
00450 robotPose = CPose3D(robotPose_.mean);
00451
00452
00453
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
00476
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 }