$search
00001 /****************************************************************************** 00002 * \file 00003 * 00004 * $Id:$ 00005 * 00006 * Copyright (C) Brno University of Technology 00007 * 00008 * This file is part of software developed by dcgm-robotics@FIT group. 00009 * 00010 * Author: Vit Stancl (stancl@fit.vutbr.cz) 00011 * Supervised by: Michal Spanel (spanel@fit.vutbr.cz) 00012 * Date: 25/1/2012 00013 * 00014 * This file is free software: you can redistribute it and/or modify 00015 * it under the terms of the GNU Lesser General Public License as published by 00016 * the Free Software Foundation, either version 3 of the License, or 00017 * (at your option) any later version. 00018 * 00019 * This file is distributed in the hope that it will be useful, 00020 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00021 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00022 * GNU Lesser General Public License for more details. 00023 * 00024 * You should have received a copy of the GNU Lesser General Public License 00025 * along with this file. If not, see <http://www.gnu.org/licenses/>. 00026 */ 00027 00028 #include <srs_env_model/but_server/registration/CPCtoOCRegistration.h> 00029 00030 00031 #include <srs_env_model/but_server/registration/pcl_registration_module.h> 00032 #include <srs_env_model/topics_list.h> 00033 #include <pcl_ros/transforms.h> 00034 #include <pcl/io/io.h> 00035 00036 00040 srs_env_model::COcPatchMaker::COcPatchMaker() 00041 : m_bTransformCamera( false ) 00042 , m_pcFrameId("/map") 00043 , m_bSpinThread( false ) 00044 , m_bCamModelInitialized(false) 00045 , m_camera_stereo_offset_left(0) 00046 , m_camera_stereo_offset_right(0) 00047 , m_bPublishCloud(false) 00048 , m_fracX(1.5) 00049 , m_fracY(1.5) 00050 { 00051 00052 } 00053 00057 void srs_env_model::COcPatchMaker::init(ros::NodeHandle & node_handle) 00058 { 00059 ROS_DEBUG("Initializing CCompressedPointCloudPlugin"); 00060 00061 if ( m_bSpinThread ) 00062 { 00063 // if we're spinning our own thread, we'll also need our own callback queue 00064 node_handle.setCallbackQueue( &callback_queue_ ); 00065 00066 need_to_terminate_ = false; 00067 spin_thread_.reset( new boost::thread(boost::bind(&COcPatchMaker::spinThread, this)) ); 00068 node_handle_ = node_handle; 00069 } 00070 00071 // Read parameters 00072 { 00073 // Where to get camera position information 00074 node_handle.param("camera_info_topic_name", m_cameraInfoTopic, CPC_CAMERA_INFO_PUBLISHER_NAME ); 00075 } 00076 00077 00078 // Subscribe to position topic 00079 // Create subscriber 00080 m_camPosSubscriber = node_handle.subscribe<sensor_msgs::CameraInfo>( m_cameraInfoTopic, 10, &srs_env_model::COcPatchMaker::onCameraChangedCB, this ); 00081 00082 if (!m_camPosSubscriber) 00083 { 00084 ROS_ERROR("Not subscribed..."); 00085 ROS_ERROR( "Cannot subscribe to the camera position publisher..." ); 00086 } 00087 00088 // stereo cam params for sensor cone: 00089 // node_handle.param<int> ("compressed_pc_camera_stereo_offset_left", m_camera_stereo_offset_left, 0); // 128 00090 // node_handle.param<int> ("compressed_pc_camera_stereo_offset_right", m_camera_stereo_offset_right, 0); 00091 00092 node_handle.param("registration_patch_view_fraction_x", m_fracX, m_fracX ); 00093 node_handle.param("registration_patch_view_fraction_y", m_fracY, m_fracY ); 00094 00095 node_handle.param("registration_patch_publish_cloud", m_bPublishCloud, m_bPublishCloud ); 00096 00097 if( m_bPublishCloud ) 00098 // Create publisher - simple point cloud 00099 m_pubConstrainedPC = node_handle.advertise<sensor_msgs::PointCloud2> (REGISTRATION_CONSTRAINED_CLOUD_PUBLISHER_NAME, 5, false); 00100 00101 } 00102 00106 bool srs_env_model::COcPatchMaker::computeCloud( const SMapWithParameters & par, const ros::Time & time ) 00107 { 00108 ROS_DEBUG( "CCompressedPointCloudPlugin: onFrameStart" ); 00109 00110 // Copy buffered camera normal and d parameter 00111 boost::recursive_mutex::scoped_lock lock( m_camPosMutex ); 00112 00113 // Clear data 00114 m_cloud.clear(); 00115 m_ocFrameId = par.frameId; 00116 m_DataTimeStamp = m_time_stamp = time; 00117 00118 bool bTransformOutput = m_ocFrameId != m_pcFrameId; 00119 00120 // Output transform matrix 00121 Eigen::Matrix4f pcOutTM; 00122 00123 // If different frame id 00124 if( bTransformOutput ) 00125 { 00126 tf::StampedTransform ocToPcTf; 00127 00128 // Get transform 00129 try { 00130 // Transformation - to, from, time, waiting time 00131 m_tfListener.waitForTransform(m_pcFrameId, m_ocFrameId, 00132 m_time_stamp, ros::Duration(5)); 00133 00134 m_tfListener.lookupTransform(m_pcFrameId, m_ocFrameId, 00135 m_time_stamp, ocToPcTf); 00136 00137 } catch (tf::TransformException& ex) { 00138 ROS_ERROR_STREAM("Transform error: " << ex.what() << ", quitting callback"); 00139 ROS_ERROR_STREAM( "Transform error."); 00140 return false; 00141 } 00142 00143 00144 // Get transformation matrix 00145 pcl_ros::transformAsMatrix(ocToPcTf, pcOutTM); // Sensor TF to defined base TF 00146 00147 } 00148 00149 if( m_cameraFrameId.size() == 0 ) 00150 { 00151 ROS_ERROR_STREAM("Wrong camera frame id..."); 00152 m_bTransformCamera = false; 00153 return false; 00154 } 00155 00156 m_bTransformCamera = m_cameraFrameId != m_ocFrameId; 00157 00158 00159 // Store camera information 00160 m_camera_size = m_camera_size_buffer; 00161 m_camera_model.fromCameraInfo( m_camera_info_buffer); 00162 00163 // Transform camera model to the cloud time and frame id 00164 { 00165 tf::StampedTransform camTf; 00166 00167 // Modify camera position to the current time 00168 if( m_bTransformCamera ) 00169 { 00170 // We need camera to octomap transfor 00171 try { 00172 // Transformation - to, from, time, waiting time 00173 // std::cerr << "T1, try to get transform from " << m_cameraFrameId << " to " << m_ocFrameId << ", time: " << par.currentTime << std::endl; 00174 m_tfListener.waitForTransform(m_ocFrameId, m_cameraFrameId, 00175 m_time_stamp, ros::Duration(5.0)); 00176 00177 m_tfListener.lookupTransform(m_ocFrameId, m_cameraFrameId, 00178 m_time_stamp, camTf); 00179 00180 } catch (tf::TransformException& ex) { 00181 ROS_ERROR_STREAM("Transform error1: " << ex.what() << ", quitting callback"); 00182 return false; 00183 } 00184 00185 } 00186 else 00187 { 00188 // Just move camera position "in time" 00189 try { 00190 // Time transformation - target frame, target time, source frame, source time, fixed frame, time, waiting time 00191 m_tfListener.waitForTransform(m_cameraFrameId, m_time_stamp, 00192 m_cameraFrameId, m_camera_info_buffer.header.stamp, 00193 "/map", ros::Duration(1.0)); 00194 00195 m_tfListener.lookupTransform( m_cameraFrameId, m_time_stamp, 00196 m_cameraFrameId, m_camera_info_buffer.header.stamp, 00197 "/map", camTf); 00198 00199 } catch (tf::TransformException& ex) { 00200 ROS_ERROR_STREAM("Transform error2: " << ex.what() << ", quitting callback"); 00201 return false; 00202 } 00203 } 00204 00205 // We have transform so convert it to the eigen matrix 00206 m_to_sensorTf = camTf.inverse(); 00207 } 00208 00209 // Initialize leaf iterators 00210 tButServerOcTree & tree( par.map->getTree() ); 00211 srs_env_model::tButServerOcTree::leaf_iterator it, itEnd( tree.end_leafs() ); 00212 00213 // Compute fraction matrix 00214 m_fracMatrix = btMatrix3x3::getIdentity().scaled( tf::Point( 1.0 / m_fracX, 1.0 / m_fracY, 1.0 ) ); 00215 00216 // Crawl through nodes 00217 for ( it = tree.begin_leafs(par.treeDepth); it != itEnd; ++it) 00218 { 00219 // Node is occupied? 00220 if (tree.isNodeOccupied(*it)) 00221 { 00222 handleOccupiedNode(it, par); 00223 }// Node is occupied? 00224 00225 } // Iterate through octree 00226 00227 if( bTransformOutput ) 00228 { 00229 // transform point cloud from octomap frame to the preset frame 00230 pcl::transformPointCloud< tPclPoint >(m_cloud, m_cloud, pcOutTM); 00231 } 00232 00233 if( m_bPublishCloud ) 00234 publishInternal( m_DataTimeStamp ); 00235 00236 return true; 00237 } 00238 00242 void srs_env_model::COcPatchMaker::handleOccupiedNode(srs_env_model::tButServerOcTree::iterator& it, const SMapWithParameters & mp) 00243 { 00244 // PERROR("OnHandleOccupied"); 00245 00246 if( ! m_bCamModelInitialized ) 00247 return; 00248 00249 // Test input point 00250 tf::Point pos(it.getX(), it.getY(), it.getZ()); 00251 // if( m_bTransformCamera ) 00252 pos = m_to_sensorTf(pos); 00253 00254 pos = pos * m_fracMatrix; 00255 00256 if( pos.z() < 0 ) 00257 return; 00258 00259 cv::Point2d uv = m_camera_model.project3dToPixel(cv::Point3d( pos.x(), pos.y(), pos.z())); 00260 00261 // ignore point if not in sensor cone 00262 if (!inSensorCone(uv)) 00263 return; 00264 00265 // Ok, add it... 00266 // std::cerr << "PCP: handle occupied" << std::endl; 00267 tPclPoint point; 00268 00269 // Set position 00270 point.x = it.getX(); 00271 point.y = it.getY(); 00272 point.z = it.getZ(); 00273 00274 // Set color 00275 point.r = it->r(); 00276 point.g = it->g(); 00277 point.b = it->b(); 00278 00279 // std::cerr << "Occupied node r " << (int)point.r << ", g " << (int)point.g << ", b " << (int)point.b << std::endl; 00280 00281 m_cloud.push_back( point ); 00282 } 00283 00287 void srs_env_model::COcPatchMaker::onCameraChangedCB(const sensor_msgs::CameraInfo::ConstPtr &cam_info) 00288 { 00289 boost::recursive_mutex::scoped_lock lock( m_camPosMutex ); 00290 00291 //PERROR("OnCameraChange."); 00292 00293 // ROS_DEBUG( "CCompressedPointCloudPlugin: onCameraChangedCB" ); 00294 00295 // Set camera position frame id 00296 m_cameraFrameId = cam_info->header.frame_id; 00297 00298 00299 ROS_DEBUG("COcToPcl: Set camera info: %d x %d\n", cam_info->height, cam_info->width); 00300 m_camera_model_buffer.fromCameraInfo(*cam_info); 00301 m_camera_size_buffer = m_camera_model_buffer.fullResolution(); 00302 00303 // Set flag 00304 m_bCamModelInitialized = true; 00305 00306 m_camera_info_buffer = *cam_info; 00307 } 00308 00309 00313 bool srs_env_model::COcPatchMaker::inSensorCone(const cv::Point2d& uv) const 00314 { 00315 const double x_offset( 0.0 ), y_offset( 0.0 ); 00316 00317 //PERROR( uv.y << " > " << 1 << " && " << uv.y << " < " << m_camera_size.height - 2 ); 00318 // Check if projected 2D coordinate in pixel range. 00319 // This check is a little more restrictive than it should be by using 00320 // 1 pixel less to account for rounding / discretization errors. 00321 // Otherwise points on the corner are accounted to be in the sensor cone. 00322 bool rv((uv.x > m_camera_stereo_offset_left + 1 - x_offset) && 00323 (uv.x < m_camera_size.width + m_camera_stereo_offset_right - 2 + x_offset) && 00324 (uv.y > 1 - y_offset) && 00325 (uv.y < m_camera_size.height - 2 + y_offset)); 00326 00327 if( ! rv ) 00328 { 00329 // std::cerr << uv.x << ", " << uv.y << " > " << m_camera_size.width << ", " << m_camera_size.height << std::endl; 00330 } 00331 00332 return rv; 00333 } 00334 00338 void srs_env_model::COcPatchMaker::spinThread() 00339 { 00340 while (node_handle_.ok()) 00341 { 00342 if (need_to_terminate_) 00343 { 00344 break; 00345 } 00346 callback_queue_.callAvailable(ros::WallDuration(0.033f)); 00347 } 00348 } 00349 00353 void srs_env_model::COcPatchMaker::publishInternal(const ros::Time & timestamp) 00354 { 00355 // Convert data 00356 sensor_msgs::PointCloud2 msg; 00357 pcl::toROSMsg< tPclPoint >(m_cloud, msg); 00358 00359 // Set message parameters and publish 00360 msg.header.frame_id = m_pcFrameId; 00361 msg.header.stamp = m_cloud.header.stamp; 00362 00363 // Publish data 00364 m_pubConstrainedPC.publish( msg ); 00365 } 00366 00367 //============================================================================= 00368 // CPcToOcRegistration 00369 //============================================================================= 00370 00374 srs_env_model::CPcToOcRegistration::CPcToOcRegistration() 00375 : m_resampledCloud( new sensor_msgs::PointCloud2 () ) 00376 { 00377 00378 } 00379 00383 void srs_env_model::CPcToOcRegistration::init(ros::NodeHandle & node_handle) 00384 { 00385 // Initialize modules 00386 m_patchMaker.init( node_handle ); 00387 m_registration.init( node_handle ); 00388 } 00389 00393 bool srs_env_model::CPcToOcRegistration::registerCloud( tPointCloudPtr & cloud, const SMapWithParameters & map ) 00394 { 00395 if( !m_registration.isRegistering() ) 00396 { 00397 ROS_ERROR( "No registration method selected." ); 00398 return false; 00399 } 00400 00401 // std::cerr << "Reg start" << std::endl; 00402 00403 // Get patch 00404 m_patchMaker.setCloudFrameId( map.frameId ); 00405 m_patchMaker.computeCloud( map, cloud->header.stamp ); 00406 00407 if( m_patchMaker.getCloud().size() == 0 ) 00408 return false; 00409 00410 // Resample input cloud 00411 sensor_msgs::PointCloud2::Ptr cloudMsg(new sensor_msgs::PointCloud2 ()); 00412 00413 pcl::toROSMsg( *cloud, *cloudMsg); 00414 // std::cerr << "Patch size: " << m_patchMaker.getCloud().size() << ", " << cloudMsg->data.size() << std::endl; 00415 00416 m_gridFilter.setInputCloud( cloudMsg ); 00417 m_gridFilter.setLeafSize( map.resolution, map.resolution, map.resolution ); 00418 m_gridFilter.filter( *m_resampledCloud ); 00419 00420 // std::cerr << "Voxel grid size: " << m_resampledCloud->data.size() << std::endl; 00421 00422 // Try to register cloud 00423 tPointCloudPtr cloudSource( new tPointCloud() ); 00424 tPointCloudPtr cloudBuffer( new tPointCloud() ); 00425 tPointCloudPtr cloudTarget( new tPointCloud() ); 00426 pcl::fromROSMsg( *m_resampledCloud, *cloudSource ); 00427 pcl::copyPointCloud( m_patchMaker.getCloud(), *cloudTarget ); 00428 00429 // std::cerr << "Calling registration: " << cloudSource->size() << ", " << cloudTarget->size() << std::endl; 00430 00431 bool rv( m_registration.process( cloudSource, cloudTarget, cloudBuffer ) ); 00432 00433 if( ! rv ) 00434 std::cerr << "Registration failed" << std::endl; 00435 00436 return rv; 00437 }