$search
00001 #include <stdexcept> 00002 00003 #include <boost/filesystem/fstream.hpp> 00004 #include <boost/format.hpp> 00005 #include <boost/scope_exit.hpp> 00006 #include <boost/version.hpp> 00007 00008 #include <dynamic_reconfigure/server.h> 00009 #include <geometry_msgs/PoseWithCovarianceStamped.h> 00010 #include <image_proc/advertisement_checker.h> 00011 #include <image_transport/image_transport.h> 00012 #include <ros/param.h> 00013 #include <ros/ros.h> 00014 #include <ros/transport_hints.h> 00015 #include <sensor_msgs/Image.h> 00016 #include <std_msgs/String.h> 00017 #include <tf/transform_broadcaster.h> 00018 00019 #include <visp_tracker/MovingEdgeSites.h> 00020 00021 #include <boost/bind.hpp> 00022 #include <visp/vpExponentialMap.h> 00023 #include <visp/vpImage.h> 00024 #include <visp/vpImageConvert.h> 00025 #include <visp/vpCameraParameters.h> 00026 #include <visp/vpMbEdgeTracker.h> 00027 00028 #include "conversion.hh" 00029 #include "callbacks.hh" 00030 #include "file.hh" 00031 #include "names.hh" 00032 00033 #include "tracker.hh" 00034 00035 // TODO: 00036 // - add a topic allowing to suggest an estimation of the cMo 00037 // - handle automatic reset when tracking is lost. 00038 00039 namespace visp_tracker 00040 { 00041 bool 00042 Tracker::initCallback(visp_tracker::Init::Request& req, 00043 visp_tracker::Init::Response& res) 00044 { 00045 ROS_INFO("Initialization request received."); 00046 00047 res.initialization_succeed = false; 00048 00049 // If something goes wrong, rollback all changes. 00050 BOOST_SCOPE_EXIT((&res)(&tracker_)(&state_) 00051 (&lastTrackedImage_)) 00052 { 00053 if(!res.initialization_succeed) 00054 { 00055 tracker_.resetTracker(); 00056 state_ = WAITING_FOR_INITIALIZATION; 00057 lastTrackedImage_ = 0; 00058 } 00059 } BOOST_SCOPE_EXIT_END; 00060 00061 std::string fullModelPath; 00062 boost::filesystem::ofstream modelStream; 00063 00064 // Load model from parameter. 00065 if (!makeModelFile(modelStream, fullModelPath)) 00066 return true; 00067 00068 // Load moving edges. 00069 vpMe movingEdge; 00070 convertInitRequestToVpMe(req, tracker_, movingEdge); 00071 00072 // Update parameters. 00073 visp_tracker::MovingEdgeConfig config; 00074 convertVpMeToMovingEdgeConfig(movingEdge, tracker_, config); 00075 reconfigureSrv_.updateConfig(config); 00076 00077 //FIXME: not sure if this is needed. 00078 movingEdge.initMask(); 00079 00080 // Reset the tracker and the node state. 00081 tracker_.resetTracker(); 00082 state_ = WAITING_FOR_INITIALIZATION; 00083 lastTrackedImage_ = 0; 00084 00085 tracker_.setMovingEdge(movingEdge); 00086 00087 // Load the model. 00088 try 00089 { 00090 ROS_DEBUG_STREAM("Trying to load the model: " << fullModelPath); 00091 tracker_.loadModel(fullModelPath.c_str()); 00092 modelStream.close(); 00093 } 00094 catch(...) 00095 { 00096 ROS_ERROR_STREAM("Failed to load the model: " << fullModelPath); 00097 return true; 00098 } 00099 ROS_DEBUG("Model has been successfully loaded."); 00100 00101 // Load the initial cMo. 00102 transformToVpHomogeneousMatrix(cMo_, req.initial_cMo); 00103 00104 // Enable covariance matrix. 00105 tracker_.setCovarianceComputation(true); 00106 00107 // Try to initialize the tracker. 00108 ROS_INFO_STREAM("Initializing tracker with cMo:\n" << cMo_); 00109 try 00110 { 00111 tracker_.initFromPose(image_, cMo_); 00112 ROS_INFO("Tracker successfully initialized."); 00113 00114 movingEdge.print(); 00115 } 00116 catch(const std::string& str) 00117 { 00118 ROS_ERROR_STREAM("Tracker initialization has failed: " << str); 00119 } 00120 00121 // Initialization is valid. 00122 res.initialization_succeed = true; 00123 state_ = TRACKING; 00124 return true; 00125 } 00126 00127 void 00128 Tracker::updateMovingEdgeSites(visp_tracker::MovingEdgeSitesPtr sites) 00129 { 00130 if (!sites) 00131 return; 00132 00133 std::list<vpMbtDistanceLine*> linesList; 00134 tracker_.getLline(linesList, 0); 00135 00136 std::list<vpMbtDistanceLine*>::iterator linesIterator = 00137 linesList.begin(); 00138 if (linesList.empty()) 00139 ROS_DEBUG_THROTTLE(10, "no distance lines"); 00140 bool noVisibleLine = true; 00141 for (; linesIterator != linesList.end(); ++linesIterator) 00142 { 00143 vpMbtDistanceLine* line = *linesIterator; 00144 00145 if (line && line->isVisible()) 00146 { 00147 std::list<vpMeSite>::const_iterator sitesIterator = 00148 line->meline->list.begin(); 00149 if (line->meline->list.empty()) 00150 ROS_DEBUG_THROTTLE(10, "no moving edge for a line"); 00151 for (; sitesIterator != line->meline->list.end(); ++sitesIterator) 00152 { 00153 visp_tracker::MovingEdgeSite movingEdgeSite; 00154 movingEdgeSite.x = sitesIterator->ifloat; 00155 movingEdgeSite.y = sitesIterator->jfloat; 00156 movingEdgeSite.suppress = sitesIterator->suppress; 00157 sites->moving_edge_sites.push_back (movingEdgeSite); 00158 } 00159 noVisibleLine = false; 00160 } 00161 } 00162 if (noVisibleLine) 00163 ROS_DEBUG_THROTTLE(10, "no distance lines"); 00164 } 00165 00166 void Tracker::checkInputs() 00167 { 00168 ros::V_string topics; 00169 topics.push_back(rectifiedImageTopic_); 00170 checkInputs_.start(topics, 60.0); 00171 } 00172 00173 Tracker::Tracker(ros::NodeHandle& nh, 00174 ros::NodeHandle& privateNh, 00175 volatile bool& exiting, 00176 unsigned queueSize) 00177 : exiting_ (exiting), 00178 queueSize_(queueSize), 00179 nodeHandle_(nh), 00180 nodeHandlePrivate_(privateNh), 00181 imageTransport_(nodeHandle_), 00182 state_(WAITING_FOR_INITIALIZATION), 00183 image_(), 00184 cameraPrefix_(), 00185 rectifiedImageTopic_(), 00186 cameraInfoTopic_(), 00187 vrmlPath_(), 00188 cameraSubscriber_(), 00189 mutex_ (), 00190 reconfigureSrv_(mutex_, nodeHandlePrivate_), 00191 resultPublisher_(), 00192 transformationPublisher_(), 00193 movingEdgeSitesPublisher_(), 00194 initService_(), 00195 header_(), 00196 info_(), 00197 movingEdge_(), 00198 cameraParameters_(), 00199 tracker_(), 00200 lastTrackedImage_(), 00201 checkInputs_(nodeHandle_, ros::this_node::getName()), 00202 cMo_ (), 00203 listener_ (), 00204 worldFrameId_ (), 00205 compensateRobotMotion_ (false), 00206 transformBroadcaster_ (), 00207 childFrameId_ (), 00208 objectPositionHintSubscriber_ (), 00209 objectPositionHint_ () 00210 { 00211 // Set cMo to identity. 00212 cMo_.eye(); 00213 00214 // Parameters. 00215 nodeHandlePrivate_.param<std::string>("camera_prefix", cameraPrefix_, ""); 00216 00217 if (cameraPrefix_.empty ()) 00218 { 00219 ROS_FATAL 00220 ("The camera_prefix parameter not set.\n" 00221 "Please relaunch the tracker while setting this parameter, i.e.\n" 00222 "$ rosrun visp_tracker tracker _camera_prefix:=/my/camera"); 00223 ros::shutdown (); 00224 return; 00225 } 00226 nodeHandle_.setParam("camera_prefix", cameraPrefix_); 00227 00228 nodeHandle_.param<std::string>("frame_id", childFrameId_, "object_position"); 00229 00230 // Robot motion compensation. 00231 nodeHandle_.param<std::string>("world_frame_id", worldFrameId_, "/odom"); 00232 nodeHandle_.param<bool> 00233 ("compensate_robot_motion", compensateRobotMotion_, false); 00234 00235 // Compute topic and services names. 00236 rectifiedImageTopic_ = 00237 ros::names::resolve(cameraPrefix_ + "/image_rect"); 00238 00239 // Check for subscribed topics. 00240 checkInputs(); 00241 00242 // Result publisher. 00243 resultPublisher_ = 00244 nodeHandle_.advertise<geometry_msgs::PoseWithCovarianceStamped> 00245 (visp_tracker::object_position_covariance_topic, queueSize_); 00246 00247 transformationPublisher_ = 00248 nodeHandle_.advertise<geometry_msgs::TransformStamped> 00249 (visp_tracker::object_position_topic, queueSize_); 00250 00251 // Moving edge sites_ publisher. 00252 movingEdgeSitesPublisher_ = 00253 nodeHandle_.advertise<visp_tracker::MovingEdgeSites> 00254 (visp_tracker::moving_edge_sites_topic, queueSize_); 00255 00256 // Camera subscriber. 00257 cameraSubscriber_ = 00258 imageTransport_.subscribeCamera 00259 (rectifiedImageTopic_, queueSize_, 00260 bindImageCallback(image_, header_, info_)); 00261 00262 // Object position hint subscriber. 00263 typedef boost::function< 00264 void (const geometry_msgs::TransformStampedConstPtr&)> 00265 objectPositionHintCallback_t; 00266 objectPositionHintCallback_t callback = 00267 boost::bind (&Tracker::objectPositionHintCallback, this, _1); 00268 objectPositionHintSubscriber_ = 00269 nodeHandle_.subscribe<geometry_msgs::TransformStamped> 00270 ("object_position_hint", queueSize_, callback); 00271 00272 // Initialization. 00273 movingEdge_.initMask(); 00274 tracker_.setMovingEdge(movingEdge_); 00275 00276 // Dynamic reconfigure. 00277 reconfigureSrv_t::CallbackType f = 00278 boost::bind(&reconfigureCallback, boost::ref(tracker_), 00279 boost::ref(image_), boost::ref(movingEdge_), 00280 boost::ref(mutex_), _1, _2); 00281 reconfigureSrv_.setCallback(f); 00282 00283 // Wait for the image to be initialized. 00284 waitForImage(); 00285 if (this->exiting()) 00286 return; 00287 if (!image_.getWidth() || !image_.getHeight()) 00288 throw std::runtime_error("failed to retrieve image"); 00289 00290 // Tracker initialization. 00291 initializeVpCameraFromCameraInfo(cameraParameters_, info_); 00292 00293 // Double check camera parameters. 00294 if (cameraParameters_.get_px () == 0. 00295 || cameraParameters_.get_px () == 1. 00296 || cameraParameters_.get_py () == 0. 00297 || cameraParameters_.get_py () == 1. 00298 || cameraParameters_.get_u0 () == 0. 00299 || cameraParameters_.get_u0 () == 1. 00300 || cameraParameters_.get_v0 () == 0. 00301 || cameraParameters_.get_v0 () == 1.) 00302 ROS_WARN ("Dubious camera parameters detected.\n" 00303 "\n" 00304 "It seems that the matrix P from your camera\n" 00305 "calibration topics is wrong.\n" 00306 "The tracker will continue anyway, but you\n" 00307 "should double check your calibration data,\n" 00308 "especially if the model re-projection fails.\n" 00309 "\n" 00310 "This warning is triggered is px, py, u0 or v0\n" 00311 "is set to 0. or 1. (exactly)."); 00312 00313 tracker_.setCameraParameters(cameraParameters_); 00314 tracker_.setDisplayMovingEdges(false); 00315 00316 ROS_INFO_STREAM(cameraParameters_); 00317 00318 // Service declaration. 00319 initCallback_t initCallback = 00320 boost::bind(&Tracker::initCallback, this, _1, _2); 00321 00322 initService_ = nodeHandle_.advertiseService 00323 (visp_tracker::init_service, initCallback); 00324 } 00325 00326 void Tracker::spin() 00327 { 00328 ros::Rate loopRateTracking(100); 00329 tf::Transform transform; 00330 std_msgs::Header lastHeader; 00331 00332 while (!exiting()) 00333 { 00334 // When a camera sequence is played several times, 00335 // the seq id will decrease, in this case we want to 00336 // continue the tracking. 00337 if (header_.seq < lastHeader.seq) 00338 lastTrackedImage_ = 0; 00339 00340 if (lastTrackedImage_ < header_.seq) 00341 { 00342 lastTrackedImage_ = header_.seq; 00343 00344 // If we can estimate the camera displacement using tf, 00345 // we update the cMo to compensate for robot motion. 00346 if (compensateRobotMotion_) 00347 try 00348 { 00349 tf::StampedTransform stampedTransform; 00350 listener_.lookupTransform 00351 (header_.frame_id, // camera frame name 00352 header_.stamp, // current image time 00353 header_.frame_id, // camera frame name 00354 lastHeader.stamp, // last processed image time 00355 worldFrameId_, // frame attached to the environment 00356 stampedTransform 00357 ); 00358 vpHomogeneousMatrix newMold; 00359 transformToVpHomogeneousMatrix (newMold, stampedTransform); 00360 cMo_ = newMold * cMo_; 00361 } 00362 catch(tf::TransformException& e) 00363 {} 00364 00365 // If we are lost but an estimation of the object position 00366 // is provided, use it to try to reinitialize the system. 00367 if (state_ == LOST) 00368 { 00369 // If the last received message is recent enough, 00370 // use it otherwise do nothing. 00371 if (ros::Time::now () - objectPositionHint_.header.stamp 00372 < ros::Duration (1.)) 00373 transformToVpHomogeneousMatrix 00374 (cMo_, objectPositionHint_.transform); 00375 } 00376 00377 // We try to track the image even if we are lost, 00378 // in the case the tracker recovers... 00379 if (state_ == TRACKING || state_ == LOST) 00380 try 00381 { 00382 tracker_.initFromPose(image_, cMo_); 00383 tracker_.track(image_); 00384 tracker_.getPose(cMo_); 00385 } 00386 catch(...) 00387 { 00388 ROS_WARN_THROTTLE(10, "tracking lost"); 00389 state_ = LOST; 00390 } 00391 00392 // Publish the tracking result. 00393 if (state_ == TRACKING) 00394 { 00395 geometry_msgs::Transform transformMsg; 00396 vpHomogeneousMatrixToTransform(transformMsg, cMo_); 00397 00398 // Publish position. 00399 if (transformationPublisher_.getNumSubscribers () > 0) 00400 { 00401 geometry_msgs::TransformStampedPtr objectPosition 00402 (new geometry_msgs::TransformStamped); 00403 objectPosition->header = header_; 00404 objectPosition->child_frame_id = childFrameId_; 00405 objectPosition->transform = transformMsg; 00406 transformationPublisher_.publish(objectPosition); 00407 } 00408 00409 // Publish result. 00410 if (resultPublisher_.getNumSubscribers () > 0) 00411 { 00412 geometry_msgs::PoseWithCovarianceStampedPtr result 00413 (new geometry_msgs::PoseWithCovarianceStamped); 00414 result->header = header_; 00415 result->pose.pose.position.x = 00416 transformMsg.translation.x; 00417 result->pose.pose.position.y = 00418 transformMsg.translation.y; 00419 result->pose.pose.position.z = 00420 transformMsg.translation.z; 00421 00422 result->pose.pose.orientation.x = 00423 transformMsg.rotation.x; 00424 result->pose.pose.orientation.y = 00425 transformMsg.rotation.y; 00426 result->pose.pose.orientation.z = 00427 transformMsg.rotation.z; 00428 result->pose.pose.orientation.w = 00429 transformMsg.rotation.w; 00430 const vpMatrix& covariance = 00431 tracker_.getCovarianceMatrix(); 00432 for (unsigned i = 0; i < covariance.getRows(); ++i) 00433 for (unsigned j = 0; j < covariance.getCols(); ++j) 00434 { 00435 unsigned idx = i * covariance.getCols() + j; 00436 if (idx >= 36) 00437 continue; 00438 result->pose.covariance[idx] = covariance[i][j]; 00439 } 00440 resultPublisher_.publish(result); 00441 } 00442 00443 // Publish moving edge sites. 00444 if (movingEdgeSitesPublisher_.getNumSubscribers () > 0) 00445 { 00446 visp_tracker::MovingEdgeSitesPtr sites 00447 (new visp_tracker::MovingEdgeSites); 00448 updateMovingEdgeSites(sites); 00449 sites->header = header_; 00450 movingEdgeSitesPublisher_.publish(sites); 00451 } 00452 00453 // Publish to tf. 00454 transform.setOrigin 00455 (tf::Vector3(transformMsg.translation.x, 00456 transformMsg.translation.y, 00457 transformMsg.translation.z)); 00458 transform.setRotation 00459 (tf::Quaternion 00460 (transformMsg.rotation.x, 00461 transformMsg.rotation.y, 00462 transformMsg.rotation.z, 00463 transformMsg.rotation.w)); 00464 transformBroadcaster_.sendTransform 00465 (tf::StampedTransform 00466 (transform, 00467 header_.stamp, 00468 header_.frame_id, 00469 childFrameId_)); 00470 } 00471 } 00472 lastHeader = header_; 00473 00474 spinOnce(); 00475 loopRateTracking.sleep(); 00476 } 00477 } 00478 00479 // Make sure that we have an image *and* associated calibration 00480 // data. 00481 void 00482 Tracker::waitForImage() 00483 { 00484 ros::Rate loop_rate(10); 00485 while (!exiting() 00486 && (!image_.getWidth() || !image_.getHeight()) 00487 && (!info_ || info_->K[0] == 0.)) 00488 { 00489 ROS_INFO_THROTTLE(1, "waiting for a rectified image..."); 00490 spinOnce(); 00491 loop_rate.sleep(); 00492 } 00493 } 00494 00495 void 00496 Tracker::objectPositionHintCallback 00497 (const geometry_msgs::TransformStampedConstPtr& transform) 00498 { 00499 objectPositionHint_ = *transform; 00500 } 00501 00502 } // end of namespace visp_tracker.