$search
00001 /* 00002 * Gazebo - Outdoor Multi-Robot Simulator 00003 * Copyright (C) 2003 00004 * Nate Koenig & Andrew Howard 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00019 * 00020 */ 00021 /* 00022 @mainpage 00023 Desc: GazeboRosStereoCamera plugin for simulating cameras in Gazebo 00024 Author: John Hsu 00025 Date: 24 Sept 2008 00026 SVN info: $Id$ 00027 @htmlinclude manifest.html 00028 @b GazeboRosStereoCamera plugin broadcasts ROS Image messages 00029 */ 00030 00031 #include <algorithm> 00032 #include <assert.h> 00033 #include <boost/thread/thread.hpp> 00034 #include <boost/bind.hpp> 00035 00036 #include <gazebo_plugins/gazebo_ros_stereo_camera.h> 00037 00038 #include <gazebo/Timer.hh> 00039 #include <gazebo/Sensor.hh> 00040 #include <gazebo/Model.hh> 00041 #include <gazebo/Global.hh> 00042 #include <gazebo/XMLConfig.hh> 00043 #include <gazebo/Simulator.hh> 00044 #include <gazebo/gazebo.h> 00045 #include <gazebo/GazeboError.hh> 00046 #include <gazebo/ControllerFactory.hh> 00047 #include <gazebo/MonoCameraSensor.hh> 00048 00049 #include "sensor_msgs/Image.h" 00050 #include "sensor_msgs/fill_image.h" 00051 00052 #include <geometry_msgs/Point32.h> 00053 #include <sensor_msgs/ChannelFloat32.h> 00054 00055 namespace gazebo 00056 { 00057 00058 GZ_REGISTER_DYNAMIC_CONTROLLER("gazebo_ros_stereo_camera", GazeboRosStereoCamera); 00059 00061 // Constructor 00062 GazeboRosStereoCamera::GazeboRosStereoCamera(Entity *parent) 00063 : Controller(parent) 00064 { 00065 00066 this->myParent = dynamic_cast<StereoCameraSensor*>(this->parent); 00067 00068 if (!this->myParent) 00069 gzthrow("GazeboRosStereoCamera controller requires a Camera Sensor as its parent"); 00070 00071 Param::Begin(&this->parameters); 00072 this->robotNamespaceP = new ParamT<std::string>("robotNamespace","/",0); 00073 this->imageTopicNameP = new ParamT<std::string>("imageTopicName","", 1); 00074 this->pointCloudTopicNameP = new ParamT<std::string>("pointCloudTopicName","", 1); 00075 this->cameraInfoTopicNameP = new ParamT<std::string>("cameraInfoTopicName","", 1); 00076 this->frameNameP = new ParamT<std::string>("frameName","generic_camera_link", 0); 00077 // camera parameters 00078 this->CxPrimeP = new ParamT<double>("CxPrime",0, 0); // default to 0 for compute on the fly 00079 this->CxP = new ParamT<double>("Cx" ,0, 0); // default to 0 for compute on the fly 00080 this->CyP = new ParamT<double>("Cy" ,0, 0); // default to 0 for compute on the fly 00081 this->focal_lengthP = new ParamT<double>("focal_length" ,0, 0); // == image_width(px) / (2*tan( hfov(radian) /2)), default to 0 for compute on the fly 00082 this->hack_baselineP = new ParamT<double>("hackBaseline" ,0, 0); // hack for right stereo camera 00083 this->distortion_k1P = new ParamT<double>("distortion_k1" ,0, 0); 00084 this->distortion_k2P = new ParamT<double>("distortion_k2" ,0, 0); 00085 this->distortion_k3P = new ParamT<double>("distortion_k3" ,0, 0); 00086 this->distortion_t1P = new ParamT<double>("distortion_t1" ,0, 0); 00087 this->distortion_t2P = new ParamT<double>("distortion_t2" ,0, 0); 00088 Param::End(); 00089 00090 this->imageConnectCount = 0; 00091 this->pointCloudConnectCount = 0; 00092 this->infoConnectCount = 0; 00093 00094 // set sensor update rate to controller update rate? 00095 //(dynamic_cast<OgreCamera*>(this->myParent))->SetUpdateRate(this->updateRateP->GetValue()); 00096 } 00097 00099 // Destructor 00100 GazeboRosStereoCamera::~GazeboRosStereoCamera() 00101 { 00102 delete this->robotNamespaceP; 00103 delete this->rosnode_; 00104 delete this->imageTopicNameP; 00105 delete this->pointCloudTopicNameP; 00106 delete this->cameraInfoTopicNameP; 00107 delete this->frameNameP; 00108 delete this->CxPrimeP; 00109 delete this->CxP; 00110 delete this->CyP; 00111 delete this->focal_lengthP; 00112 delete this->hack_baselineP; 00113 delete this->distortion_k1P; 00114 delete this->distortion_k2P; 00115 delete this->distortion_k3P; 00116 delete this->distortion_t1P; 00117 delete this->distortion_t2P; 00118 00119 00120 00121 } 00122 00124 // Load the controller 00125 void GazeboRosStereoCamera::LoadChild(XMLConfigNode *node) 00126 { 00127 this->robotNamespaceP->Load(node); 00128 this->robotNamespace = this->robotNamespaceP->GetValue(); 00129 if (!ros::isInitialized()) 00130 { 00131 int argc = 0; 00132 char** argv = NULL; 00133 ros::init(argc,argv,"gazebo",ros::init_options::NoSigintHandler|ros::init_options::AnonymousName); 00134 } 00135 00136 this->rosnode_ = new ros::NodeHandle(this->robotNamespace); 00137 00138 this->imageTopicNameP->Load(node); 00139 this->pointCloudTopicNameP->Load(node); 00140 this->cameraInfoTopicNameP->Load(node); 00141 this->frameNameP->Load(node); 00142 this->CxPrimeP->Load(node); 00143 this->CxP->Load(node); 00144 this->CyP->Load(node); 00145 this->focal_lengthP->Load(node); 00146 this->hack_baselineP->Load(node); 00147 this->distortion_k1P->Load(node); 00148 this->distortion_k2P->Load(node); 00149 this->distortion_k3P->Load(node); 00150 this->distortion_t1P->Load(node); 00151 this->distortion_t2P->Load(node); 00152 this->imageTopicName = this->imageTopicNameP->GetValue(); 00153 this->pointCloudTopicName = this->pointCloudTopicNameP->GetValue(); 00154 this->cameraInfoTopicName = this->cameraInfoTopicNameP->GetValue(); 00155 this->frameName = this->frameNameP->GetValue(); 00156 this->CxPrime = this->CxPrimeP->GetValue(); 00157 this->Cx = this->CxP->GetValue(); 00158 this->Cy = this->CyP->GetValue(); 00159 this->focal_length = this->focal_lengthP->GetValue(); 00160 this->hack_baseline = this->hack_baselineP->GetValue(); 00161 this->distortion_k1 = this->distortion_k1P->GetValue(); 00162 this->distortion_k2 = this->distortion_k2P->GetValue(); 00163 this->distortion_k3 = this->distortion_k3P->GetValue(); 00164 this->distortion_t1 = this->distortion_t1P->GetValue(); 00165 this->distortion_t2 = this->distortion_t2P->GetValue(); 00166 00167 #ifdef USE_CBQ 00168 ros::AdvertiseOptions image_ao = ros::AdvertiseOptions::create<sensor_msgs::Image>( 00169 this->imageTopicName,1, 00170 boost::bind( &GazeboRosStereoCamera::ImageConnect,this), 00171 boost::bind( &GazeboRosStereoCamera::ImageDisconnect,this), ros::VoidPtr(), &this->camera_queue_); 00172 this->image_pub_ = this->rosnode_->advertise(image_ao); 00173 00174 ros::AdvertiseOptions point_cloud_ao = ros::AdvertiseOptions::create<sensor_msgs::PointCloud>( 00175 this->pointCloudTopicName,1, 00176 boost::bind( &GazeboRosStereoCamera::PointCloudConnect,this), 00177 boost::bind( &GazeboRosStereoCamera::PointCloudDisconnect,this), ros::VoidPtr(), &this->camera_queue_); 00178 this->point_cloud_pub_ = this->rosnode_->advertise(point_cloud_ao); 00179 00180 ros::AdvertiseOptions camera_info_ao = ros::AdvertiseOptions::create<sensor_msgs::CameraInfo>( 00181 this->cameraInfoTopicName,1, 00182 boost::bind( &GazeboRosStereoCamera::InfoConnect,this), 00183 boost::bind( &GazeboRosStereoCamera::InfoDisconnect,this), ros::VoidPtr(), &this->camera_queue_); 00184 this->camera_info_pub_ = this->rosnode_->advertise(camera_info_ao); 00185 #else 00186 this->image_pub_ = this->rosnode_->advertise<sensor_msgs::Image>(this->imageTopicName,1, 00187 boost::bind( &GazeboRosStereoCamera::ImageConnect, this), 00188 boost::bind( &GazeboRosStereoCamera::ImageDisconnect, this)); 00189 this->point_cloud_pub_ = this->rosnode_->advertise<sensor_msgs::PointCloud>(this->pointCloudTopicName,1, 00190 boost::bind( &GazeboRosStereoCamera::PointCloudConnect, this), 00191 boost::bind( &GazeboRosStereoCamera::PointCloudDisconnect, this)); 00192 this->camera_info_pub_ = this->rosnode_->advertise<sensor_msgs::CameraInfo>(this->cameraInfoTopicName,1, 00193 boost::bind( &GazeboRosStereoCamera::InfoConnect, this), 00194 boost::bind( &GazeboRosStereoCamera::InfoDisconnect, this)); 00195 #endif 00196 } 00197 00199 // Increment count 00200 void GazeboRosStereoCamera::InfoConnect() 00201 { 00202 this->infoConnectCount++; 00203 } 00205 // Decrement count 00206 void GazeboRosStereoCamera::InfoDisconnect() 00207 { 00208 this->infoConnectCount--; 00209 } 00210 00212 // Increment count 00213 void GazeboRosStereoCamera::PointCloudConnect() 00214 { 00215 this->pointCloudConnectCount++; 00216 } 00218 // Decrement count 00219 void GazeboRosStereoCamera::PointCloudDisconnect() 00220 { 00221 this->pointCloudConnectCount--; 00222 00223 if ((this->pointCloudConnectCount == 0) && (this->imageConnectCount == 0)) 00224 this->myParent->SetActive(false); 00225 } 00226 00228 // Increment count 00229 void GazeboRosStereoCamera::ImageConnect() 00230 { 00231 this->imageConnectCount++; 00232 } 00234 // Decrement count 00235 void GazeboRosStereoCamera::ImageDisconnect() 00236 { 00237 this->imageConnectCount--; 00238 00239 if ((this->pointCloudConnectCount == 0) && (this->imageConnectCount == 0)) 00240 this->myParent->SetActive(false); 00241 } 00242 00244 // Initialize the controller 00245 void GazeboRosStereoCamera::InitChild() 00246 { 00247 00248 // sensor generation off by default 00249 this->myParent->SetActive(false); 00250 00251 // set buffer size 00252 this->width = this->myParent->GetImageWidth(); 00253 this->height = this->myParent->GetImageHeight(); 00254 this->depth = this->myParent->GetImageDepth(); 00255 if (this->myParent->GetImageFormat() == "L8") 00256 { 00257 this->type = sensor_msgs::image_encodings::MONO8; 00258 this->skip = 1; 00259 } 00260 else if (this->myParent->GetImageFormat() == "R8G8B8") 00261 { 00262 this->type = sensor_msgs::image_encodings::RGB8; 00263 this->skip = 3; 00264 } 00265 else if (this->myParent->GetImageFormat() == "B8G8R8") 00266 { 00267 this->type = sensor_msgs::image_encodings::BGR8; 00268 this->skip = 3; 00269 } 00270 else if (this->myParent->GetImageFormat() == "BAYER_RGGB8") 00271 { 00272 this->type = sensor_msgs::image_encodings::BAYER_RGGB8; 00273 this->skip = 1; 00274 } 00275 else if (this->myParent->GetImageFormat() == "BAYER_BGGR8") 00276 { 00277 this->type = sensor_msgs::image_encodings::BAYER_BGGR8; 00278 this->skip = 1; 00279 } 00280 else if (this->myParent->GetImageFormat() == "BAYER_GBRG8") 00281 { 00282 this->type = sensor_msgs::image_encodings::BAYER_GBRG8; 00283 this->skip = 1; 00284 } 00285 else if (this->myParent->GetImageFormat() == "BAYER_GRBG8") 00286 { 00287 this->type = sensor_msgs::image_encodings::BAYER_GRBG8; 00288 this->skip = 1; 00289 } 00290 else 00291 { 00292 ROS_ERROR("Unsupported Gazebo ImageFormat\n"); 00293 this->type = sensor_msgs::image_encodings::BGR8; 00294 this->skip = 3; 00295 } 00296 00298 if (this->CxPrime == 0) 00299 this->CxPrime = ((double)this->width+1.0) /2.0; 00300 if (this->Cx == 0) 00301 this->Cx = ((double)this->width+1.0) /2.0; 00302 if (this->Cy == 0) 00303 this->Cy = ((double)this->height+1.0) /2.0; 00304 if (this->focal_length == 0) 00305 this->focal_length = ((double)this->width) / (2.0 *tan(this->myParent->GetHFOV().GetAsRadian()/2.0)); 00306 00307 #ifdef USE_CBQ 00308 // start custom queue for camera 00309 this->callback_queue_thread_ = boost::thread( boost::bind( &GazeboRosStereoCamera::CameraQueueThread,this ) ); 00310 #endif 00311 00312 00313 } 00314 00316 // Update the controller 00317 void GazeboRosStereoCamera::UpdateChild() 00318 { 00319 00320 // as long as ros is connected, parent is active 00321 //ROS_ERROR("debug image count %d",this->imageConnectCount); 00322 if (!this->myParent->IsActive()) 00323 { 00324 if ((this->pointCloudConnectCount > 0) || (this->imageConnectCount > 0)) 00325 // do this first so there's chance for sensor to run 1 frame after activate 00326 this->myParent->SetActive(true); 00327 } 00328 else 00329 { 00330 this->PutCameraData(); 00331 } 00332 00334 if (this->infoConnectCount > 0) 00335 this->PublishCameraInfo(); 00336 } 00337 00339 // Finalize the controller 00340 void GazeboRosStereoCamera::FiniChild() 00341 { 00342 this->myParent->SetActive(false); 00343 this->rosnode_->shutdown(); 00344 #ifdef USE_CBQ 00345 this->camera_queue_.clear(); 00346 this->camera_queue_.disable(); 00347 this->callback_queue_thread_.join(); 00348 #endif 00349 00350 } 00351 00352 00353 00354 00355 00357 // Put laser data to the interface 00358 void GazeboRosStereoCamera::PutCameraData() 00359 { 00360 00361 const unsigned char *src; 00367 const float* depths; 00368 //boost::recursive_mutex::scoped_lock mr_lock(*Simulator::Instance()->GetMRMutex()); 00369 00370 // Get a pointer to image data 00371 { 00372 //DIAGNOSTICTIMER(timer("gazebo_ros_camera: GetImageData",6)); 00373 src = this->myParent->GetImageData(0); 00374 depths = this->myParent->GetDepthData(0); 00375 } 00376 00377 if (src) 00378 { 00379 //double tmpT0 = Simulator::Instance()->GetWallTime(); 00380 this->lock.lock(); 00381 // copy data into image 00382 this->imageMsg.header.frame_id = this->frameName; 00383 Time lastRenderTime = (dynamic_cast<OgreCamera*>(this->myParent))->GetLastRenderTime(); 00384 //Time lastRenderTime = Simulator::Instance()->GetSimTime(); 00385 //printf("name[%s] render[%f] vs. sim time[%f], diff[%f]\n",this->GetName().c_str(),lastRenderTime.Double(),Simulator::Instance()->GetSimTime().Double(),lastRenderTime.Double()-Simulator::Instance()->GetSimTime().Double()); 00386 //ROS_DEBUG("camera time %f %d %d",lastRenderTime.Double(),lastRenderTime.sec,lastRenderTime.nsec); 00387 this->imageMsg.header.stamp.sec = lastRenderTime.sec; 00388 this->imageMsg.header.stamp.nsec = lastRenderTime.nsec; 00389 00390 //double tmpT1 = Simulator::Instance()->GetWallTime(); 00391 //double tmpT2; 00392 00394 if (this->image_pub_.getNumSubscribers() > 0) 00395 { 00396 00397 // copy from src to imageMsg 00398 fillImage(this->imageMsg, 00399 this->type, 00400 this->height, 00401 this->width, 00402 this->skip*this->width, 00403 (void*)src ); 00404 // publish to ros 00405 this->image_pub_.publish(this->imageMsg); 00406 } 00407 this->lock.unlock(); 00408 } 00409 00410 if (depths) 00411 { 00412 //double tmpT0 = Simulator::Instance()->GetWallTime(); 00413 this->lock.lock(); 00414 this->pointCloudMsg.header.frame_id = this->frameName; 00415 Time lastRenderTime = (dynamic_cast<OgreCamera*>(this->myParent))->GetLastRenderTime(); 00416 this->pointCloudMsg.header.stamp.sec = lastRenderTime.sec; 00417 this->pointCloudMsg.header.stamp.nsec = lastRenderTime.nsec; 00418 00420 fillDepthImage(this->pointCloudMsg, 00421 this->height, 00422 this->width, 00423 this->skip, 00424 (void*)depths ); 00425 /* 00426 for (int i = 0 ; i < this->height; i++) 00427 for (int j = 0 ; j < this->width; i++) { 00428 ROS_ERROR("test %d %d %f",i,j,depths[j + i*this->width]); 00429 } 00430 */ 00431 00432 //tmpT2 = Simulator::Instance()->GetWallTime(); 00433 00434 // publish to ros 00435 this->point_cloud_pub_.publish(this->pointCloudMsg); 00436 00437 //double tmpT3 = Simulator::Instance()->GetWallTime(); 00438 00439 this->lock.unlock(); 00440 } 00441 } 00442 00443 00449 bool 00450 GazeboRosStereoCamera::fillDepthImage(sensor_msgs::PointCloud& point_cloud, 00451 uint32_t rows_arg, 00452 uint32_t cols_arg, 00453 uint32_t step_arg, 00454 void* data_arg) 00455 { 00456 point_cloud.points.resize(0); 00457 point_cloud.channels.resize(1); 00458 point_cloud.channels[0].values.resize(0); 00459 00460 float* toCopyFrom = (float*)data_arg; 00461 int index = 0; 00462 00463 double hfov = this->myParent->GetHFOV().GetAsRadian(); 00464 double fl = ((double)this->width) / (2.0 *tan(hfov/2.0)); 00465 //ROS_ERROR("debug hfov: %f fl: %f w: %f h: %f",hfov, fl, (double)cols_arg, (double)rows_arg); 00466 //ROS_ERROR("debug %f %f", this->myParent->GetFarClip() , this->myParent->GetNearClip()); 00467 00468 // convert depth to point cloud 00469 for (uint32_t j=0; j<rows_arg; j++) 00470 { 00471 double pAngle; 00472 if (rows_arg>1) pAngle = atan2( (double)j - 0.5*(double)(rows_arg-1), fl); 00473 else pAngle = 0.0; 00474 00475 for (uint32_t i=0; i<cols_arg; i++) 00476 { 00477 double yAngle; 00478 if (cols_arg>1) yAngle = atan2( (double)i - 0.5*(double)(cols_arg-1), fl); 00479 else yAngle = 0.0; 00480 00481 //double depth = (this->myParent->GetFarClip() - this->myParent->GetNearClip()) * ( toCopyFrom[index++] ) + 1.0 * this->myParent->GetNearClip() ; 00482 //double depth = ( this->myParent->GetFarClip() + this->myParent->GetNearClip() + toCopyFrom[index++] ) 00483 // * 0.5 ; //this->myParent->GetNearClip() ; 00484 double depth = toCopyFrom[index++]; // + 0.0*this->myParent->GetNearClip(); 00485 00486 geometry_msgs::Point32 point; 00487 00488 point.y = -depth * tan(yAngle); 00489 point.z = -depth * tan(pAngle); 00490 point.x = depth; 00491 00492 this->pointCloudMsg.points.push_back(point); 00493 } 00494 } 00495 this->pointCloudMsg.channels[0].values.push_back(1.0); 00496 return true; 00497 } 00498 00500 // Put laser data to the interface 00501 void GazeboRosStereoCamera::PublishCameraInfo() 00502 { 00503 // fill CameraInfo 00504 this->cameraInfoMsg.header.frame_id = this->frameName; 00505 Time lastRenderTime = (dynamic_cast<OgreCamera*>(this->myParent))->GetLastRenderTime(); 00506 //Time lastRenderTime = Simulator::Instance()->GetSimTime(); 00507 this->cameraInfoMsg.header.stamp.sec = lastRenderTime.sec; 00508 this->cameraInfoMsg.header.stamp.nsec = lastRenderTime.nsec; 00509 this->cameraInfoMsg.height = this->height; 00510 this->cameraInfoMsg.width = this->width; 00511 // distortion 00512 #if ROS_VERSION_MINIMUM(1, 3, 0) 00513 this->cameraInfoMsg.distortion_model = "plumb_bob"; 00514 this->cameraInfoMsg.D.resize(5); 00515 #endif 00516 this->cameraInfoMsg.D[0] = this->distortion_k1; 00517 this->cameraInfoMsg.D[1] = this->distortion_k2; 00518 this->cameraInfoMsg.D[2] = this->distortion_k3; 00519 this->cameraInfoMsg.D[3] = this->distortion_t1; 00520 this->cameraInfoMsg.D[4] = this->distortion_t2; 00521 // original camera matrix 00522 this->cameraInfoMsg.K[0] = this->focal_length; 00523 this->cameraInfoMsg.K[1] = 0.0; 00524 this->cameraInfoMsg.K[2] = this->Cx; 00525 this->cameraInfoMsg.K[3] = 0.0; 00526 this->cameraInfoMsg.K[4] = this->focal_length; 00527 this->cameraInfoMsg.K[5] = this->Cy; 00528 this->cameraInfoMsg.K[6] = 0.0; 00529 this->cameraInfoMsg.K[7] = 0.0; 00530 this->cameraInfoMsg.K[8] = 1.0; 00531 // rectification 00532 this->cameraInfoMsg.R[0] = 1.0; 00533 this->cameraInfoMsg.R[1] = 0.0; 00534 this->cameraInfoMsg.R[2] = 0.0; 00535 this->cameraInfoMsg.R[3] = 0.0; 00536 this->cameraInfoMsg.R[4] = 1.0; 00537 this->cameraInfoMsg.R[5] = 0.0; 00538 this->cameraInfoMsg.R[6] = 0.0; 00539 this->cameraInfoMsg.R[7] = 0.0; 00540 this->cameraInfoMsg.R[8] = 1.0; 00541 // camera projection matrix (same as camera matrix due to lack of distortion/rectification) (is this generated?) 00542 this->cameraInfoMsg.P[0] = this->focal_length; 00543 this->cameraInfoMsg.P[1] = 0.0; 00544 this->cameraInfoMsg.P[2] = this->Cx; 00545 this->cameraInfoMsg.P[3] = -this->focal_length * this->hack_baseline; 00546 this->cameraInfoMsg.P[4] = 0.0; 00547 this->cameraInfoMsg.P[5] = this->focal_length; 00548 this->cameraInfoMsg.P[6] = this->Cy; 00549 this->cameraInfoMsg.P[7] = 0.0; 00550 this->cameraInfoMsg.P[8] = 0.0; 00551 this->cameraInfoMsg.P[9] = 0.0; 00552 this->cameraInfoMsg.P[10] = 1.0; 00553 this->cameraInfoMsg.P[11] = 0.0; 00554 this->camera_info_pub_.publish(this->cameraInfoMsg); 00555 00556 00557 } 00558 00559 00560 #ifdef USE_CBQ 00561 00562 // Put laser data to the interface 00563 void GazeboRosStereoCamera::CameraQueueThread() 00564 { 00565 static const double timeout = 0.01; 00566 00567 while (this->rosnode_->ok()) 00568 { 00569 this->camera_queue_.callAvailable(ros::WallDuration(timeout)); 00570 } 00571 } 00572 #endif 00573 00574 00575 }