ajockey.cpp
Go to the documentation of this file.
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   // Debug log level
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   // Waiting for the first odometry message.
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   // We got odom_, save as start_pose_.
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       // set the action state to preempted
00098       // TODO: should the server be preempted?
00099       // server_.setPreempted();
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     // set the action state to preempted
00134     // TODO: should the server be preempted?
00135     // server_.setPreempted();
00136     image_handler_.shutdown();
00137     odom_handler_.shutdown();
00138     reset();
00139     return;
00140   }
00141 
00142   image_handler_.shutdown();
00143   odom_handler_.shutdown();
00144 
00145   // Wait for processImage to finish.
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       // The feature was visible over a certain distance, save it into segment_.
00165       segment_.landmarks.push_back(landmarks_[i].landmark);
00166     }
00167   }
00168 
00169   // Save the segment into the database and return its id.
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   // TODO: discuss with Karel what to do on interrupt:
00188   // - wait and do nothing.
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     // If no feature was detected.
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   // Match descriptors.
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       // Feature is still visible, update corresponding landmark.
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       // Save matched feature index to know which features to add as potential landmark.
00319       used_features_idx.push_back(feature_idx);
00320     }
00321     else
00322     {
00323       // The feature is not available anymore, but was available before.
00324       if (std::abs(landmarks_[landmark_idx].landmark.du - landmarks_[landmark_idx].landmark.dv) > min_landmark_dist_)
00325       {
00326         // The feature was visible over a certain distance, save it into segment_.
00327         landmark_to_save.push_back(landmark_idx);
00328       }
00329       // Delete the segment, whether it is used or not.
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   // Add new features as potential landmarks.
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       // Feature was not yet used, add it to potential landmarks.
00366       landmarks_.push_back(PotentialLandmark(descriptors[i], image->header.stamp, keypoints[i].pt.x, keypoints[i].pt.y, d));
00367     }
00368   }
00369 
00370   // Clean up old landmarks.
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 } // namespace featurenav_base
00391 


featurenav_base
Author(s): Gaƫl Ecorchard
autogenerated on Sat Jun 8 2019 19:52:22