$search
00001 /**************************************************************** 00002 * 00003 * Copyright (c) 2010 00004 * 00005 * Fraunhofer Institute for Manufacturing Engineering 00006 * and Automation (IPA) 00007 * 00008 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 00009 * 00010 * Project name: care-o-bot 00011 * ROS stack name: cob_driver 00012 * ROS package name: cob_camera_sensors 00013 * 00014 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 00015 * 00016 * Author: Jan Fischer, email:jan.fischer@ipa.fhg.de 00017 * Supervised by: Jan Fischer, email:jan.fischer@ipa.fhg.de 00018 * 00019 * Date of creation: Jan 2010 00020 * ToDo: 00021 * 00022 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 00023 * 00024 * Redistribution and use in source and binary forms, with or without 00025 * modification, are permitted provided that the following conditions are met: 00026 * 00027 * * Redistributions of source code must retain the above copyright 00028 * notice, this list of conditions and the following disclaimer. 00029 * * Redistributions in binary form must reproduce the above copyright 00030 * notice, this list of conditions and the following disclaimer in the 00031 * documentation and/or other materials provided with the distribution. 00032 * * Neither the name of the Fraunhofer Institute for Manufacturing 00033 * Engineering and Automation (IPA) nor the names of its 00034 * contributors may be used to endorse or promote products derived from 00035 * this software without specific prior written permission. 00036 * 00037 * This program is free software: you can redistribute it and/or modify 00038 * it under the terms of the GNU Lesser General Public License LGPL as 00039 * published by the Free Software Foundation, either version 3 of the 00040 * License, or (at your option) any later version. 00041 * 00042 * This program is distributed in the hope that it will be useful, 00043 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00044 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00045 * GNU Lesser General Public License LGPL for more details. 00046 * 00047 * You should have received a copy of the GNU Lesser General Public 00048 * License LGPL along with this program. 00049 * If not, see <http://www.gnu.org/licenses/>. 00050 * 00051 ****************************************************************/ 00052 00053 //################## 00054 //#### includes #### 00055 00056 // standard includes 00057 //-- 00058 00059 // ROS includes 00060 #include <ros/ros.h> 00061 #include <image_transport/image_transport.h> 00062 #include <cv_bridge/CvBridge.h> 00063 00064 // ROS message includes 00065 #include <sensor_msgs/Image.h> 00066 #include <sensor_msgs/CameraInfo.h> 00067 #include <sensor_msgs/fill_image.h> 00068 #include <sensor_msgs/SetCameraInfo.h> 00069 #include <sensor_msgs/PointCloud.h> 00070 #include <sensor_msgs/PointCloud2.h> 00071 00072 #include <cob_camera_sensors/GetTOFImages.h> 00073 00074 // external includes 00075 #include <cob_camera_sensors/AbstractRangeImagingSensor.h> 00076 #include <cob_vision_utils/CameraSensorToolbox.h> 00077 #include <cob_vision_utils/GlobalDefines.h> 00078 #include <cob_vision_utils/VisionUtils.h> 00079 00080 #include <boost/thread/mutex.hpp> 00081 00082 using namespace ipa_CameraSensors; 00083 00084 class CobTofCameraNode 00085 { 00086 enum t_Mode 00087 { 00088 MODE_TOPIC = 0, 00089 MODE_SERVICE 00090 }; 00091 00092 private: 00093 ros::NodeHandle node_handle_; 00094 00095 image_transport::ImageTransport image_transport_; 00096 image_transport::CameraPublisher xyz_image_publisher_; 00097 image_transport::CameraPublisher grey_image_publisher_; 00098 ros::Publisher topicPub_pointCloud_; 00099 ros::Publisher topicPub_pointCloud2_; 00100 00101 sensor_msgs::CameraInfo camera_info_msg_; 00102 00103 ros::ServiceServer camera_info_service_; 00104 ros::ServiceServer image_service_; 00105 00106 AbstractRangeImagingSensorPtr tof_camera_; 00107 00108 std::string config_directory_; 00109 int tof_camera_index_; 00110 ipa_CameraSensors::t_cameraType tof_camera_type_; 00111 bool filter_xyz_by_amplitude_; 00112 bool filter_xyz_tearoff_edges_; 00113 int lower_amplitude_threshold_; 00114 int upper_amplitude_threshold_; 00115 double tearoff_tear_half_fraction_; 00116 00117 cv::Mat xyz_image_32F3_; 00118 cv::Mat grey_image_32F1_; 00119 00120 CobTofCameraNode::t_Mode ros_node_mode_; 00121 boost::mutex service_mutex_; 00122 00123 bool publish_point_cloud_; 00124 bool publish_point_cloud_2_; 00125 00126 public: 00128 CobTofCameraNode(const ros::NodeHandle& node_handle) 00129 : node_handle_(node_handle), 00130 image_transport_(node_handle), 00131 tof_camera_(AbstractRangeImagingSensorPtr()), 00132 xyz_image_32F3_(cv::Mat()), 00133 grey_image_32F1_(cv::Mat()), 00134 publish_point_cloud_(false), 00135 publish_point_cloud_2_(false) 00136 { 00138 } 00139 00141 ~CobTofCameraNode() 00142 { 00143 tof_camera_->Close(); 00144 } 00145 00148 bool init() 00149 { 00150 if (loadParameters() == false) 00151 { 00152 ROS_ERROR("[color_camera] Could not read all parameters from launch file"); 00153 return false; 00154 } 00155 00156 00157 if (tof_camera_->Init(config_directory_, tof_camera_index_) & ipa_CameraSensors::RET_FAILED) 00158 { 00159 00160 std::stringstream ss; 00161 ss << "Initialization of tof camera "; 00162 ss << tof_camera_index_; 00163 ss << " failed"; 00164 ROS_ERROR("[tof_camera] %s", ss.str().c_str()); 00165 tof_camera_ = AbstractRangeImagingSensorPtr(); 00166 return false; 00167 } 00168 00169 if (tof_camera_->Open() & ipa_CameraSensors::RET_FAILED) 00170 { 00171 std::stringstream ss; 00172 ss << "Could not open tof camera "; 00173 ss << tof_camera_index_; 00174 ROS_ERROR("[tof_camera] %s", ss.str().c_str()); 00175 tof_camera_ = AbstractRangeImagingSensorPtr(); 00176 return false; 00177 } 00178 00180 ipa_CameraSensors::t_cameraProperty cameraProperty; 00181 cameraProperty.propertyID = ipa_CameraSensors::PROP_CAMERA_RESOLUTION; 00182 tof_camera_->GetProperty(&cameraProperty); 00183 int range_sensor_width = cameraProperty.cameraResolution.xResolution; 00184 int range_sensor_height = cameraProperty.cameraResolution.yResolution; 00185 cv::Size range_image_size(range_sensor_width, range_sensor_height); 00186 00188 ipa_CameraSensors::CameraSensorToolboxPtr tof_sensor_toolbox = ipa_CameraSensors::CreateCameraSensorToolbox(); 00189 tof_sensor_toolbox->Init(config_directory_, tof_camera_->GetCameraType(), tof_camera_index_, range_image_size); 00190 00191 cv::Mat intrinsic_mat = tof_sensor_toolbox->GetIntrinsicMatrix(tof_camera_type_, tof_camera_index_); 00192 cv::Mat distortion_map_X = tof_sensor_toolbox->GetDistortionMapX(tof_camera_type_, tof_camera_index_); 00193 cv::Mat distortion_map_Y = tof_sensor_toolbox->GetDistortionMapY(tof_camera_type_, tof_camera_index_); 00194 tof_camera_->SetIntrinsics(intrinsic_mat, distortion_map_X, distortion_map_Y); 00195 00197 camera_info_service_ = node_handle_.advertiseService("set_camera_info", &CobTofCameraNode::setCameraInfo, this); 00198 image_service_ = node_handle_.advertiseService("get_images", &CobTofCameraNode::imageSrvCallback, this); 00199 xyz_image_publisher_ = image_transport_.advertiseCamera("image_xyz", 1); 00200 grey_image_publisher_ = image_transport_.advertiseCamera("image_grey", 1); 00201 if(publish_point_cloud_2_) topicPub_pointCloud2_ = node_handle_.advertise<sensor_msgs::PointCloud2>("point_cloud2", 1); 00202 if(publish_point_cloud_) topicPub_pointCloud_ = node_handle_.advertise<sensor_msgs::PointCloud>("point_cloud", 1); 00203 00204 cv::Mat d = tof_sensor_toolbox->GetDistortionParameters(tof_camera_type_, tof_camera_index_); 00205 camera_info_msg_.header.stamp = ros::Time::now(); 00206 camera_info_msg_.header.frame_id = "head_tof_link"; 00207 /*camera_info_msg_.D[0] = d.at<double>(0, 0); 00208 camera_info_msg_.D[1] = d.at<double>(0, 1); 00209 camera_info_msg_.D[2] = d.at<double>(0, 2); 00210 camera_info_msg_.D[3] = d.at<double>(0, 3); 00211 camera_info_msg_.D[4] = 0;*/ 00212 00213 cv::Mat k = tof_sensor_toolbox->GetIntrinsicMatrix(tof_camera_type_, tof_camera_index_); 00214 /*camera_info_msg_.K[0] = k.at<double>(0, 0); 00215 camera_info_msg_.K[1] = k.at<double>(0, 1); 00216 camera_info_msg_.K[2] = k.at<double>(0, 2); 00217 camera_info_msg_.K[3] = k.at<double>(1, 0); 00218 camera_info_msg_.K[4] = k.at<double>(1, 1); 00219 camera_info_msg_.K[5] = k.at<double>(1, 2); 00220 camera_info_msg_.K[6] = k.at<double>(2, 0); 00221 camera_info_msg_.K[7] = k.at<double>(2, 1); 00222 camera_info_msg_.K[8] = k.at<double>(2, 2);*/ 00223 00224 camera_info_msg_.width = range_sensor_width; 00225 camera_info_msg_.height = range_sensor_height; 00226 00227 return true; 00228 } 00229 00234 bool setCameraInfo(sensor_msgs::SetCameraInfo::Request& req, 00235 sensor_msgs::SetCameraInfo::Response& rsp) 00236 { 00238 camera_info_msg_ = req.camera_info; 00239 00240 rsp.success = false; 00241 rsp.status_message = "[tof_camera] Setting camera parameters through ROS not implemented"; 00242 00243 return true; 00244 } 00245 00247 bool spin() 00248 { 00249 boost::mutex::scoped_lock lock(service_mutex_); 00250 sensor_msgs::Image::Ptr xyz_image_msg_ptr; 00251 sensor_msgs::Image::Ptr grey_image_msg_ptr; 00252 sensor_msgs::CameraInfo tof_image_info; 00253 00254 if(tof_camera_->AcquireImages(0, &grey_image_32F1_, &xyz_image_32F3_, false, false, ipa_CameraSensors::INTENSITY_32F1) & ipa_Utils::RET_FAILED) 00255 { 00256 ROS_ERROR("[tof_camera] Tof image acquisition failed"); 00257 return false; 00258 } 00259 00261 //if(filter_xyz_tearoff_edges_ || filter_xyz_by_amplitude_) 00262 // ROS_ERROR("[tof_camera] FUNCTION UNCOMMENT BY JSF"); 00263 if(filter_xyz_tearoff_edges_) ipa_Utils::FilterTearOffEdges(xyz_image_32F3_, 0, (float)tearoff_tear_half_fraction_); 00264 if(filter_xyz_by_amplitude_) ipa_Utils::FilterByAmplitude(xyz_image_32F3_, grey_image_32F1_, 0, 0, lower_amplitude_threshold_, upper_amplitude_threshold_); 00265 00266 try 00267 { 00268 IplImage img = xyz_image_32F3_; 00269 xyz_image_msg_ptr = sensor_msgs::CvBridge::cvToImgMsg(&img, "passthrough"); 00270 } 00271 catch (sensor_msgs::CvBridgeException error) 00272 { 00273 ROS_ERROR("[tof_camera] Could not convert 32bit xyz IplImage to ROS message"); 00274 return false; 00275 } 00276 00277 try 00278 { 00279 IplImage img = grey_image_32F1_; 00280 grey_image_msg_ptr = sensor_msgs::CvBridge::cvToImgMsg(&img, "passthrough"); 00281 } 00282 catch (sensor_msgs::CvBridgeException error) 00283 { 00284 ROS_ERROR("[tof_camera] Could not convert 32bit grey IplImage to ROS message"); 00285 return false; 00286 } 00287 00289 ros::Time now = ros::Time::now(); 00290 xyz_image_msg_ptr->header.stamp = now; 00291 xyz_image_msg_ptr->header.frame_id = "head_tof_link"; 00292 grey_image_msg_ptr->header.stamp = now; 00293 grey_image_msg_ptr->header.frame_id = "head_tof_link"; 00294 00295 tof_image_info = camera_info_msg_; 00296 tof_image_info.width = grey_image_32F1_.cols; 00297 tof_image_info.height = grey_image_32F1_.rows; 00298 tof_image_info.header.stamp = now; 00299 tof_image_info.header.frame_id = "head_tof_link"; 00300 00302 xyz_image_publisher_.publish(*xyz_image_msg_ptr, tof_image_info); 00303 grey_image_publisher_.publish(*grey_image_msg_ptr, tof_image_info); 00304 00305 if(publish_point_cloud_) publishPointCloud(now); 00306 if(publish_point_cloud_2_) publishPointCloud2(now); 00307 00308 return true; 00309 } 00310 00311 void publishPointCloud(ros::Time now) 00312 { 00313 ROS_DEBUG("convert xyz_image to point_cloud"); 00314 sensor_msgs::PointCloud pc_msg; 00315 // create point_cloud message 00316 pc_msg.header.stamp = now; 00317 pc_msg.header.frame_id = "head_tof_link"; 00318 00319 cv::Mat cpp_xyz_image_32F3 = xyz_image_32F3_; 00320 cv::Mat cpp_grey_image_32F1 = grey_image_32F1_; 00321 00322 float* f_ptr = 0; 00323 for (int row = 0; row < cpp_xyz_image_32F3.rows; row++) 00324 { 00325 f_ptr = cpp_xyz_image_32F3.ptr<float>(row); 00326 for (int col = 0; col < cpp_xyz_image_32F3.cols; col++) 00327 { 00328 geometry_msgs::Point32 pt; 00329 pt.x = f_ptr[3*col + 0]; 00330 pt.y = f_ptr[3*col + 1]; 00331 pt.z = f_ptr[3*col + 2]; 00332 pc_msg.points.push_back(pt); 00333 } 00334 } 00335 topicPub_pointCloud_.publish(pc_msg); 00336 } 00337 00338 void publishPointCloud2(ros::Time now) 00339 { 00340 cv::Mat cpp_xyz_image_32F3 = xyz_image_32F3_; 00341 cv::Mat cpp_confidence_mask_32F1 = grey_image_32F1_; 00342 00343 sensor_msgs::PointCloud2 pc_msg; 00344 // create point_cloud message 00345 pc_msg.header.stamp = now; 00346 pc_msg.header.frame_id = "head_tof_link"; 00347 pc_msg.width = cpp_xyz_image_32F3.cols; 00348 pc_msg.height = cpp_xyz_image_32F3.rows; 00349 pc_msg.fields.resize(4); 00350 pc_msg.fields[0].name = "x"; 00351 pc_msg.fields[0].datatype = sensor_msgs::PointField::FLOAT32; 00352 pc_msg.fields[1].name = "y"; 00353 pc_msg.fields[1].datatype = sensor_msgs::PointField::FLOAT32; 00354 pc_msg.fields[2].name = "z"; 00355 pc_msg.fields[2].datatype = sensor_msgs::PointField::FLOAT32; 00356 pc_msg.fields[3].name = "confidence"; 00357 pc_msg.fields[3].datatype = sensor_msgs::PointField::FLOAT32; 00358 int offset = 0; 00359 for (size_t d = 0; d < pc_msg.fields.size(); ++d, offset += 4) 00360 { 00361 pc_msg.fields[d].offset = offset; 00362 } 00363 pc_msg.point_step = 16; 00364 pc_msg.row_step = pc_msg.point_step * pc_msg.width; 00365 pc_msg.data.resize (pc_msg.width*pc_msg.height*pc_msg.point_step); 00366 pc_msg.is_dense = true; 00367 pc_msg.is_bigendian = false; 00368 00369 float* f_ptr = 0; 00370 float* g_ptr = 0; 00371 int pc_msg_idx=0; 00372 for (int row = 0; row < cpp_xyz_image_32F3.rows; row++) 00373 { 00374 f_ptr = cpp_xyz_image_32F3.ptr<float>(row); 00375 g_ptr = cpp_confidence_mask_32F1.ptr<float>(row); 00376 for (int col = 0; col < cpp_xyz_image_32F3.cols; col++, pc_msg_idx++) 00377 { 00378 memcpy(&pc_msg.data[pc_msg_idx * pc_msg.point_step], &f_ptr[3*col], 3*sizeof(float)); 00379 memcpy(&pc_msg.data[pc_msg_idx * pc_msg.point_step + pc_msg.fields[3].offset], &g_ptr[col], sizeof(float)); 00380 } 00381 } 00382 topicPub_pointCloud2_.publish(pc_msg); 00383 } 00384 00385 bool imageSrvCallback(cob_camera_sensors::GetTOFImages::Request &req, 00386 cob_camera_sensors::GetTOFImages::Response &res) 00387 { 00388 boost::mutex::scoped_lock lock(service_mutex_); 00389 // Convert openCV IplImages to ROS messages 00390 try 00391 { 00392 IplImage grey_img = grey_image_32F1_; 00393 IplImage xyz_img = xyz_image_32F3_; 00394 res.greyImage = *(sensor_msgs::CvBridge::cvToImgMsg(&grey_img, "passthrough")); 00395 res.xyzImage = *(sensor_msgs::CvBridge::cvToImgMsg(&xyz_img, "passthrough")); 00396 } 00397 catch (sensor_msgs::CvBridgeException error) 00398 { 00399 ROS_ERROR("[tof_camera_type_node] Could not convert IplImage to ROS message"); 00400 } 00401 00402 // Set time stamp 00403 ros::Time now = ros::Time::now(); 00404 res.greyImage.header.stamp = now; 00405 res.greyImage.header.frame_id = "head_tof_link"; 00406 res.xyzImage.header.stamp = now; 00407 res.xyzImage.header.frame_id = "head_tof_link"; 00408 return true; 00409 } 00410 00411 bool loadParameters() 00412 { 00413 std::string tmp_string = "NULL"; 00414 00416 if (node_handle_.getParam("tof_camera/configuration_files", config_directory_) == false) 00417 { 00418 ROS_ERROR("[tof_camera] Path to xml configuration for tof camera not specified"); 00419 return false; 00420 } 00421 00422 ROS_INFO("Configuration directory: %s", config_directory_.c_str()); 00423 00425 if (node_handle_.getParam("tof_camera/tof_camera_index", tof_camera_index_) == false) 00426 { 00427 ROS_ERROR("[tof_camera] 'tof_camera_index' (0 or 1) not specified"); 00428 return false; 00429 } 00430 00432 if (node_handle_.getParam("tof_camera/tof_camera_type", tmp_string) == false) 00433 { 00434 ROS_ERROR("[tof_camera] 'tof_camera_type' not specified"); 00435 return false; 00436 } 00437 if (tmp_string == "CAM_SWISSRANGER") 00438 { 00439 tof_camera_ = ipa_CameraSensors::CreateRangeImagingSensor_Swissranger(); 00440 tof_camera_type_ = ipa_CameraSensors::CAM_SWISSRANGER; 00441 } 00442 else if (tmp_string == "CAM_VIRTUAL") 00443 { 00444 tof_camera_ = ipa_CameraSensors::CreateRangeImagingSensor_VirtualCam(); 00445 tof_camera_type_ = ipa_CameraSensors::CAM_VIRTUALRANGE; 00446 } 00447 else 00448 { 00449 std::string str = "[tof_camera] Camera type '" + tmp_string + "' unknown, try 'CAM_SWISSRANGER'"; 00450 ROS_ERROR("%s", str.c_str()); 00451 return false; 00452 } 00453 00454 ROS_INFO("Camera type: %s_%d", tmp_string.c_str(), tof_camera_index_); 00455 00457 if (node_handle_.getParam("tof_camera/filter_xyz_by_amplitude", filter_xyz_by_amplitude_) == false) 00458 { 00459 ROS_ERROR("[tof_camera] 'filter_xyz_by_amplitude not specified"); 00460 return false; 00461 } 00463 if (node_handle_.getParam("tof_camera/filter_xyz_tearoff_edges", filter_xyz_tearoff_edges_) == false) 00464 { 00465 ROS_ERROR("[tof_camera] 'filter_xyz_tearoff_edges_' not specified"); 00466 return false; 00467 } 00469 if (node_handle_.getParam("tof_camera/lower_amplitude_threshold", lower_amplitude_threshold_) == false) 00470 { 00471 ROS_ERROR("[tof_camera] 'lower_amplitude_threshold' not specified"); 00472 return false; 00473 } 00475 if (node_handle_.getParam("tof_camera/upper_amplitude_threshold", upper_amplitude_threshold_) == false) 00476 { 00477 ROS_ERROR("[tof_camera] 'upper_amplitude_threshold' not specified"); 00478 return false; 00479 } 00481 if (node_handle_.getParam("tof_camera/tearoff_pi_half_fraction", tearoff_tear_half_fraction_) == false) 00482 { 00483 ROS_ERROR("[tof_camera] 'tearoff_pi_half_fraction' not specified"); 00484 return false; 00485 } 00487 if (node_handle_.getParam("tof_camera/ros_node_mode", tmp_string) == false) 00488 { 00489 ROS_ERROR("[tof_camera] Mode for tof camera node not specified"); 00490 return false; 00491 } 00492 if (tmp_string == "MODE_SERVICE") 00493 { 00494 ros_node_mode_ = CobTofCameraNode::MODE_SERVICE; 00495 } 00496 else if (tmp_string == "MODE_TOPIC") 00497 { 00498 ros_node_mode_ = CobTofCameraNode::MODE_TOPIC; 00499 } 00500 else 00501 { 00502 std::string str = "[tof_camera] Mode '" + tmp_string + "' unknown, try 'MODE_SERVICE' or 'MODE_TOPIC'"; 00503 ROS_ERROR("%s", str.c_str()); 00504 return false; 00505 } 00506 00507 if(node_handle_.getParam("tof_camera/publish_point_cloud", publish_point_cloud_) == false) 00508 { 00509 ROS_WARN("[tof_camera] Flag for publishing PointCloud not set, falling back to default (false)"); 00510 } 00511 if(node_handle_.getParam("tof_camera/publish_point_cloud_2", publish_point_cloud_2_) == false) 00512 { 00513 ROS_WARN("[tof_camera] Flag for publishing PointCloud2 not set, falling back to default (false)"); 00514 } 00515 00516 00517 ROS_INFO("ROS node mode: %s", tmp_string.c_str()); 00518 00519 return true; 00520 } 00521 }; 00522 00523 00524 //####################### 00525 //#### main programm #### 00526 int main(int argc, char** argv) 00527 { 00529 ros::init(argc, argv, "tof_camera"); 00530 00532 ros::NodeHandle nh; 00533 00535 CobTofCameraNode camera_node(nh); 00536 00538 if (!camera_node.init()) return 0; 00539 00540 //ros::spin(); 00541 ros::Rate rate(100); 00542 while(nh.ok()) 00543 { 00544 camera_node.spin(); 00545 ros::spinOnce(); 00546 rate.sleep(); 00547 } 00548 00549 return 0; 00550 }