00001 #include <featurenav_base/ajockey.h>
00002
00003 namespace featurenav_base
00004 {
00005
00006 const ros::Duration AJockey::max_odom_age_ = ros::Duration(0.5);
00007 const ros::Duration AJockey::max_landmark_age_ = ros::Duration(2.0);
00008
00009 AJockey::AJockey(const std::string& name, const std::string& segment_interface_name, const std::string& segment_setter_name) :
00010 LearningJockey(name),
00011 it_(private_nh_),
00012 matcher_max_relative_distance_(0.8),
00013 min_landmark_dist_(0.02),
00014 max_segment_length_(0),
00015 segment_interface_name_(segment_interface_name),
00016 segment_setter_name_(segment_setter_name),
00017 has_odom_(false),
00018 image_processing_running_(false)
00019 {
00020
00021 if(ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME, ros::console::levels::Debug))
00022 {
00023 ros::console::notifyLoggerLevelsChanged();
00024 }
00025
00026 private_nh_.getParam("matcher_max_relative_distance", matcher_max_relative_distance_);
00027 private_nh_.getParam("min_landmark_dist", min_landmark_dist_);
00028 private_nh_.getParam("max_segment_length", max_segment_length_);
00029
00030 ROS_DEBUG_STREAM("Waiting for service \"" << segment_setter_name_ << "\"");
00031 segment_setter_proxy_ = nh_.serviceClient<SetSegment>(segment_setter_name_);
00032 segment_setter_proxy_.waitForExistence();
00033 }
00034
00035 void AJockey::reset()
00036 {
00037 has_odom_ = false;
00038 segment_.distance = 0;
00039 segment_.landmarks.clear();
00040 landmarks_.clear();
00041 image_processing_running_ = false;
00042 }
00043
00044 void AJockey::onLearn()
00045 {
00046 if ((canDo == NULL) || (startDo == NULL))
00047 {
00048 ROS_ERROR("Internal initialization problem, will not start learning...");
00049 server_.setAborted();
00050 return;
00051 }
00052 if (extract_features_ == NULL)
00053 {
00054 ROS_ERROR("extract_feature function not set, will not start learning...");
00055 server_.setAborted();
00056 return;
00057 }
00058 if (match_descriptors_ == NULL)
00059 {
00060 ROS_ERROR("match_descriptor function not set, will not start learning...");
00061 server_.setAborted();
00062 return;
00063 }
00064
00065 odom_handler_ = private_nh_.subscribe("odom", 1, &AJockey::callback_odom, this);
00066
00067
00068 ros::Duration(0.2).sleep();
00069 ros::spinOnce();
00070 while (!has_odom_ && ros::ok())
00071 {
00072 ros::spinOnce();
00073 ROS_WARN_STREAM_THROTTLE(5, "Waiting for odometry on topic " << odom_handler_.getTopic());
00074 ros::Duration(0.01).sleep();
00075 }
00076 ROS_DEBUG("Received odometry");
00077
00078
00079 start_pose_ = odom_.pose.pose;
00080 const double start_angle = tf::getYaw(start_pose_.orientation);
00081 segment_.yaw = start_angle;
00082
00083 image_handler_ = it_.subscribe("camera/image_raw", 1, &AJockey::callback_image, this);
00084
00085 feedback_.current_state = lama_jockeys::LearnFeedback::LEARNING;
00086 feedback_.completion = -1;
00087 server_.publishFeedback(feedback_);
00088
00089 ros::Rate r(100);
00090 unsigned int feedback_publish_counter = 0;
00091 while (ros::ok())
00092 {
00093 ros::spinOnce();
00094 if (server_.isPreemptRequested() && !ros::ok())
00095 {
00096 ROS_INFO("Preempted");
00097
00098
00099
00100 break;
00101 }
00102
00103 if (std::abs(max_segment_length_) > 1e-10)
00104 {
00105 const double d = distance_from_start();
00106 if ((feedback_publish_counter % 50) == 0)
00107 {
00108 feedback_.completion = d / max_segment_length_;
00109 feedback_.time_elapsed = getCompletionDuration();
00110 server_.publishFeedback(feedback_);
00111 }
00112 if (d > max_segment_length_)
00113 {
00114 ROS_INFO("Max segment length (%.3f m) reached, successfully finishing", max_segment_length_);
00115 onStop();
00116 break;
00117 }
00118 }
00119 feedback_publish_counter++;
00120 r.sleep();
00121 }
00122
00123 ROS_DEBUG("Exiting onLearn");
00124 }
00125
00126 void AJockey::onStop()
00127 {
00128 ROS_DEBUG("Received ON_STOP_LEARN or stopping after max segment length reached");
00129
00130 if (server_.isPreemptRequested() && !ros::ok())
00131 {
00132 ROS_INFO("Preempted");
00133
00134
00135
00136 image_handler_.shutdown();
00137 odom_handler_.shutdown();
00138 reset();
00139 return;
00140 }
00141
00142 image_handler_.shutdown();
00143 odom_handler_.shutdown();
00144
00145
00146 ROS_DEBUG("Waiting for processImage to finish");
00147 ros::Rate r(1000);
00148 while (ros::ok())
00149 {
00150 if (!image_processing_running_)
00151 {
00152 break;
00153 }
00154 r.sleep();
00155 ros::spinOnce();
00156 }
00157 ROS_DEBUG("processImage finished");
00158
00159 segment_.distance = distance_from_start();
00160 for (size_t i = 0; i < landmarks_.size(); ++i)
00161 {
00162 if (std::abs(landmarks_[i].landmark.du - landmarks_[i].landmark.dv) > min_landmark_dist_)
00163 {
00164
00165 segment_.landmarks.push_back(landmarks_[i].landmark);
00166 }
00167 }
00168
00169
00170 ROS_INFO("Saving a segment with %zu landmarks", segment_.landmarks.size());
00171 SetSegment segment_setter;
00172 segment_setter.request.descriptor = segment_;
00173 if (!segment_setter_proxy_.call(segment_setter))
00174 {
00175 ROS_ERROR("Failed to add Segment to the map");
00176 server_.setAborted();
00177 return;
00178 }
00179 ROS_DEBUG("Added Segment with id %d", segment_setter.response.id);
00180 result_.descriptor_links.push_back(segmentDescriptorLink(segment_setter.response.id));
00181 server_.setSucceeded(result_);
00182 reset();
00183 }
00184
00185 void AJockey::onInterrupt()
00186 {
00187
00188
00189 }
00190
00191 void AJockey::onContinue()
00192 {
00193 }
00194
00195 void AJockey::callback_image(const sensor_msgs::ImageConstPtr& msg)
00196 {
00197 ros::Time start_time = ros::Time::now();
00198
00199 if (!has_odom_)
00200 {
00201 ROS_WARN("No Odometry received, ignoring image");
00202 return;
00203 }
00204
00205 if ((odom_.header.stamp < msg->header.stamp) && (msg->header.stamp - odom_.header.stamp) > max_odom_age_)
00206 {
00207 ROS_WARN("Odometry is too old, ignoring image");
00208 return;
00209 }
00210
00211 processImage(msg);
00212 ROS_DEBUG("Computation time: %.3f", (ros::Time::now().toSec() - start_time.toSec()));
00213 }
00214
00215 void AJockey::callback_odom(const nav_msgs::OdometryConstPtr& msg)
00216 {
00217 if ((ros::Time::now() - msg->header.stamp) < max_odom_age_)
00218 {
00219 odom_ = *msg;
00220 has_odom_ = true;
00221 return;
00222 }
00223 ROS_WARN("Odometry received but too old (%.3f s)", (ros::Time::now() - msg->header.stamp).toSec());
00224 }
00225
00233 size_t AJockey::processImage(const sensor_msgs::ImageConstPtr& image)
00234 {
00235 if (extract_features_ == NULL || match_descriptors_ == NULL)
00236 {
00237 ROS_ERROR("Extract_feature or match_descriptors function not set, doing nothing...");
00238 return 0;
00239 }
00240
00241 const double d = distance_from_start();
00242
00243 image_processing_running_ = true;
00244
00245 vector<KeyPoint> keypoints;
00246 vector<Feature> descriptors;
00247 try
00248 {
00249 extract_features_(image, keypoints, descriptors);
00250 }
00251 catch (std::exception)
00252 {
00253 ROS_ERROR("Error caught on extract_features, ignoring image");
00254 return 0;
00255 }
00256
00257 if (descriptors.empty())
00258 {
00259
00260 ROS_DEBUG("No detected feature, waiting for next image");
00261 image_processing_running_ = false;
00262 return 0;
00263 }
00264
00265 if (keypoints.size() != descriptors.size())
00266 {
00267 ROS_ERROR("keypoints and descriptors differ in call of extract_features function");
00268 image_processing_running_ = false;
00269 return 0;
00270 }
00271
00272 ROS_DEBUG_STREAM("Number of detected features: " << keypoints.size());
00273
00274
00275 vector<Feature> query_descriptors;
00276 query_descriptors.reserve(landmarks_.size());
00277 for (size_t i = 0; i < landmarks_.size(); ++i)
00278 {
00279 query_descriptors.push_back(landmarks_[i].landmark.descriptor);
00280 }
00281 vector<vector<DMatch> > matches;
00282 try
00283 {
00284 match_descriptors_(query_descriptors, descriptors, matches);
00285 }
00286 catch (std::exception)
00287 {
00288 ROS_ERROR("Error caught on match_descriptors, ignoring image");
00289 return 0;
00290 }
00291
00292 if (matches.empty())
00293 {
00294 ROS_ERROR_STREAM("No match found");
00295 }
00296
00297 vector<size_t> used_features_idx;
00298 vector<size_t> landmark_to_save;
00299 vector<size_t> landmark_to_delete;
00300 for (size_t i = 0; i < matches.size(); ++i)
00301 {
00302 if (matches[i].size() < 2)
00303 {
00304 ROS_DEBUG("Not enough matches, found %zu", matches[i].size());
00305 continue;
00306 }
00307
00308 const size_t landmark_idx = matches[i][0].queryIdx;
00309 const size_t feature_idx = matches[i][0].trainIdx;
00310
00311 if (matches[i][0].distance < matcher_max_relative_distance_ * matches[i][1].distance)
00312 {
00313
00314 landmarks_[landmark_idx].landmark.dv = d;
00315 landmarks_[landmark_idx].landmark.v.x = keypoints[feature_idx].pt.x;
00316 landmarks_[landmark_idx].landmark.v.y = keypoints[feature_idx].pt.y;
00317 landmarks_[landmark_idx].last_seen = image->header.stamp;
00318
00319 used_features_idx.push_back(feature_idx);
00320 }
00321 else
00322 {
00323
00324 if (std::abs(landmarks_[landmark_idx].landmark.du - landmarks_[landmark_idx].landmark.dv) > min_landmark_dist_)
00325 {
00326
00327 landmark_to_save.push_back(landmark_idx);
00328 }
00329
00330 landmark_to_delete.push_back(landmark_idx);
00331 }
00332 }
00333
00334 std::sort(landmark_to_save.begin(), landmark_to_save.end());
00335 segment_.landmarks.reserve(segment_.landmarks.size() + landmark_to_save.size());
00336 int last_index = -1;
00337 for (int i = 0; i < landmark_to_save.size(); i++)
00338 {
00339 if (i != last_index)
00340 {
00341 segment_.landmarks.push_back(landmarks_[landmark_to_save[i]].landmark);
00342 }
00343 last_index = i;
00344 }
00345
00346 std::sort(landmark_to_delete.begin(), landmark_to_delete.end());
00347 for (int i = landmark_to_delete.size() - 1; i >= 0; --i)
00348 {
00349 if (landmark_to_delete[i] < landmarks_.size())
00350 {
00351 landmarks_.erase(landmarks_.begin() + landmark_to_delete[i]);
00352 }
00353 }
00354
00355 ROS_DEBUG("Number of landmarks added to segment: %zu", landmark_to_save.size());
00356 ROS_DEBUG("Number of features already as potential landmarks: %zu", used_features_idx.size());
00357
00358
00359
00360 std::sort(used_features_idx.begin(), used_features_idx.end());
00361 for (size_t i = 0; i < keypoints.size(); ++i)
00362 {
00363 if (!std::binary_search(used_features_idx.begin(), used_features_idx.end(), i))
00364 {
00365
00366 landmarks_.push_back(PotentialLandmark(descriptors[i], image->header.stamp, keypoints[i].pt.x, keypoints[i].pt.y, d));
00367 }
00368 }
00369
00370
00371 size_t cleanedup_landmarks_count = landmarks_.size();
00372 for (int i = landmarks_.size() - 1; i >= 0; --i)
00373 {
00374 if ((image->header.stamp - landmarks_[i].last_seen) > max_landmark_age_)
00375 {
00376 landmarks_.erase(landmarks_.begin() + i);
00377 }
00378 }
00379 cleanedup_landmarks_count -= landmarks_.size();
00380 ROS_DEBUG("Number of cleaned-up potential landmarks: %zu", cleanedup_landmarks_count);
00381
00382 ROS_DEBUG("Number of potential landmarks: %zu", landmarks_.size());
00383 ROS_DEBUG("Number of learned landmarks: %zu", segment_.landmarks.size());
00384 ROS_DEBUG("Distance from the start [m]: %.3f", d);
00385
00386 image_processing_running_ = false;
00387 return landmarks_.size();
00388 }
00389
00390 }
00391