00001 #include "sensors_2_link_alg_node.h"
00002
00003 Sensors2LinkAlgNode::Sensors2LinkAlgNode(void) :
00004 algorithm_base::IriBaseAlgorithm<Sensors2LinkAlgorithm>()
00005 {
00006
00007
00008
00009
00010
00011
00012 this->scan_subscriber_ = this->public_node_handle_.subscribe("scan", 100, &Sensors2LinkAlgNode::scan_callback, this);
00013 this->odom_relative_subscriber_ = this->public_node_handle_.subscribe("odom_relative", 100, &Sensors2LinkAlgNode::odom_relative_callback, this);
00014 this->cmd_vel_subscriber_ = this->public_node_handle_.subscribe("cmd_vel", 100, &Sensors2LinkAlgNode::cmd_vel_callback, this);
00015
00016
00017 this->get_link_server_ = this->public_node_handle_.advertiseService("get_link", &Sensors2LinkAlgNode::get_linkCallback, this);
00018
00019
00020 get_relative_pose_client_ = this->public_node_handle_.serviceClient<iri_laser_icp::GetRelativePose>("get_relative_pose");
00021
00022
00023
00024
00025
00026
00027 odom_rel_ = MatrixXd::Zero(3,1);
00028 odom_rel_cov_ = MatrixXd::Zero(3,3);
00029 Jp = MatrixXd::Identity(3,3);
00030 Jd = MatrixXd::Identity(3,3);
00031 fusion_ready_ = false;
00032 new_laser_scan_ = false;
00033 prev_seq = 0;
00034 }
00035
00036 Sensors2LinkAlgNode::~Sensors2LinkAlgNode(void)
00037 {
00038
00039 }
00040
00041 void Sensors2LinkAlgNode::mainNodeThread(void)
00042 {
00043
00044
00045
00046
00047
00048
00049
00050 }
00051
00052
00053 void Sensors2LinkAlgNode::scan_callback(const sensor_msgs::LaserScan::ConstPtr& msg)
00054 {
00055
00056
00057
00058 last_laser_scan_ = (*msg);
00059 new_laser_scan_ = true;
00060
00061
00062 if (laser_scan_buffer_.empty())
00063 {
00064 laser_scan_buffer_.push_back(*msg);
00065 new_laser_scan_ = false;
00066 }
00067 }
00068
00069 void Sensors2LinkAlgNode::odom_relative_callback(const nav_msgs::Odometry::ConstPtr& msg)
00070 {
00071 if (prev_seq + 1 < msg->header.seq)
00072 ROS_WARN("SENSORS 2 LINK: Any odom relative lost! prev_seq = %i current_seq = %i", prev_seq, msg->header.seq);
00073
00074 prev_seq = msg->header.seq;
00075
00076 Q = MatrixXd::Zero(3,3);
00077 d_local = MatrixXd::Zero(3,1);
00078
00079
00080 if (!stopped_since_last_odom)
00081 {
00082
00083 d_local = pose_2_vector(msg->pose.pose);
00084
00085
00086 Q = covariance_2_matrix(msg->pose);
00087 }
00088 else
00089 ROS_DEBUG("SENSORS 2 LINK: Zero motion relative odometry");
00090
00091
00092 if (currently_stopped)
00093 stopped_since_last_odom = true;
00094
00095
00096
00097 odom_rel_time_ = msg->header.stamp;
00098
00099
00100
00101 Jp(0,2) =-sin(odom_rel_(2)) * d_local(0) - cos(odom_rel_(2)) * d_local(1);
00102 Jp(1,2) = cos(odom_rel_(2)) * d_local(0) - sin(odom_rel_(2)) * d_local(1);
00103
00104
00105 Jd = rotation_matrix(odom_rel_(2));
00106
00107
00108 odom_rel_cov_ = Jp * odom_rel_cov_ * Jp.transpose() + Jd * Q * Jd.transpose();
00109
00110
00111 odom_rel_ += rotation_matrix(odom_rel_(2)) * d_local;
00112
00113
00114
00115 if (odom_rel_buffer_.size() == odom_rel_buffer_.capacity())
00116 {
00117 odom_rel_time_buffer_.reserve(int(odom_rel_time_buffer_.size()) + 1000);
00118 odom_rel_cov_buffer_.reserve(int(odom_rel_cov_buffer_.size()) + 1000);
00119 odom_rel_buffer_.reserve(int(odom_rel_buffer_.size()) + 1000);
00120 }
00121 odom_rel_time_buffer_.push_back(odom_rel_time_);
00122 odom_rel_cov_buffer_.push_back(odom_rel_cov_);
00123 odom_rel_buffer_.push_back(odom_rel_);
00124
00125
00126
00127
00128
00129 if (!online_mode && odom_rel_buffer_.size() > 1 && new_laser_scan_ && last_laser_scan_.header.stamp < msg->header.stamp)
00130 {
00131
00132 if (odom_buffer_.size() == odom_buffer_.capacity())
00133 odom_buffer_.reserve(int(odom_buffer_.size()) + 1000);
00134
00135
00136 geometry_msgs::PoseWithCovarianceStamped new_odometry = odometry_fusion(last_laser_scan_, odom_rel_buffer_.size() - 1);
00137 odom_buffer_.push_back(new_odometry);
00138 new_laser_scan_ = false;
00139
00140
00141 }
00142
00143
00144 else
00145 {
00146 if (odom_rel_buffer_.size() > 1 && new_laser_scan_ && last_laser_scan_.header.stamp < msg->header.stamp)
00147 {
00148 fusion_ready_ = true;
00149 new_laser_scan_ = false;
00150 ready_laser_scan_ = last_laser_scan_;
00151 ready_odom_id_ = odom_rel_buffer_.size() - 1;
00152 }
00153 }
00154 }
00155
00156 void Sensors2LinkAlgNode::cmd_vel_callback(const geometry_msgs::Twist::ConstPtr& msg)
00157 {
00158 currently_stopped = (msg->linear.x == 0 && msg->linear.y == 0 && msg->linear.z == 0 && msg->angular.x == 0 && msg->angular.y == 0 && msg->angular.z == 0);
00159
00160 if (stopped_since_last_odom)
00161 stopped_since_last_odom = currently_stopped;
00162 }
00163
00164
00165 bool Sensors2LinkAlgNode::get_linkCallback(iri_poseslam::GetLink::Request &req, iri_poseslam::GetLink::Response &res)
00166 {
00167
00168
00169 res.success = false;
00170 res.end = false;
00171
00172
00173 if (req.current_step == 0 && req.with_step == 0)
00174 {
00175 if (!laser_scan_buffer_.empty())
00176 {
00177
00178 res.odom.header = laser_scan_buffer_.front().header;
00179 res.success = true;
00180
00181 ROS_INFO("SENSORS 2 LINK: first laser scan header given");
00182 }
00183 }
00184
00185
00186 else
00187 {
00188
00189 if (req.current_step < req.with_step)
00190 {
00191
00192 if (!online_mode)
00193 res = offline_odometry();
00194
00195
00196 else
00197 res = online_odometry();
00198 }
00199
00200
00201 else
00202 {
00203 if (req.current_step != laser_scan_buffer_.size() - 1)
00204 ROS_WARN("SENSORS 2 LINK: current_scan is not last_scan in LOOP CLOSURE");
00205
00206 get_relative_pose_srv_.request.scan_ref = laser_scan_buffer_.at(req.with_step);
00207 get_relative_pose_srv_.request.scan_sens = laser_scan_buffer_.at(req.current_step);
00208
00209 Vector3d prior_d = pose_2_vector(get_relative_pose_srv_.response.pose_rel.pose.pose);
00210 base_2_laser_frame(prior_d);
00211 get_relative_pose_srv_.request.prior_d = vector_2_pose(prior_d);
00212
00213
00214 if (get_relative_pose_client_.call(get_relative_pose_srv_))
00215 {
00216 if (get_relative_pose_srv_.response.success)
00217 {
00218 Vector3d link_ICP = pose_2_vector(get_relative_pose_srv_.response.pose_rel.pose.pose);
00219 Matrix3d link_ICP_cov = covariance_2_matrix(get_relative_pose_srv_.response.pose_rel.pose);
00220
00221
00222 laser_2_base_frame(link_ICP, link_ICP_cov);
00223
00224
00225 link_ICP_cov = link_ICP_cov * ICP_covariance_correction_factor;
00226
00227 res.odom.header = get_relative_pose_srv_.request.scan_sens.header;
00228 res.odom.pose = eigen_2_posewithcovariance(link_ICP, link_ICP_cov);
00229
00230 res.success = true;
00231
00232
00233
00234
00235 }
00236 else
00237 {
00238 ROS_WARN("SENSORS 2 LINK: ICP didn't found a match in loop closure");
00239 res.success = false;
00240 }
00241 }
00242 else
00243 {
00244 ROS_ERROR("SENSORS 2 LINK: ICP communication failed in loop closure");
00245 res.success = false;
00246 }
00247 }
00248 }
00249
00250 return true;
00251 }
00252
00253
00254
00255
00256
00257 void Sensors2LinkAlgNode::node_config_update(Config &config, uint32_t level)
00258 {
00259 this->alg_.lock();
00260
00261 stopped_since_last_odom = config.cmd_vel_available;
00262 currently_stopped = config.cmd_vel_available;
00263 online_mode = config.online_mode;
00264 d_base_2_laser(0) = config.dx_base_2_laser;
00265 d_base_2_laser(1) = config.dy_base_2_laser;
00266 d_base_2_laser(2) = config.dth_base_2_laser;
00267 ICP_covariance_correction_factor = config.ICP_covariance_correction_factor;
00268
00269 ROS_WARN("SENSORS 2 LINK: Config updated");
00270
00271 this->alg_.unlock();
00272 }
00273
00274 void Sensors2LinkAlgNode::addNodeDiagnostics(void)
00275 {
00276 }
00277
00278 geometry_msgs::PoseWithCovarianceStamped Sensors2LinkAlgNode::odometry_fusion(const sensor_msgs::LaserScan &laser_scan, const int &odom_rel_idx)
00279 {
00280 Vector3d odom, odom_fused, odom_ICP;
00281 Matrix3d odom_cov, odom_fused_cov, odom_ICP_cov;
00282 geometry_msgs::PoseWithCovarianceStamped fused_odom;
00283
00284
00285 std::pair<Vector3d, Matrix3d> int_odom = interpolate_odom_rel(odom_rel_idx, laser_scan.header.stamp);
00286
00287 Vector3d int_odom_rel = int_odom.first;
00288 Matrix3d int_odom_rel_cov = int_odom.second;
00289
00290
00291 get_relative_pose_srv_.request.scan_sens = laser_scan;
00292 get_relative_pose_srv_.request.scan_ref = laser_scan_buffer_.back();
00293
00294 base_2_laser_frame(int_odom_rel);
00295 get_relative_pose_srv_.request.prior_d = vector_2_pose(int_odom_rel);
00296
00297
00298 if (get_relative_pose_client_.call(get_relative_pose_srv_))
00299 {
00300 if (get_relative_pose_srv_.response.success)
00301 {
00302 ROS_DEBUG("SENSORS 2 LINK: ICP in odometry success!");
00303 odom_ICP = pose_2_vector(get_relative_pose_srv_.response.pose_rel.pose.pose);
00304 odom_ICP_cov = covariance_2_matrix(get_relative_pose_srv_.response.pose_rel.pose);
00305
00306
00307 laser_2_base_frame(odom_ICP, odom_ICP_cov);
00308
00309
00310 odom_ICP_cov = odom_ICP_cov * ICP_covariance_correction_factor;
00311
00312
00313
00314
00315
00316
00317 if (int_odom_rel_cov.isZero())
00318 {
00319
00320 odom_cov = int_odom_rel_cov;
00321 odom = int_odom_rel;
00322 }
00323 else
00324 {
00325
00326
00327
00328
00329
00330
00331
00332 if (int_odom_rel_cov.trace() < odom_ICP_cov.trace())
00333 {
00334 odom = int_odom_rel;
00335 odom_cov = int_odom_rel_cov;
00336 }
00337
00338 else
00339 {
00340 odom = odom_ICP;
00341 odom_cov = odom_ICP_cov;
00342 }
00343
00344
00345
00346
00347
00348
00349
00350
00351 }
00352 }
00353 else
00354 {
00355 ROS_WARN("SENSORS 2 LINK: ICP didn't found a match in odometry fusion");
00356 odom_cov = int_odom_rel_cov;
00357 odom = int_odom_rel;
00358 }
00359 }
00360 else
00361 {
00362 ROS_WARN("SENSORS 2 LINK: ICP communication failed in odometry fusion");
00363 odom_cov = int_odom_rel_cov;
00364 odom = int_odom_rel;
00365 }
00366
00367
00368 if (laser_scan_buffer_.size() == laser_scan_buffer_.capacity())
00369 laser_scan_buffer_.reserve(int(laser_scan_buffer_.size()) + 1000);
00370
00371
00372 laser_scan_buffer_.push_back(laser_scan);
00373
00374
00375 fused_odom.header = laser_scan.header;
00376 fused_odom.pose = eigen_2_posewithcovariance(odom, odom_cov);
00377
00378 return fused_odom;
00379 }
00380
00381 Matrix3d Sensors2LinkAlgNode::rotation_matrix(const double &alpha) const
00382 {
00383 Matrix3d rot = MatrixXd::Identity(3,3);
00384
00385 rot(0,0) = cos(alpha);
00386 rot(0,1) = -sin(alpha);
00387 rot(1,0) = sin(alpha);
00388 rot(1,1) = cos(alpha);
00389
00390 return rot;
00391 }
00392
00393 Matrix3d Sensors2LinkAlgNode::covariance_2_matrix(const geometry_msgs::PoseWithCovariance &pose) const
00394 {
00395 Matrix3d cov;
00396
00397 cov(0,0) = pose.covariance[0];
00398 cov(0,1) = pose.covariance[1];
00399 cov(0,2) = pose.covariance[5];
00400 cov(1,0) = pose.covariance[6];
00401 cov(1,1) = pose.covariance[7];
00402 cov(1,2) = pose.covariance[11];
00403 cov(2,0) = pose.covariance[30];
00404 cov(2,1) = pose.covariance[31];
00405 cov(2,2) = pose.covariance[35];
00406
00407 return cov;
00408 }
00409
00410 Vector3d Sensors2LinkAlgNode::pose_2_vector(const geometry_msgs::Pose &pose) const
00411 {
00412 Vector3d p;
00413
00414 p(0) = pose.position.x;
00415 p(1) = pose.position.y;
00416 p(2) = tf::getYaw(pose.orientation);
00417
00418 return p;
00419 }
00420
00421 geometry_msgs::PoseWithCovariance Sensors2LinkAlgNode::eigen_2_posewithcovariance(const Vector3d &p, const Matrix3d &cov) const
00422 {
00423 geometry_msgs::PoseWithCovariance pose;
00424
00425 pose.pose = vector_2_pose(p);
00426
00427 pose.covariance[0] = cov(0,0);
00428 pose.covariance[1] = cov(0,1);
00429 pose.covariance[5] = cov(0,2);
00430 pose.covariance[6] = cov(1,0);
00431 pose.covariance[7] = cov(1,1);
00432 pose.covariance[11] = cov(1,2);
00433 pose.covariance[30] = cov(2,0);
00434 pose.covariance[31] = cov(2,1);
00435 pose.covariance[35] = cov(2,2);
00436
00437 return pose;
00438 }
00439
00440 geometry_msgs::Pose Sensors2LinkAlgNode::vector_2_pose(const Vector3d &p) const
00441 {
00442 geometry_msgs::Pose pose;
00443
00444 pose.position.x = p(0);
00445 pose.position.y = p(1);
00446 pose.position.z = 0;
00447 pose.orientation = tf::createQuaternionMsgFromYaw(p(2));
00448
00449 return pose;
00450 }
00451
00452 iri_poseslam::GetLink::Response Sensors2LinkAlgNode::offline_odometry()
00453 {
00454 iri_poseslam::GetLink::Response result;
00455
00456
00457 if (!odom_buffer_.empty())
00458 {
00459 result.odom = odom_buffer_.front();
00460 result.success = true;
00461 result.end = false;
00462
00463
00464 odom_buffer_.erase(odom_buffer_.begin());
00465 }
00466 else
00467 {
00468 ROS_DEBUG("SENSORS 2 LINK: There is no more precomputed odometries");
00469 result.success = false;
00470 result.end = true;
00471 }
00472
00473 return result;
00474 }
00475
00476 iri_poseslam::GetLink::Response Sensors2LinkAlgNode::online_odometry()
00477 {
00478 iri_poseslam::GetLink::Response result;
00479
00480 if (fusion_ready_)
00481 {
00482
00483 result.odom = odometry_fusion(ready_laser_scan_, ready_odom_id_);
00484 result.success = true;
00485 result.end = false;
00486 fusion_ready_ = false;
00487 }
00488 else
00489 {
00490 ROS_DEBUG("SENSORS 2 LINK: There is not enough sensor data");
00491 result.success = false;
00492 result.end = true;
00493 }
00494
00495 return result;
00496 }
00497
00498 std::pair<Vector3d, Matrix3d> Sensors2LinkAlgNode::interpolate_odom_rel(const int &odom_rel_idx, const ros::Time scan_stamp)
00499 {
00500 Vector3d pre_odom_rel = odom_rel_buffer_.at(odom_rel_idx - 1);
00501 Vector3d post_odom_rel = odom_rel_buffer_.at(odom_rel_idx);
00502 Matrix3d pre_odom_rel_cov = odom_rel_cov_buffer_.at(odom_rel_idx - 1);
00503 Matrix3d post_odom_rel_cov = odom_rel_cov_buffer_.at(odom_rel_idx);
00504 ros::Time pre_odom_rel_time = odom_rel_time_buffer_.at(odom_rel_idx - 1);
00505 ros::Time post_odom_rel_time = odom_rel_time_buffer_.at(odom_rel_idx);
00506
00507
00508 double alpha = (scan_stamp - pre_odom_rel_time).toSec() / (post_odom_rel_time - pre_odom_rel_time).toSec();
00509 alpha = 1;
00510
00511 VectorXd int_odom_rel = (1 - alpha) * pre_odom_rel + alpha * post_odom_rel;
00512 MatrixXd int_odom_rel_cov = (1 - alpha) * pre_odom_rel_cov + alpha * post_odom_rel_cov;
00513
00514
00515 MatrixXd rot = rotation_matrix(int_odom_rel(2));
00516 VectorXd rest_odom_rel = rot * (post_odom_rel - int_odom_rel);
00517
00518 rot = rotation_matrix(-int_odom_rel(2));
00519 MatrixXd rest_odom_rel_cov = rot * (post_odom_rel_cov - int_odom_rel_cov) * rot.transpose();
00520
00521 odom_rel_buffer_.clear();
00522 odom_rel_cov_buffer_.clear();
00523 odom_rel_time_buffer_.clear();
00524
00525 odom_rel_buffer_.push_back(rest_odom_rel);
00526 odom_rel_cov_buffer_.push_back(rest_odom_rel_cov);
00527 odom_rel_time_buffer_.push_back(pre_odom_rel_time + (post_odom_rel_time - pre_odom_rel_time) * alpha);
00528
00529
00530 odom_rel_ = rest_odom_rel;
00531 odom_rel_cov_ = rest_odom_rel_cov;
00532
00533 std::pair<Vector3d, Matrix3d> result;
00534 result.first = int_odom_rel;
00535 result.second = int_odom_rel_cov;
00536
00537 return result;
00538 }
00539
00540 void Sensors2LinkAlgNode::base_2_laser_frame(Vector3d &prior_d)
00541 {
00542 Matrix3d rot_frame = rotation_matrix(-d_base_2_laser(2));
00543 Matrix3d rot_d = rotation_matrix(prior_d(2));
00544
00545 prior_d = rot_d * rot_frame * d_base_2_laser + rot_frame * prior_d - rot_frame * d_base_2_laser;
00546
00547 }
00548
00549 void Sensors2LinkAlgNode::laser_2_base_frame(Vector3d &odom_ICP, Matrix3d &odom_ICP_cov)
00550 {
00551 Matrix3d rot_frame = rotation_matrix(d_base_2_laser(2));
00552 Matrix3d rot_d = rotation_matrix(odom_ICP(2));
00553 odom_ICP = d_base_2_laser + rot_frame * odom_ICP - rot_d * d_base_2_laser;
00554
00555 Matrix3d J = rotation_matrix(d_base_2_laser(2));
00556 odom_ICP_cov = J * odom_ICP_cov * J.transpose();
00557 }
00558
00559
00560 int main(int argc,char *argv[])
00561 {
00562 return algorithm_base::main<Sensors2LinkAlgNode>(argc, argv, "sensors_2_link_node");
00563 }