00001 #include "poseslam_alg_node.h"
00002
00003 PoseslamAlgNode::PoseslamAlgNode(void) :
00004 algorithm_base::IriBaseAlgorithm<PoseslamAlgorithm>()
00005 {
00006
00007 this->loop_rate_ = alg_.config_.loop_rate;
00008
00009
00010 this->trajectory_publisher_ = this->public_node_handle_.advertise<iri_poseslam::Trajectory>("trajectory", 10);
00011 if (alg_.config_.also_no_loops)
00012 this->no_loops_trajectory_publisher_ = this->public_node_handle_.advertise<iri_poseslam::Trajectory>("no_loops_trajectory", 10);
00013
00014
00015
00016
00017
00018
00019 get_link_client_ = this->public_node_handle_.serviceClient<iri_poseslam::GetLink>("get_link");
00020
00021
00022
00023
00024
00025
00026 step_ = 0;
00027 ended = 0;
00028 }
00029
00030 PoseslamAlgNode::~PoseslamAlgNode(void)
00031 {
00032
00033 }
00034
00035 void PoseslamAlgNode::mainNodeThread(void)
00036 {
00037 bool LoopClosure = false;
00038 while (step_ == 0)
00039 {
00040
00041
00042 get_link_srv_.request.current_step = 0;
00043 get_link_srv_.request.with_step = 0;
00044 if (get_link_client_.call(get_link_srv_))
00045 {
00046 if (get_link_srv_.response.success)
00047 {
00048 augment_trajectories(get_link_srv_.response.odom.header);
00049 update_trajectories(false);
00050
00051 this->trajectory_publisher_.publish(this->Trajectory_msg_);
00052 if (alg_.config_.also_no_loops)
00053 this->no_loops_trajectory_publisher_.publish(this->Trajectory_msg_no_loops_);
00054
00055 step_++;
00056
00057 ROS_INFO("POSE SLAM: First laser scan header obtained");
00058 }
00059 }
00060
00061
00062 }
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076 get_link_srv_.request.current_step = step_ - 1;
00077 get_link_srv_.request.with_step = step_;
00078
00079 alg_.tSens = ros::Time::now().toSec();
00080
00081 if (get_link_client_.call(get_link_srv_))
00082 {
00083 if (get_link_srv_.response.success && !get_link_srv_.response.end)
00084 {
00085
00086 if (alg_.timePub.capacity() == alg_.timePub.size())
00087 {
00088 alg_.timePub.reserve(alg_.timePub.size() + size_t(100));
00089 alg_.timeSensOpen.reserve(alg_.timeSensOpen.size() + size_t(100));
00090 alg_.timeSensClose.reserve(alg_.timeSensClose.size() + size_t(100));
00091 }
00092 alg_.timePub.push_back(0.0);
00093 alg_.timeSensOpen.push_back(0.0);
00094 alg_.timeSensClose.push_back(0.0);
00095
00096 alg_.timeSensOpen.back() = alg_.timeSensOpen.back() + ros::Time::now().toSec() - alg_.tSens;
00097
00098
00099 alg_.augmentation(step_, get_link_srv_.response.odom);
00100
00101
00102 augment_trajectories(get_link_srv_.response.odom.header);
00103
00104
00105 alg_.create_candidates_list(true);
00106
00107
00108 while (alg_.any_candidate() && !alg_.is_redundant())
00109 {
00110
00111 alg_.select_best_candidate();
00112
00113
00114
00115 alg_.redundant_evaluation();
00116
00117
00118 if (alg_.loop_closure_requeriments())
00119 {
00120 get_link_srv_.request.current_step = step_;
00121 get_link_srv_.request.with_step = alg_.get_candidate_step();
00122 get_link_srv_.request.prior_d = alg_.get_candidate_d();
00123
00124 ROS_DEBUG("POSE SLAM: trying to close a loop: actual step_: %i with step_: %i", int(get_link_srv_.request.current_step), int(get_link_srv_.request.with_step));
00125
00126 alg_.tSens = ros::Time::now().toSec();
00127 if (get_link_client_.call(get_link_srv_))
00128 LoopClosure = get_link_srv_.response.success;
00129 else
00130 ROS_ERROR("POSE SLAM: Communication with sensors_2_link failed");
00131 alg_.timeSensClose.back() = alg_.timeSensClose.back() + ros::Time::now().toSec() - alg_.tSens;
00132
00133 if (LoopClosure)
00134 LoopClosure = alg_.try_loop_closure(get_link_srv_.response.odom);
00135
00136 if (LoopClosure)
00137 {
00138 ROS_INFO("POSE SLAM: LOOP CLOSED! current step_: %i with step_: %i", step_, alg_.get_candidate_step());
00139 loops_.push_back(alg_.get_candidate_step());
00140 loops_.push_back(step_);
00141 }
00142 }
00143 alg_.update_candidates_list(LoopClosure, true);
00144 }
00145
00146 if (!alg_.is_redundant())
00147 ROS_DEBUG("POSE SLAM: NEW STATE - step_: %i - IG: %f", step_, alg_.get_candidate_ig());
00148
00149
00150 alg_.tPub = ros::Time::now().toSec();
00151 update_trajectories(LoopClosure);
00152
00153
00154 this->trajectory_publisher_.publish(this->Trajectory_msg_);
00155 if (alg_.config_.also_no_loops)
00156 this->no_loops_trajectory_publisher_.publish(this->Trajectory_msg_no_loops_);
00157 alg_.timePub.back() = alg_.timePub.back() + ros::Time::now().toSec() - alg_.tPub;
00158
00159 step_++;
00160 ended = 0;
00161 }
00162 else if (step_ != 1 && get_link_srv_.response.end)
00163 ended++;
00164
00165
00166
00167
00168 }
00169 else
00170 ROS_ERROR("POSE SLAM: Failed to Call Server (service get_link) in next odom");
00171
00172
00173
00174 if (ended == 5)
00175 {
00176 ROS_INFO("POSE SLAM: SIMULATION ENDED");
00177
00178 print_results();
00179 ended ++;
00180 }
00181 }
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191 void PoseslamAlgNode::node_config_update(Config &config, uint32_t level)
00192 {
00193 this->alg_.lock();
00194 loop_rate_ = config.loop_rate;
00195 dz_footprint_2_base_ = config.dz_footprint_2_base;
00196 this->alg_.unlock();
00197 }
00198
00199 void PoseslamAlgNode::addNodeDiagnostics(void)
00200 {
00201 }
00202
00203
00204 int main(int argc,char *argv[])
00205 {
00206 return algorithm_base::main<PoseslamAlgNode>(argc, argv, "poseslam_alg_node");
00207 }
00208
00209 void PoseslamAlgNode::augment_trajectories(const std_msgs::Header& header)
00210 {
00211
00212
00213
00214 Trajectory_msg_.header.seq = step_;
00215 Trajectory_msg_.header.stamp = ros::Time::now();
00216 Trajectory_msg_.header.frame_id = "/world";
00217
00218 if (alg_.config_.also_no_loops)
00219 {
00220 Trajectory_msg_no_loops_.header.seq = step_;
00221 Trajectory_msg_no_loops_.header.stamp = ros::Time::now();
00222 Trajectory_msg_no_loops_.header.frame_id = "/world";
00223 }
00224
00225
00226 Trajectory_msg_.steps_2_states.push_back(-1);
00227 if (alg_.config_.also_no_loops)
00228 Trajectory_msg_no_loops_.steps_2_states.push_back(-1);
00229
00230
00231 Trajectory_msg_.poses.push_back(create_PoseWithCovarianceStamped(header, alg_.get_last_pose(), alg_.get_last_covariance()));
00232 Trajectory_poses_.push_back(alg_.get_last_pose());
00233
00234 if (alg_.config_.also_no_loops)
00235 Trajectory_msg_no_loops_.poses.push_back(create_PoseWithCovarianceStamped(header, alg_.get_last_pose_no_loops(), alg_.get_last_covariance_no_loops()));
00236 }
00237
00238 void PoseslamAlgNode::update_trajectories(const bool& LoopClosed)
00239 {
00240
00241
00242
00243 if (!alg_.is_redundant())
00244 {
00245 Trajectory_msg_.states_2_steps.push_back(step_);
00246 Trajectory_msg_.steps_2_states.back() = Trajectory_msg_.states_2_steps.size() - 1;
00247 if (alg_.config_.also_no_loops)
00248 {
00249 Trajectory_msg_no_loops_.states_2_steps.push_back(step_);
00250 Trajectory_msg_no_loops_.steps_2_states.back() = Trajectory_msg_no_loops_.states_2_steps.size() - 1;
00251 }
00252 ROS_DEBUG("POSE SLAM: Update traj: STATE %u - step_ %u", uint(Trajectory_msg_.states_2_steps.size() - 1), step_);
00253 }
00254
00255
00256 if (LoopClosed)
00257 recompute_trajectory();
00258 }
00259
00260 void PoseslamAlgNode::print_results() const
00261 {
00262
00263 std::fstream file;
00264 file.open("/home/jvallve/poseSLAM_results.m",std::ios::out);
00265 if (file.is_open())
00266 {
00267
00268 this->alg_.print_time_file(file);
00269
00270 file << "time_publishing";
00271 this->alg_.print_vector_in_file(this->alg_.timePub, file);
00272 file << "time_sensing_open";
00273 this->alg_.print_vector_in_file(this->alg_.timeSensOpen, file);
00274 file << "time_sensing_close";
00275 this->alg_.print_vector_in_file(this->alg_.timeSensClose, file);
00276 file << "states_2_steps";
00277 this->alg_.print_vector_in_file(alg_.get_trajectory_steps(), file);
00278 file << "loops";
00279 if (loops_.size() > 0)
00280 this->alg_.print_vector_in_file(loops_, file);
00281 else
00282 file << " = []";
00283 for (uint i=0; i<alg_.Q_aug_.size(); i++)
00284 {
00285 file << "Q_aug{" << i+1 << "}";
00286 this->alg_.print_matrix_in_file(alg_.Q_aug_.at(i), file);
00287 }
00288 for (uint i=0; i<alg_.Q_loop_.size(); i++)
00289 {
00290 file << "Q_loop{" << i+1 << "}";
00291 this->alg_.print_matrix_in_file(alg_.Q_loop_.at(i), file);
00292 }
00293 for (uint i=0; i<alg_.d_aug_.size(); i++)
00294 {
00295 file << "d_aug{" << i+1 << "}";
00296 this->alg_.print_matrix_in_file(alg_.d_aug_.at(i), file);
00297 }
00298 for (uint i=0; i<alg_.d_loop_.size(); i++)
00299 {
00300 file << "d_loop{" << i+1 << "}";
00301 this->alg_.print_matrix_in_file(alg_.d_loop_.at(i), file);
00302 }
00303 for (uint i=0; i<alg_.d_loop_.size(); i++)
00304 {
00305 file << "success_loop{" << i+1 << "}";
00306 if (alg_.success_loop_.at(i))
00307 file << "= 1;\n";
00308 else
00309 file << "= 0;\n";
00310 }
00311 }
00312 }
00313
00314 void PoseslamAlgNode::recompute_trajectory()
00315 {
00316
00317
00318
00319 std::vector<VectorXd> non_redundant_trajectory_poses = alg_.get_trajectory();
00320 std::vector<std::vector<double> > non_redundant_trajectory_cov = alg_.get_trajectory_covariance();
00321
00322 std::vector<double> empty_cov (9,0);
00323
00324
00325
00326 for (uint i = 0; i < Trajectory_msg_.states_2_steps.size() - 1; i++)
00327 {
00328 uint initial_step = Trajectory_msg_.states_2_steps.at(i);
00329 uint last_step = Trajectory_msg_.states_2_steps.at(i + 1);
00330
00331
00332 std::vector<VectorXd> new_segment = recompute_segment(non_redundant_trajectory_poses.at(i), non_redundant_trajectory_poses.at(i + 1), initial_step, last_step);
00333
00334
00335
00336
00337 Trajectory_msg_.poses.at(initial_step).pose = create_PoseWithCovariance(non_redundant_trajectory_poses.at(i), non_redundant_trajectory_cov.at(i));
00338
00339
00340
00341 for (uint j = 0; j < new_segment.size(); j++)
00342 {
00343
00344 Trajectory_msg_.poses.at(initial_step + j + 1).pose = create_PoseWithCovariance(new_segment.at(j), empty_cov);
00345 Trajectory_poses_.at(initial_step + j + 1) = new_segment.at(j);
00346 }
00347 }
00348
00349
00350 Trajectory_msg_.poses.back().pose = create_PoseWithCovariance(non_redundant_trajectory_poses.back(), non_redundant_trajectory_cov.back());
00351
00352
00353
00354
00355
00356 for (uint i = Trajectory_msg_.loops.size(); i < loops_.size(); i++)
00357 Trajectory_msg_.loops.push_back(loops_.at(i));
00358
00359
00360 for (uint i = 0; i < Trajectory_msg_.states_2_steps.size(); i++)
00361 Trajectory_poses_.at(Trajectory_msg_.states_2_steps.at(i)) = non_redundant_trajectory_poses.at(i);
00362
00363 }
00364
00365 std::vector<VectorXd> PoseslamAlgNode::recompute_segment(const VectorXd& new_initial_pose, const VectorXd& new_final_pose, const int& initial_step, const int& final_step)
00366 {
00367 std::vector<VectorXd> segment_poses;
00368
00369
00370 std::vector<VectorXd> old_segment_poses;
00371 old_segment_poses.assign(Trajectory_poses_.begin() + initial_step + 1, Trajectory_poses_.begin() + final_step);
00372
00373 VectorXd old_initial_pose = Trajectory_poses_.at(initial_step);
00374 VectorXd old_final_pose = Trajectory_poses_.at(final_step);
00375
00376 VectorXd old_d = old_final_pose - old_initial_pose;
00377 Vector2d old_dxy = old_d.head(2);
00378 double old_dist = sqrt(old_dxy.dot(old_dxy));
00379
00380
00381 VectorXd new_d = new_final_pose - new_initial_pose;
00382 Vector2d new_dxy = new_d.head(2);
00383 double new_dist = sqrt(new_dxy.dot(new_dxy));
00384
00385
00386 double xy_scale = new_dist / old_dist;
00387 double th_scale = pi_2_pi(new_d(2)) / pi_2_pi(old_d(2));
00388
00389 double rotation = angle_between_2D_vectors(old_dxy, new_dxy);
00390 MatrixXd R = rotation_matrix(rotation);
00391
00392 for (uint i = 0; i < old_segment_poses.size(); i++)
00393 {
00394
00395 VectorXd d_i = old_segment_poses.at(i) - old_initial_pose;
00396
00397
00398 d_i.head(2) = xy_scale * d_i.head(2);
00399 d_i(2) = th_scale * pi_2_pi(d_i(2));
00400
00401
00402 d_i = R * d_i;
00403
00404
00405 VectorXd pose_i = new_initial_pose + d_i;
00406
00407
00408 segment_poses.push_back(pose_i);
00409 }
00410
00411 return segment_poses;
00412 }
00413
00414 geometry_msgs::PoseWithCovarianceStamped PoseslamAlgNode::create_PoseWithCovarianceStamped(const std_msgs::Header& header, const VectorXd& last_pose, const std::vector<double>& last_cov)
00415 {
00416 geometry_msgs::PoseWithCovarianceStamped new_pose;
00417
00418 new_pose.header = header;
00419 new_pose.header.frame_id = "/world";
00420 new_pose.pose = create_PoseWithCovariance(last_pose, last_cov);
00421
00422 return new_pose;
00423 }
00424
00425 geometry_msgs::PoseWithCovariance PoseslamAlgNode::create_PoseWithCovariance(const VectorXd& last_pose, const std::vector<double>& last_cov)
00426 {
00427 geometry_msgs::PoseWithCovariance new_pose;
00428
00429 new_pose.pose.position.x = last_pose(0);
00430 new_pose.pose.position.y = last_pose(1);
00431 new_pose.pose.position.z = dz_footprint_2_base_;
00432 new_pose.pose.orientation = tf::createQuaternionMsgFromYaw(last_pose(2));
00433
00434 for (uint j = 0; j < 9; j++)
00435 new_pose.covariance.at(j) = last_cov.at(j);
00436
00437 return new_pose;
00438 }
00439
00440 MatrixXd PoseslamAlgNode::rotation_matrix(const double &alpha) const
00441 {
00442 MatrixXd rot = MatrixXd::Identity(3,3);
00443
00444 rot(0,0) = cos(alpha);
00445 rot(0,1) = -sin(alpha);
00446 rot(1,0) = sin(alpha);
00447 rot(1,1) = cos(alpha);
00448
00449 return rot;
00450 }
00451
00452 double PoseslamAlgNode::angle_between_2D_vectors(const Vector2d& v, const Vector2d& w) const
00453 {
00454 Vector3d v3D = MatrixXd::Zero(3,1);
00455 Vector3d w3D = MatrixXd::Zero(3,1);
00456
00457 v3D.head(2) = v;
00458 w3D.head(2) = w;
00459
00460 Vector3d cross_prod = v3D.cross(w3D);
00461
00462 double v_norm = sqrt(v.dot(v));
00463 double w_norm = sqrt(w.dot(w));
00464 double cross_norm = sqrt(cross_prod.dot(cross_prod));
00465 double sign = 1;
00466 if (cross_prod(2) < 0)
00467 sign = -1;
00468
00469 return pi_2_pi(asin(sign * cross_norm / (v_norm * w_norm)));
00470 }
00471
00472 double PoseslamAlgNode::pi_2_pi(const double& angle) const
00473 {
00474 return angle - 2 * M_PI * floor((angle + M_PI)/(2 * M_PI));
00475 }