$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: GazeboRosOpenniKinect plugin for simulating cameras in Gazebo 00024 Author: John Hsu and Melonee Wise 00025 Date: 24 Sept 2008 00026 SVN info: $Id$ 00027 @htmlinclude manifest.html 00028 @b GazeboRosOpenniKinect 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_openni_kinect.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 #include <gazebo/Pose3d.hh> 00049 00050 00051 #include "sensor_msgs/Image.h" 00052 #include "sensor_msgs/fill_image.h" 00053 #include "image_transport/image_transport.h" 00054 00055 #include <geometry_msgs/Point32.h> 00056 #include <sensor_msgs/ChannelFloat32.h> 00057 00058 namespace gazebo 00059 { 00060 00061 GZ_REGISTER_DYNAMIC_CONTROLLER("gazebo_ros_openni_kinect", GazeboRosOpenniKinect); 00062 00064 // Constructor 00065 GazeboRosOpenniKinect::GazeboRosOpenniKinect(Entity *parent) 00066 : Controller(parent) 00067 { 00068 this->myParent = dynamic_cast<MonoCameraSensor*>(this->parent); 00069 00070 if (!this->myParent) 00071 gzthrow("GazeboRosOpenniKinect controller requires a Camera Sensor as its parent"); 00072 00073 Param::Begin(&this->parameters); 00074 this->robotNamespaceP = new ParamT<std::string>("robotNamespace","/",0); 00075 this->imageTopicNameP = new ParamT<std::string>("imageTopicName","image_raw", 0); 00076 this->cameraInfoTopicNameP = new ParamT<std::string>("cameraInfoTopicName","camera_info", 0); 00077 this->depthImageCameraInfoTopicNameP = new ParamT<std::string>("depthImageCameraInfoTopicName","depth/camera_info", 0); 00078 this->pointCloudTopicNameP = new ParamT<std::string>("pointCloudTopicName","points", 0); 00079 this->depthImageTopicNameP = new ParamT<std::string>("depthImageTopicName","depth/image_raw", 0); 00080 this->cameraNameP = new ParamT<std::string>("cameraName","", 0); 00081 this->frameNameP = new ParamT<std::string>("frameName","generic_camera_link", 0); 00082 // camera parameters 00083 this->CxPrimeP = new ParamT<double>("CxPrime",0, 0); // default to 0 for compute on the fly 00084 this->CxP = new ParamT<double>("Cx" ,0, 0); // default to 0 for compute on the fly 00085 this->CyP = new ParamT<double>("Cy" ,0, 0); // default to 0 for compute on the fly 00086 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 00087 this->hack_baselineP = new ParamT<double>("hackBaseline" ,0, 0); // hack for right stereo camera 00088 this->pointCloudCutoffP = new ParamT<double>("pointCloudCutoff" ,0.4, 0); 00089 this->distortion_k1P = new ParamT<double>("distortion_k1" ,0, 0); 00090 this->distortion_k2P = new ParamT<double>("distortion_k2" ,0, 0); 00091 this->distortion_k3P = new ParamT<double>("distortion_k3" ,0, 0); 00092 this->distortion_t1P = new ParamT<double>("distortion_t1" ,0, 0); 00093 this->distortion_t2P = new ParamT<double>("distortion_t2" ,0, 0); 00094 Param::End(); 00095 00096 this->imageConnectCount = 0; 00097 this->infoConnectCount = 0; 00098 this->depthInfoConnectCount = 0; 00099 this->pointCloudConnectCount = 0; 00100 this->depthImageConnectCount = 0; 00101 00102 last_image_pub_time_ = Time(0); 00103 last_camera_info_pub_time_ = Time(0); 00104 last_point_cloud_pub_time_ = Time(0); 00105 00106 // set sensor update rate to controller update rate? 00107 //(dynamic_cast<OgreCamera*>(this->myParent))->SetUpdateRate(this->updateRateP->GetValue()); 00108 } 00109 00110 void GazeboRosOpenniKinect::configCallback(gazebo_plugins::GazeboRosOpenniKinectConfig &config, uint32_t level) 00111 { 00112 ROS_INFO("Reconfigure request for the gazebo ros camera: %s. New rate: %.2f", this->cameraName.c_str(), config.imager_rate); 00113 00114 (dynamic_cast<OgreCamera*>(this->myParent))->SetUpdateRate(config.imager_rate); 00115 } 00116 00118 // Destructor 00119 GazeboRosOpenniKinect::~GazeboRosOpenniKinect() 00120 { 00121 delete this->robotNamespaceP; 00122 delete this->rosnode_; 00123 delete this->managernode_; 00124 delete this->manager_; 00125 delete this->imageTopicNameP; 00126 delete this->cameraInfoTopicNameP; 00127 delete this->depthImageCameraInfoTopicNameP; 00128 delete this->pointCloudTopicNameP; 00129 delete this->depthImageTopicNameP; 00130 delete this->frameNameP; 00131 delete this->CxPrimeP; 00132 delete this->CxP; 00133 delete this->CyP; 00134 delete this->focal_lengthP; 00135 delete this->hack_baselineP; 00136 delete this->pointCloudCutoffP; 00137 delete this->distortion_k1P; 00138 delete this->distortion_k2P; 00139 delete this->distortion_k3P; 00140 delete this->distortion_t1P; 00141 delete this->distortion_t2P; 00142 } 00143 00145 // Load the controller 00146 void GazeboRosOpenniKinect::LoadChild(XMLConfigNode *node) 00147 { 00148 this->robotNamespaceP->Load(node); 00149 this->robotNamespace = this->robotNamespaceP->GetValue(); 00150 if (!ros::isInitialized()) 00151 { 00152 int argc = 0; 00153 char** argv = NULL; 00154 ros::init(argc,argv,"gazebo",ros::init_options::NoSigintHandler|ros::init_options::AnonymousName); 00155 } 00156 00157 this->imageTopicNameP->Load(node); 00158 this->cameraInfoTopicNameP->Load(node); 00159 this->depthImageCameraInfoTopicNameP->Load(node); 00160 this->pointCloudTopicNameP->Load(node); 00161 this->depthImageTopicNameP->Load(node); 00162 this->cameraNameP->Load(node); 00163 this->frameNameP->Load(node); 00164 this->CxPrimeP->Load(node); 00165 this->CxP->Load(node); 00166 this->CyP->Load(node); 00167 this->focal_lengthP->Load(node); 00168 this->hack_baselineP->Load(node); 00169 this->pointCloudCutoffP->Load(node); 00170 this->distortion_k1P->Load(node); 00171 this->distortion_k2P->Load(node); 00172 this->distortion_k3P->Load(node); 00173 this->distortion_t1P->Load(node); 00174 this->distortion_t2P->Load(node); 00175 this->imageTopicName = this->imageTopicNameP->GetValue(); 00176 this->cameraInfoTopicName = this->cameraInfoTopicNameP->GetValue(); 00177 this->depthImageCameraInfoTopicName = this->depthImageCameraInfoTopicNameP->GetValue(); 00178 this->pointCloudTopicName = this->pointCloudTopicNameP->GetValue(); 00179 this->depthImageTopicName = this->depthImageTopicNameP->GetValue(); 00180 this->frameName = this->frameNameP->GetValue(); 00181 this->CxPrime = this->CxPrimeP->GetValue(); 00182 this->Cx = this->CxP->GetValue(); 00183 this->Cy = this->CyP->GetValue(); 00184 this->focal_length = this->focal_lengthP->GetValue(); 00185 this->hack_baseline = this->hack_baselineP->GetValue(); 00186 this->pointCloudCutoff = this->pointCloudCutoffP->GetValue(); 00187 this->distortion_k1 = this->distortion_k1P->GetValue(); 00188 this->distortion_k2 = this->distortion_k2P->GetValue(); 00189 this->distortion_k3 = this->distortion_k3P->GetValue(); 00190 this->distortion_t1 = this->distortion_t1P->GetValue(); 00191 this->distortion_t2 = this->distortion_t2P->GetValue(); 00192 if ((this->distortion_k1 != 0.0) || (this->distortion_k2 != 0.0) || 00193 (this->distortion_k3 != 0.0) || (this->distortion_t1 != 0.0) || 00194 (this->distortion_t2 != 0.0)) 00195 ROS_WARN("gazebo_ros_camera simulation does not support non-zero distortion parameters right now, your simulation maybe wrong."); 00196 00197 this->cameraName = this->cameraNameP->GetValue(); 00198 this->rosnode_ = new ros::NodeHandle(this->robotNamespace+"/"+this->cameraName); 00199 if (!this->cameraName.empty()) 00200 this->managernode_ = new ros::NodeHandle(this->robotNamespace+"/"+this->cameraName+"_manager"); 00201 else 00202 this->managernode_ = new ros::NodeHandle(this->robotNamespace+"/"+this->GetName()+"_manager"); 00203 this->manager_ = new nodelet::Loader(*this->managernode_); 00204 this->itnode_ = new image_transport::ImageTransport(*this->rosnode_); 00205 00206 if (!this->cameraName.empty()) { 00207 dyn_srv_ = new dynamic_reconfigure::Server<gazebo_plugins::GazeboRosOpenniKinectConfig>(*this->rosnode_); 00208 dynamic_reconfigure::Server<gazebo_plugins::GazeboRosOpenniKinectConfig>::CallbackType f = boost::bind(&GazeboRosOpenniKinect::configCallback, this, _1, _2); 00209 dyn_srv_->setCallback(f); 00210 } 00211 00212 00213 this->image_pub_ = this->itnode_->advertise( 00214 this->imageTopicName,1, 00215 boost::bind( &GazeboRosOpenniKinect::ImageConnect,this), 00216 boost::bind( &GazeboRosOpenniKinect::ImageDisconnect,this), ros::VoidPtr(), &this->camera_queue_); 00217 00218 ros::AdvertiseOptions camera_info_ao = ros::AdvertiseOptions::create<sensor_msgs::CameraInfo>( 00219 this->cameraInfoTopicName,1, 00220 boost::bind( &GazeboRosOpenniKinect::InfoConnect,this), 00221 boost::bind( &GazeboRosOpenniKinect::InfoDisconnect,this), ros::VoidPtr(), &this->camera_queue_); 00222 this->camera_info_pub_ = this->rosnode_->advertise(camera_info_ao); 00223 00224 ros::SubscribeOptions zoom_so = ros::SubscribeOptions::create<std_msgs::Float64>( 00225 "set_hfov",1, 00226 boost::bind( &GazeboRosOpenniKinect::SetHFOV,this,_1), 00227 ros::VoidPtr(), &this->camera_queue_); 00228 this->cameraHFOVSubscriber_ = this->rosnode_->subscribe(zoom_so); 00229 00230 ros::SubscribeOptions rate_so = ros::SubscribeOptions::create<std_msgs::Float64>( 00231 "set_update_rate",1, 00232 boost::bind( &GazeboRosOpenniKinect::SetUpdateRate,this,_1), 00233 ros::VoidPtr(), &this->camera_queue_); 00234 this->cameraUpdateRateSubscriber_ = this->rosnode_->subscribe(rate_so); 00235 00236 ros::AdvertiseOptions point_cloud_ao = ros::AdvertiseOptions::create<pcl::PointCloud<pcl::PointXYZ> >( 00237 this->pointCloudTopicName,1, 00238 boost::bind( &GazeboRosOpenniKinect::PointCloudConnect,this), 00239 boost::bind( &GazeboRosOpenniKinect::PointCloudDisconnect,this), ros::VoidPtr(), &this->camera_queue_); 00240 this->point_cloud_pub_ = this->rosnode_->advertise(point_cloud_ao); 00241 00242 ros::AdvertiseOptions depth_image_ao = 00243 ros::AdvertiseOptions::create< sensor_msgs::Image >( 00244 this->depthImageTopicName,1, 00245 boost::bind( &GazeboRosOpenniKinect::DepthImageConnect,this), 00246 boost::bind( &GazeboRosOpenniKinect::DepthImageDisconnect,this), 00247 ros::VoidPtr(), &this->camera_queue_); 00248 this->depth_image_pub_ = this->rosnode_->advertise(depth_image_ao); 00249 00250 ros::AdvertiseOptions depth_image_camera_info_ao = 00251 ros::AdvertiseOptions::create<sensor_msgs::CameraInfo>( 00252 this->depthImageCameraInfoTopicName,1, 00253 boost::bind( &GazeboRosOpenniKinect::DepthInfoConnect,this), 00254 boost::bind( &GazeboRosOpenniKinect::DepthInfoDisconnect,this), 00255 ros::VoidPtr(), &this->camera_queue_); 00256 this->depth_image_camera_info_pub_ = this->rosnode_->advertise(depth_image_camera_info_ao); 00257 00258 } 00259 00261 // Increment count 00262 void GazeboRosOpenniKinect::InfoConnect() 00263 { 00264 this->infoConnectCount++; 00265 } 00267 // Decrement count 00268 void GazeboRosOpenniKinect::InfoDisconnect() 00269 { 00270 this->infoConnectCount--; 00271 } 00272 00274 // Increment count 00275 void GazeboRosOpenniKinect::DepthInfoConnect() 00276 { 00277 this->depthInfoConnectCount++; 00278 } 00280 // Decrement count 00281 void GazeboRosOpenniKinect::DepthInfoDisconnect() 00282 { 00283 this->depthInfoConnectCount--; 00284 } 00285 00287 // Set Horizontal Field of View 00288 void GazeboRosOpenniKinect::SetHFOV(const std_msgs::Float64::ConstPtr& hfov) 00289 { 00290 (dynamic_cast<OgreCamera*>(this->myParent))->SetFOV(hfov->data); 00291 } 00292 00294 // Set Update Rate 00295 void GazeboRosOpenniKinect::SetUpdateRate(const std_msgs::Float64::ConstPtr& update_rate) 00296 { 00297 (dynamic_cast<OgreCamera*>(this->myParent))->SetUpdateRate(update_rate->data); 00298 } 00299 00301 // Increment count 00302 void GazeboRosOpenniKinect::ImageConnect() 00303 { 00304 this->imageConnectCount++; 00305 this->myParent->SetActive(true); 00306 } 00308 // Decrement count 00309 void GazeboRosOpenniKinect::ImageDisconnect() 00310 { 00311 this->imageConnectCount--; 00312 00313 if (this->pointCloudConnectCount == 0) 00314 { 00315 this->myParent->SimulateDepthData(false); 00316 if (this->imageConnectCount == 0) 00317 this->myParent->SetActive(false); 00318 } 00319 } 00320 00322 // Increment count 00323 void GazeboRosOpenniKinect::PointCloudConnect() 00324 { 00325 this->pointCloudConnectCount++; 00326 this->myParent->SetActive(true); 00327 this->myParent->SimulateDepthData(true); 00328 } 00330 // Decrement count 00331 void GazeboRosOpenniKinect::PointCloudDisconnect() 00332 { 00333 this->pointCloudConnectCount--; 00334 00335 if (this->pointCloudConnectCount == 0) 00336 { 00337 this->myParent->SimulateDepthData(false); 00338 if (this->imageConnectCount == 0) 00339 this->myParent->SetActive(false); 00340 } 00341 } 00342 00344 // Increment count 00345 void GazeboRosOpenniKinect::DepthImageConnect() 00346 { 00347 this->depthImageConnectCount++; 00348 this->myParent->SetActive(true); 00349 this->myParent->SimulateDepthData(true); 00350 } 00352 // Decrement count 00353 void GazeboRosOpenniKinect::DepthImageDisconnect() 00354 { 00355 this->depthImageConnectCount--; 00356 00357 if (this->depthImageConnectCount == 0) 00358 { 00359 this->myParent->SimulateDepthData(false); 00360 if (this->imageConnectCount == 0) 00361 this->myParent->SetActive(false); 00362 } 00363 } 00364 00366 // Initialize the controller 00367 void GazeboRosOpenniKinect::InitChild() 00368 { 00369 // sensor generation off by default 00370 this->myParent->SetActive(false); 00371 this->myParent->SimulateDepthData(false); 00372 00373 // set buffer size 00374 this->width = this->myParent->GetImageWidth(); 00375 this->height = this->myParent->GetImageHeight(); 00376 this->depth = this->myParent->GetImageDepth(); 00377 if (this->myParent->GetImageFormat() == "L8") 00378 { 00379 this->type = sensor_msgs::image_encodings::MONO8; 00380 this->skip = 1; 00381 } 00382 else if (this->myParent->GetImageFormat() == "R8G8B8") 00383 { 00384 this->type = sensor_msgs::image_encodings::RGB8; 00385 this->skip = 3; 00386 } 00387 else if (this->myParent->GetImageFormat() == "B8G8R8") 00388 { 00389 this->type = sensor_msgs::image_encodings::BGR8; 00390 this->skip = 3; 00391 } 00392 else if (this->myParent->GetImageFormat() == "BAYER_RGGB8") 00393 { 00394 ROS_WARN("bayer simulation maybe computationally expensive."); 00395 this->type = sensor_msgs::image_encodings::BAYER_RGGB8; 00396 this->skip = 1; 00397 } 00398 else if (this->myParent->GetImageFormat() == "BAYER_BGGR8") 00399 { 00400 ROS_WARN("bayer simulation maybe computationally expensive."); 00401 this->type = sensor_msgs::image_encodings::BAYER_BGGR8; 00402 this->skip = 1; 00403 } 00404 else if (this->myParent->GetImageFormat() == "BAYER_GBRG8") 00405 { 00406 ROS_WARN("bayer simulation maybe computationally expensive."); 00407 this->type = sensor_msgs::image_encodings::BAYER_GBRG8; 00408 this->skip = 1; 00409 } 00410 else if (this->myParent->GetImageFormat() == "BAYER_GRBG8") 00411 { 00412 ROS_WARN("bayer simulation maybe computationally expensive."); 00413 this->type = sensor_msgs::image_encodings::BAYER_GRBG8; 00414 this->skip = 1; 00415 } 00416 else 00417 { 00418 ROS_ERROR("Unsupported Gazebo ImageFormat\n"); 00419 this->type = sensor_msgs::image_encodings::BGR8; 00420 this->skip = 3; 00421 } 00422 00424 if (this->CxPrime == 0) 00425 this->CxPrime = ((double)this->width+1.0) /2.0; 00426 if (this->Cx == 0) 00427 this->Cx = ((double)this->width+1.0) /2.0; 00428 if (this->Cy == 0) 00429 this->Cy = ((double)this->height+1.0) /2.0; 00430 00431 00432 double computed_focal_length = ((double)this->width) / (2.0 *tan(this->myParent->GetHFOV().GetAsRadian()/2.0)); 00433 if (this->focal_length == 0) 00434 this->focal_length = computed_focal_length; 00435 else 00436 if (fabs(this->focal_length - computed_focal_length) > 1e-8) // check against float precision 00437 ROS_WARN("The <focal_length>[%f] you have provided for camera [%s] is inconsistent with specified image_width [%d] and HFOV [%f]. Please double check to see that focal_length = width / (2.0 * tan( HFOV/2.0 )), the explected focal_lengtth value is [%f], please update your camera model description accordingly.", 00438 this->focal_length,this->myParent->GetName().c_str(),this->width,this->myParent->GetHFOV().GetAsRadian(), 00439 computed_focal_length); 00440 00441 00442 // start custom queue for camera 00443 this->callback_queue_thread_ = boost::thread( boost::bind( &GazeboRosOpenniKinect::CameraQueueThread,this ) ); 00444 00445 00446 } 00447 00449 // Update the controller 00450 void GazeboRosOpenniKinect::UpdateChild() 00451 { 00452 00453 // Only publish to ROS when the camera updates 00454 Time sensor_update_time = (dynamic_cast<OgreCamera*>(this->myParent))->GetLastRenderTime(); 00455 00456 // as long as ros is connected, parent is active 00457 if (this->myParent->IsActive()) 00458 { 00459 if (sensor_update_time > last_image_pub_time_) 00460 this->PutCameraData(); 00461 } 00462 00464 if (this->infoConnectCount > 0) //publish regardless camera image subscription 00465 this->PublishCameraInfo(this->camera_info_pub_); 00466 00467 if (this->depthInfoConnectCount > 0) //publish regardless camera image subscription 00468 this->PublishCameraInfo(this->depth_image_camera_info_pub_); 00469 } 00470 00472 // Finalize the controller 00473 void GazeboRosOpenniKinect::FiniChild() 00474 { 00475 this->myParent->SetActive(false); 00476 this->manager_->clear(); 00477 this->managernode_->shutdown(); 00478 this->rosnode_->shutdown(); 00479 this->camera_queue_.clear(); 00480 this->camera_queue_.disable(); 00481 this->callback_queue_thread_.join(); 00482 00483 } 00484 00486 // Put laser data to the interface 00487 void GazeboRosOpenniKinect::PutCameraData() 00488 { 00489 //ROS_ERROR("debug %d %s",this->imageConnectCount,this->GetName().c_str()); 00490 const unsigned char *src; 00491 const float* depths; 00492 00493 // Get a pointer to image data 00494 if (this->imageConnectCount > 0) 00495 { 00496 // prevent parent from updating the image and the depth data while we are retrieving it 00497 boost::recursive_mutex::scoped_lock mr_lock(*Simulator::Instance()->GetMRMutex()); 00498 00499 //DIAGNOSTICTIMER(timer("gazebo_ros_camera: GetImageData",6)); 00500 src = this->myParent->GetImageData(0); 00501 if (src) 00502 { 00503 //double tmpT0 = Simulator::Instance()->GetWallTime(); 00504 00505 this->lock.lock(); 00506 // copy data into image 00507 this->imageMsg.header.frame_id = this->frameName; 00508 Time lastRenderTime = (dynamic_cast<OgreCamera*>(this->myParent))->GetLastRenderTime(); 00509 //Time lastRenderTime = Simulator::Instance()->GetSimTime(); 00510 //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()); 00511 //ROS_DEBUG("camera time %f %d %d",lastRenderTime.Double(),lastRenderTime.sec,lastRenderTime.nsec); 00512 this->imageMsg.header.stamp.sec = lastRenderTime.sec; 00513 this->imageMsg.header.stamp.nsec = lastRenderTime.nsec; 00514 00515 //double tmpT1 = Simulator::Instance()->GetWallTime(); 00516 //double tmpT2; 00517 00519 if (this->image_pub_.getNumSubscribers() > 0) 00520 { 00521 00522 // copy from src to imageMsg 00523 fillImage(this->imageMsg, 00524 this->type, 00525 this->height, 00526 this->width, 00527 this->skip*this->width, 00528 (void*)src ); 00529 00530 //tmpT2 = Simulator::Instance()->GetWallTime(); 00531 00532 // publish to ros 00533 last_image_pub_time_ = Simulator::Instance()->GetSimTime(); 00534 this->image_pub_.publish(this->imageMsg); 00535 } 00536 00537 //double tmpT3 = Simulator::Instance()->GetWallTime(); 00538 00539 this->lock.unlock(); 00540 } 00541 } 00542 00543 if (this->pointCloudConnectCount > 0 || this->depthImageConnectCount > 0) 00544 { 00545 // prevent parent from updating the image and the depth data while we are retrieving it 00546 boost::recursive_mutex::scoped_lock mr_lock(*Simulator::Instance()->GetMRMutex()); 00547 00548 depths = this->myParent->GetDepthData(0); 00549 if (depths) 00550 { 00551 Time lastRenderTime = (dynamic_cast<OgreCamera*>(this->myParent))->GetLastRenderTime(); 00552 //double tmpT3 = Simulator::Instance()->GetWallTime(); 00553 if (this->pointCloudConnectCount > 0) 00554 { 00555 //double tmpT0 = Simulator::Instance()->GetWallTime(); 00556 this->lock.lock(); 00557 this->pointCloudMsg.header.frame_id = this->frameName; 00558 this->pointCloudMsg.header.stamp.sec = lastRenderTime.sec; 00559 this->pointCloudMsg.header.stamp.nsec = lastRenderTime.nsec; 00560 this->pointCloudMsg.width = this->width; 00561 this->pointCloudMsg.height = this->height; 00562 00564 FillPointCloudHelper(this->pointCloudMsg, 00565 this->height, 00566 this->width, 00567 this->skip, 00568 (void*)depths ); 00569 00570 // publish to ros 00571 this->point_cloud_pub_.publish(this->pointCloudMsg); 00572 this->lock.unlock(); 00573 } 00574 00575 if (this->depthImageConnectCount > 0) 00576 { 00577 this->lock.lock(); 00578 // copy data into image 00579 this->depth_image_msg_.header.frame_id = this->frameName; 00580 this->depth_image_msg_.header.stamp.sec = lastRenderTime.sec; 00581 this->depth_image_msg_.header.stamp.nsec = lastRenderTime.nsec; 00582 00584 FillDepthImageHelper(this->depth_image_msg_, 00585 this->height, 00586 this->width, 00587 this->skip, 00588 (void*)depths ); 00589 00590 this->depth_image_pub_.publish(this->depth_image_msg_); 00591 this->lock.unlock(); 00592 } 00593 } 00594 } 00595 } 00596 00597 00599 // fill a depth image according to http://ros.org/wiki/openni_camera 00600 // i.e. mm in uint16_t 00601 bool GazeboRosOpenniKinect::FillDepthImageHelper( 00602 sensor_msgs::Image& image_msg, 00603 uint32_t height, uint32_t width, 00604 uint32_t step, void* data_arg) 00605 { 00606 image_msg.encoding = sensor_msgs::image_encodings::MONO8; 00607 image_msg.height = height; 00608 image_msg.width = width; 00609 image_msg.step = 1; 00610 image_msg.data.resize(height*width*sizeof(uint16_t)); 00611 image_msg.is_bigendian = 0; 00612 00613 uint16_t* dest = (uint16_t*)(&(image_msg.data[0])); 00614 float* toCopyFrom = (float*)data_arg; 00615 int index = 0; 00616 00617 // convert depth to point cloud 00618 for (uint32_t j=0; j<height; j++) 00619 { 00620 for (uint32_t i=0; i<width; i++) 00621 { 00622 double depth = 0; 00623 for (uint32_t s=0; s<step; s++) // if depth > 1 (e.g. rgb), average 00624 depth += toCopyFrom[index++]; 00625 depth = depth / (double)step; 00626 00627 if(depth > this->pointCloudCutoff) 00628 { 00629 dest[i+j*width] = (uint16_t)1000.0*depth; // uint16 in mm 00630 } 00631 else //point in the unseeable range 00632 { 00633 dest[i+j*width] = 0; 00634 } 00635 } 00636 } 00637 return true; 00638 } 00639 00641 // Convert image format from gazebo sensor provided foramt to desired ros format 00642 void GazeboRosOpenniKinect::convertImageFormat(unsigned char *dst, const unsigned char *src) 00643 { 00644 // do last minute conversion if Bayer pattern is requested but not provided, go from R8G8B8 00645 // deprecated in gazebo2 branch, keep for backwards compatibility 00646 if (this->myParent->GetImageFormat() == "BAYER_RGGB8" && this->depth == 3) 00647 { 00648 for (int i=0;i<this->width;i++) 00649 { 00650 for (int j=0;j<this->height;j++) 00651 { 00652 // 00653 // RG 00654 // GB 00655 // 00656 // determine position 00657 if (j%2) // even column 00658 if (i%2) // even row, red 00659 dst[i+j*this->width] = src[i*3+j*this->width*3+0]; 00660 else // odd row, green 00661 dst[i+j*this->width] = src[i*3+j*this->width*3+1]; 00662 else // odd column 00663 if (i%2) // even row, green 00664 dst[i+j*this->width] = src[i*3+j*this->width*3+1]; 00665 else // odd row, blue 00666 dst[i+j*this->width] = src[i*3+j*this->width*3+2]; 00667 } 00668 } 00669 src=dst; 00670 } 00671 else if (this->myParent->GetImageFormat() == "BAYER_BGGR8" && this->depth == 3) 00672 { 00673 for (int i=0;i<this->width;i++) 00674 { 00675 for (int j=0;j<this->height;j++) 00676 { 00677 // 00678 // BG 00679 // GR 00680 // 00681 // determine position 00682 if (j%2) // even column 00683 if (i%2) // even row, blue 00684 dst[i+j*this->width] = src[i*3+j*this->width*3+2]; 00685 else // odd row, green 00686 dst[i+j*this->width] = src[i*3+j*this->width*3+1]; 00687 else // odd column 00688 if (i%2) // even row, green 00689 dst[i+j*this->width] = src[i*3+j*this->width*3+1]; 00690 else // odd row, red 00691 dst[i+j*this->width] = src[i*3+j*this->width*3+0]; 00692 } 00693 } 00694 src=dst; 00695 } 00696 else if (this->myParent->GetImageFormat() == "BAYER_GBRG8" && this->depth == 3) 00697 { 00698 for (int i=0;i<this->width;i++) 00699 { 00700 for (int j=0;j<this->height;j++) 00701 { 00702 // 00703 // GB 00704 // RG 00705 // 00706 // determine position 00707 if (j%2) // even column 00708 if (i%2) // even row, green 00709 dst[i+j*this->width] = src[i*3+j*this->width*3+1]; 00710 else // odd row, blue 00711 dst[i+j*this->width] = src[i*3+j*this->width*3+2]; 00712 else // odd column 00713 if (i%2) // even row, red 00714 dst[i+j*this->width] = src[i*3+j*this->width*3+0]; 00715 else // odd row, green 00716 dst[i+j*this->width] = src[i*3+j*this->width*3+1]; 00717 } 00718 } 00719 src=dst; 00720 } 00721 else if (this->myParent->GetImageFormat() == "BAYER_GRBG8" && this->depth == 3) 00722 { 00723 for (int i=0;i<this->width;i++) 00724 { 00725 for (int j=0;j<this->height;j++) 00726 { 00727 // 00728 // GR 00729 // BG 00730 // 00731 // determine position 00732 if (j%2) // even column 00733 if (i%2) // even row, green 00734 dst[i+j*this->width] = src[i*3+j*this->width*3+1]; 00735 else // odd row, red 00736 dst[i+j*this->width] = src[i*3+j*this->width*3+0]; 00737 else // odd column 00738 if (i%2) // even row, blue 00739 dst[i+j*this->width] = src[i*3+j*this->width*3+2]; 00740 else // odd row, green 00741 dst[i+j*this->width] = src[i*3+j*this->width*3+1]; 00742 } 00743 } 00744 src=dst; 00745 } 00746 } 00747 00749 // Fill depth information 00750 bool GazeboRosOpenniKinect::FillPointCloudHelper(pcl::PointCloud<pcl::PointXYZ>& point_cloud, 00751 uint32_t rows_arg, 00752 uint32_t cols_arg, 00753 uint32_t step_arg, 00754 void* data_arg) 00755 { 00756 point_cloud.points.resize(0); 00757 point_cloud.is_dense = true; 00758 //point_cloud.channels.resize(1); 00759 //point_cloud.channels[0].values.resize(0); 00760 00761 float* toCopyFrom = (float*)data_arg; 00762 int index = 0; 00763 00764 double hfov = this->myParent->GetHFOV().GetAsRadian(); 00765 double fl = ((double)this->width) / (2.0 *tan(hfov/2.0)); 00766 //ROS_ERROR("debug hfov: %f fl: %f w: %f h: %f",hfov, fl, (double)cols_arg, (double)rows_arg); 00767 //ROS_ERROR("debug %f %f", this->myParent->GetFarClip() , this->myParent->GetNearClip()); 00768 00769 // convert depth to point cloud 00770 for (uint32_t j=0; j<rows_arg; j++) 00771 { 00772 double pAngle; 00773 if (rows_arg>1) pAngle = atan2( (double)j - 0.5*(double)(rows_arg-1), fl); 00774 else pAngle = 0.0; 00775 00776 for (uint32_t i=0; i<cols_arg; i++) 00777 { 00778 double yAngle; 00779 if (cols_arg>1) yAngle = atan2( (double)i - 0.5*(double)(cols_arg-1), fl); 00780 else yAngle = 0.0; 00781 00782 //double depth = (this->myParent->GetFarClip() - this->myParent->GetNearClip()) * ( toCopyFrom[index++] ) + 1.0 * this->myParent->GetNearClip() ; 00783 //double depth = ( this->myParent->GetFarClip() + this->myParent->GetNearClip() + toCopyFrom[index++] ) 00784 // * 0.5 ; //this->myParent->GetNearClip() ; 00785 double depth = toCopyFrom[index++]; // + 0.0*this->myParent->GetNearClip(); 00786 00787 // in optical frame 00788 // hardcoded rotation rpy(-M_PI/2, 0, -M_PI/2) is built-in 00789 // to urdf, where the *_optical_frame should have above relative 00790 // rotation from the physical camera *_frame 00791 pcl::PointXYZ point; 00792 point.x = depth * tan(yAngle); 00793 point.y = depth * tan(pAngle); 00794 if(depth > this->pointCloudCutoff) 00795 { 00796 point.z = depth; 00797 } 00798 else //point in the unseeable range 00799 { 00800 point.x = point.y = point.z = std::numeric_limits<float>::quiet_NaN (); 00801 point_cloud.is_dense = false; 00802 } 00803 point_cloud.points.push_back(point); 00804 } 00805 } 00806 //this->pointCloudMsg.channels[0].values.push_back(1.0); 00807 return true; 00808 } 00810 // Put laser data to the interface 00811 void GazeboRosOpenniKinect::PublishCameraInfo(ros::Publisher camera_info_publisher) 00812 { 00813 sensor_msgs::CameraInfo camera_info_msg; 00814 // fill CameraInfo 00815 camera_info_msg.header.frame_id = this->frameName; 00816 Time lastRenderTime = (dynamic_cast<OgreCamera*>(this->myParent))->GetLastRenderTime(); 00817 //Time lastRenderTime = Simulator::Instance()->GetSimTime(); 00818 camera_info_msg.header.stamp.sec = lastRenderTime.sec; 00819 camera_info_msg.header.stamp.nsec = lastRenderTime.nsec; 00820 camera_info_msg.height = this->height; 00821 camera_info_msg.width = this->width; 00822 // distortion 00823 #if ROS_VERSION_MINIMUM(1, 3, 0) 00824 camera_info_msg.distortion_model = "plumb_bob"; 00825 camera_info_msg.D.resize(5); 00826 #endif 00827 camera_info_msg.D[0] = this->distortion_k1; 00828 camera_info_msg.D[1] = this->distortion_k2; 00829 camera_info_msg.D[2] = this->distortion_k3; 00830 camera_info_msg.D[3] = this->distortion_t1; 00831 camera_info_msg.D[4] = this->distortion_t2; 00832 // original camera matrix 00833 camera_info_msg.K[0] = this->focal_length; 00834 camera_info_msg.K[1] = 0.0; 00835 camera_info_msg.K[2] = this->Cx; 00836 camera_info_msg.K[3] = 0.0; 00837 camera_info_msg.K[4] = this->focal_length; 00838 camera_info_msg.K[5] = this->Cy; 00839 camera_info_msg.K[6] = 0.0; 00840 camera_info_msg.K[7] = 0.0; 00841 camera_info_msg.K[8] = 1.0; 00842 // rectification 00843 camera_info_msg.R[0] = 1.0; 00844 camera_info_msg.R[1] = 0.0; 00845 camera_info_msg.R[2] = 0.0; 00846 camera_info_msg.R[3] = 0.0; 00847 camera_info_msg.R[4] = 1.0; 00848 camera_info_msg.R[5] = 0.0; 00849 camera_info_msg.R[6] = 0.0; 00850 camera_info_msg.R[7] = 0.0; 00851 camera_info_msg.R[8] = 1.0; 00852 // camera projection matrix (same as camera matrix due to lack of distortion/rectification) (is this generated?) 00853 camera_info_msg.P[0] = this->focal_length; 00854 camera_info_msg.P[1] = 0.0; 00855 camera_info_msg.P[2] = this->Cx; 00856 camera_info_msg.P[3] = -this->focal_length * this->hack_baseline; 00857 camera_info_msg.P[4] = 0.0; 00858 camera_info_msg.P[5] = this->focal_length; 00859 camera_info_msg.P[6] = this->Cy; 00860 camera_info_msg.P[7] = 0.0; 00861 camera_info_msg.P[8] = 0.0; 00862 camera_info_msg.P[9] = 0.0; 00863 camera_info_msg.P[10] = 1.0; 00864 camera_info_msg.P[11] = 0.0; 00865 00866 last_camera_info_pub_time_ = Simulator::Instance()->GetSimTime(); 00867 camera_info_publisher.publish(camera_info_msg); 00868 } 00869 00870 00872 // Put laser data to the interface 00873 void GazeboRosOpenniKinect::CameraQueueThread() 00874 { 00875 static const double timeout = 0.01; 00876 00877 while (this->rosnode_->ok()) 00878 { 00879 this->camera_queue_.callAvailable(ros::WallDuration(timeout)); 00880 } 00881 } 00882 00883 }