$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: dd/mm/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/plugins/cmap_plugin.h> 00029 #include <srs_env_model/topics_list.h> 00030 00031 #include <pcl_ros/transforms.h> 00032 00033 #define FIXED_FRAME "/map" 00034 00035 srs_env_model::CCMapPlugin::CCMapPlugin(const std::string & name) 00036 : srs_env_model::CServerPluginBase(name) 00037 , m_cmapPublisherName(COLLISION_MAP_PUBLISHER_NAME) 00038 , m_collisionMapLimitRadius(2.0) 00039 , m_collisionMapVersion(0) 00040 , m_cmapFrameId(COLLISION_MAP_FRAME_ID) 00041 , m_dataBuffer( new tData ) 00042 , m_publishCollisionMap( true ) 00043 , m_tfListener( ros::Duration(tf::Transformer::DEFAULT_CACHE_TIME), true ) 00044 , m_latchedTopics(false) 00045 , m_bConvertPoint( false ) 00046 , m_mapTime(0) 00047 { 00048 } 00049 00051 void srs_env_model::CCMapPlugin::init(ros::NodeHandle & node_handle) 00052 { 00053 m_collisionMapVersion = 0; 00054 m_dataBuffer->boxes.clear(); 00055 m_data->boxes.clear(); 00056 00057 // Read parameters 00058 00059 // Get collision map radius 00060 node_handle.param("collision_map_radius", m_collisionMapLimitRadius, COLLISION_MAP_RADIUS_LIMIT ); 00061 00062 // Get collision map topic name 00063 node_handle.param("collision_map_publisher", m_cmapPublisherName, COLLISION_MAP_PUBLISHER_NAME ); 00064 00065 // Get FID to which will be points transformed when publishing collision map 00066 node_handle.param("collision_map_frame_id", m_cmapFrameId, COLLISION_MAP_FRAME_ID ); // 00067 00068 // Create and publish service - get collision map 00069 m_serviceGetCollisionMap = node_handle.advertiseService( GetCollisionMap_SRV, &CCMapPlugin::getCollisionMapSrvCallback, this); 00070 00071 // Create and publish service - is new collision map 00072 m_serviceIsNewCMap = node_handle.advertiseService( IsNewCMap_SRV, &CCMapPlugin::isNewCmapSrvCallback, this ); 00073 00074 // Create and publish service - lock map 00075 m_serviceLockCMap = node_handle.advertiseService( LockCMap_SRV, &CCMapPlugin::lockCmapSrvCallback, this ); 00076 00077 // Create and publish service - remove cube from map 00078 m_serviceRemoveCube = node_handle.advertiseService( RemoveCubeCMP_SRV, &CCMapPlugin::removeBoxCallback, this ); 00079 00080 // Create and publish service - add cube to map 00081 m_serviceAddCube = node_handle.advertiseService( AddCubeCMP_SRV, &CCMapPlugin::addBoxCallback, this ); 00082 00083 // Connect publishing services 00084 pause( false, node_handle ); 00085 } 00086 00088 void srs_env_model::CCMapPlugin::publishInternal(const ros::Time & timestamp) 00089 { 00090 // Lock data 00091 boost::mutex::scoped_lock lock(m_lockData); 00092 00093 // Should map be published? 00094 bool publishCollisionMap = m_publishCollisionMap && (m_latchedTopics || m_cmapPublisher.getNumSubscribers() > 0); 00095 00096 // Test collision maps and swap them, if needed 00097 if( ! sameCMaps( m_data.get(), m_dataBuffer.get() ) ) 00098 { 00099 // CMaps differs, increase version index and swap them 00100 ++m_collisionMapVersion; 00101 m_mapTime = timestamp; 00102 swap( m_data, m_dataBuffer ); 00103 00104 lock.unlock(); 00105 00106 // Call invalidation 00107 invalidate(); 00108 } 00109 00110 if( m_bLocked ) 00111 { 00112 if( m_mapTime != timestamp ) 00113 { 00114 retransformTF(timestamp); 00115 } 00116 } 00117 00118 // Publish collision map 00119 if (publishCollisionMap) { 00120 // std::cerr << "Publishing cmap. Frame id: " << m_cmapFrameId << ", Size: " << m_data->boxes.size() << std::endl; 00121 m_data->header.frame_id = m_cmapFrameId; 00122 m_data->header.stamp = timestamp; 00123 m_cmapPublisher.publish(*m_data); 00124 } 00125 } 00126 00128 void srs_env_model::CCMapPlugin::newMapDataCB( SMapWithParameters & par ) 00129 { 00130 if( m_bLocked ) 00131 { 00132 return; 00133 } 00134 // Lock data 00135 boost::mutex::scoped_lock lock(m_lockData); 00136 00137 // Reset collision map buffer 00138 m_dataBuffer->boxes.clear(); 00139 00140 std::string robotBaseFrameId("/base_footprint"); 00141 00142 // Get octomap to collision map transform matrix 00143 tf::StampedTransform omapToCmapTf, // Octomap to collision map 00144 baseToOmapTf, // Robot baselink to octomap 00145 cmapToWorldTF; // Collision map to world 00146 try 00147 { 00148 // Transformation - to, from, time, waiting time 00149 m_tfListener.waitForTransform(m_cmapFrameId, par.frameId, par.currentTime, ros::Duration(5)); 00150 m_tfListener.lookupTransform(m_cmapFrameId, par.frameId, par.currentTime, omapToCmapTf); 00151 00152 m_tfListener.waitForTransform(par.frameId, robotBaseFrameId, par.currentTime, ros::Duration(5)); 00153 m_tfListener.lookupTransform(par.frameId, robotBaseFrameId, par.currentTime, baseToOmapTf); 00154 } 00155 catch (tf::TransformException& ex) { 00156 ROS_ERROR_STREAM("Transform error: " << ex.what() << ", quitting callback"); 00157 PERROR( "Transform error."); 00158 return; 00159 } 00160 00161 00162 // World TF to the collision map TF 00163 pcl_ros::transformAsMatrix(omapToCmapTf, m_worldToCMapTM ); 00164 00165 // Disassemble world to cmap translation and rotation 00166 m_worldToCMapRot = m_worldToCMapTM.block<3, 3> (0, 0); 00167 m_worldToCMapTrans = m_worldToCMapTM.block<3, 1> (0, 3); 00168 00169 m_bConvertPoint = m_cmapFrameId != par.frameId; 00170 00171 // Compute robot position in the collision map coordinate system 00172 geometry_msgs::TransformStamped msg; 00173 tf::transformStampedTFToMsg(baseToOmapTf, msg); 00174 m_robotBasePosition.setX( msg.transform.translation.x ); 00175 m_robotBasePosition.setY( msg.transform.translation.y ); 00176 m_robotBasePosition.setZ( msg.transform.translation.z ); 00177 00178 tButServerOcTree & tree( par.map->getTree() ); 00179 srs_env_model::tButServerOcTree::leaf_iterator it, itEnd( tree.end_leafs() ); 00180 00181 // Crawl through nodes 00182 for ( it = tree.begin_leafs(m_crawlDepth); it != itEnd; ++it) 00183 { 00184 // Node is occupied? 00185 if (tree.isNodeOccupied(*it)) 00186 { 00187 handleOccupiedNode(it, par); 00188 }// Node is occupied? 00189 00190 } // Iterate through octree 00191 00192 m_DataTimeStamp = par.currentTime; 00193 00194 lock.unlock(); 00195 00196 invalidate(); 00197 } 00198 00200 void srs_env_model::CCMapPlugin::handleOccupiedNode(srs_env_model::tButServerOcTree::iterator& it, const SMapWithParameters & mp) 00201 { 00202 if( m_bLocked ) 00203 return; 00204 00205 // Should we publish something? 00206 if (! m_publishCollisionMap) 00207 return; 00208 00209 // Is this point near enough to the robot? 00210 if( ! isNearRobot( btVector3( it.getX(), it.getY(), it.getZ() ), it.getSize() ) ) 00211 return; 00212 00213 Eigen::Vector3f point( it.getX(), it.getY(), it.getZ() ); 00214 00215 if( m_bConvertPoint ) 00216 { 00217 // Transform point from the world to the CMap TF 00218 point = m_worldToCMapRot * point + m_worldToCMapTrans; 00219 } 00220 00221 // Add point to the collision map 00222 //arm_navigation_msgs::OrientedBoundingBox box; 00223 tBox box; 00224 00225 double size = it.getSize(); 00226 box.extents.x = box.extents.y = box.extents.z = size; 00227 box.axis.x = box.axis.y = 0.0; 00228 box.axis.z = 1.0; 00229 box.angle = 0.0; 00230 box.center.x = point[0]; 00231 box.center.y = point[1]; 00232 box.center.z = point[2]; 00233 00234 00235 m_dataBuffer->boxes.push_back(box); 00236 } 00237 00245 bool srs_env_model::CCMapPlugin::sameCMaps( arm_navigation_msgs::CollisionMap * map1, arm_navigation_msgs::CollisionMap * map2 ) 00246 { 00247 // Do not swap maps, if in the locked mode 00248 if( m_bLocked ) 00249 return true; 00250 00251 // Wrong input 00252 if( map1 == 0 || map2 == 0 ) 00253 { 00254 // std::cerr << "Some null. map1: " << map1 << ", map2: " << map2 << std::endl; 00255 return false; 00256 } 00257 00258 // Two maps cannot be the same, if differs in the size 00259 if( map1->boxes.size() != map2->boxes.size() ) 00260 { 00261 // std::cerr << "Maps size different. map1: " << map1->boxes.size() << ", map2: " << map2->boxes.size() << std::endl; 00262 return false; 00263 } 00264 00265 // Compare maps box by box 00266 arm_navigation_msgs::CollisionMap::_boxes_type::iterator icm1, icm2; 00267 arm_navigation_msgs::CollisionMap::_boxes_type::iterator end1( map1->boxes.end()); 00268 00269 long num( 0 ); 00270 for( icm1 = map1->boxes.begin(), icm2 = map2->boxes.begin(); icm1 != end1; ++icm1, ++icm2 ) 00271 { 00272 if( isGreat( icm1->center.x - icm2->center.x ) || 00273 isGreat( icm1->center.y - icm2->center.y ) || 00274 isGreat( icm1->center.z - icm2->center.z ) || 00275 isGreat( icm1->extents.x - icm2->extents.x ) ) 00276 { 00277 // std::cerr << "Point number " << num << " different." << std::endl; 00278 return false; 00279 } 00280 ++num; 00281 } 00282 00283 return true; 00284 } 00285 00286 00287 00295 bool srs_env_model::CCMapPlugin::isNearRobot( const btVector3 & point, double extent ) 00296 { 00297 btScalar s( point.distance( m_robotBasePosition ) ); 00298 00299 return s - extent < m_collisionMapLimitRadius; 00300 } 00301 00308 bool srs_env_model::CCMapPlugin::getCollisionMapSrvCallback( srs_env_model::GetCollisionMap::Request & req, srs_env_model::GetCollisionMap::Response & res ) 00309 { 00310 00311 PERROR( "Get collision map service called" ); 00312 00313 // Response map version should be current version number 00314 res.current_version = m_collisionMapVersion; 00315 00316 // Get callers map version and compare to the current version number 00317 if( req.my_version != m_collisionMapVersion ) 00318 { 00319 res.map = *m_data; 00320 }else{ 00321 // No new data needed 00322 res.map = m_dataEmpty; 00323 } 00324 00325 return true; 00326 } 00327 00331 bool srs_env_model::CCMapPlugin::shouldPublish( ) 00332 { 00333 return(m_publishCollisionMap && (m_latchedTopics || m_cmapPublisher.getNumSubscribers() > 0)); 00334 } 00335 00341 bool srs_env_model::CCMapPlugin::isNewCmapSrvCallback( srs_env_model::IsNewCollisionMap::Request & req, srs_env_model::IsNewCollisionMap::Response & res ) 00342 { 00343 PERROR( "Is new cmap service called "); 00344 00345 res.is_newer = req.my_time < m_mapTime; 00346 res.current_time = m_mapTime; 00347 00348 return true; 00349 } 00350 00356 bool srs_env_model::CCMapPlugin::lockCmapSrvCallback( srs_env_model::LockCollisionMap::Request & req, srs_env_model::LockCollisionMap::Response & res ) 00357 { 00358 boost::mutex::scoped_lock lock( m_lockData ); 00359 00360 bool locked( req.lock != 0 ); 00361 00362 m_bLocked = locked; 00363 /* 00364 if( locked ) 00365 std::cerr << "Locking called. Lock set." << std::endl; 00366 else 00367 std::cerr << "Locking called. Lock removed." << std::endl; 00368 */ 00369 return true; 00370 } 00371 00375 void srs_env_model::CCMapPlugin::pause( bool bPause, ros::NodeHandle & node_handle ) 00376 { 00377 if( bPause ) 00378 m_cmapPublisher.shutdown(); 00379 else 00380 m_cmapPublisher = node_handle.advertise<arm_navigation_msgs::CollisionMap> ( m_cmapPublisherName, 5, m_latchedTopics); 00381 } 00382 00389 long srs_env_model::CCMapPlugin::removeInsideBox( const tBoxPoint & center, const tBoxPoint & size, tBoxVec & boxes ) 00390 { 00391 boost::mutex::scoped_lock lock( m_lockData ); 00392 00393 // Create data copy 00394 tBoxVec buffer( boxes.begin(), boxes.end() ); 00395 00396 // Clear output 00397 boxes.clear(); 00398 00399 // Compute testing box extents 00400 tBoxPoint extents; 00401 extents.x = size.x / 2.0; extents.y = size.y / 2.0; extents.z = size.z / 2.0; 00402 00403 long counter(0); 00404 00405 // Add excluded boxes to the data 00406 tBoxVec::iterator it, itEnd( buffer.end() ); 00407 for( it = buffer.begin(); it != itEnd; ++it ) 00408 { 00409 if( abs( it->center.x - center.x ) > (it->extents.x + extents.x ) || 00410 abs( it->center.y - center.y ) > (it->extents.y + extents.y ) || 00411 abs( it->center.z - center.z ) > (it->extents.z + extents.z ) ) 00412 { 00413 boxes.push_back( *it ); 00414 ++counter; 00415 } 00416 00417 } 00418 00419 // Return number of removed boxes 00420 return buffer.size() - counter; 00421 } 00422 00423 #define POINT_ADD( p0, p1, p2 ) {p0.x = p1.x + p2.x; p0.y = p1.y + p2.y; p0.z = p1.z + p2.z;} 00424 #define POINT_SUB( p0, p1, p2 ) {p0.x = p1.x - p2.x; p0.y = p1.y - p2.y; p0.z = p1.z - p2.z;} 00425 #define POINT_ABS( p0, p1 ) {p0.x = abs(p1.x); p0.y = abs(p1.y); p0.z = abs(p1.z); } 00426 00432 bool srs_env_model::CCMapPlugin::removeBoxCallback( srs_env_model::RemoveCube::Request & req, srs_env_model::RemoveCube::Response & res ) 00433 { 00434 // Test frame id 00435 if (req.frame_id != m_cmapFrameId) 00436 { 00437 // Transform size 00438 geometry_msgs::PointStamped vs, vsout; 00439 vs.header.frame_id = req.frame_id; 00440 vs.header.stamp = m_mapTime; 00441 POINT_ADD( vs.point, req.size, req.pose.position ); 00442 m_tfListener.transformPoint(m_cmapFrameId, vs, vsout); 00443 00444 // Transform pose 00445 geometry_msgs::PoseStamped ps, psout; 00446 ps.header.frame_id = req.frame_id; 00447 ps.header.stamp = m_mapTime; 00448 ps.pose = req.pose; 00449 00450 m_tfListener.transformPose(m_cmapFrameId, ps, psout); 00451 req.pose = psout.pose; 00452 00453 // Finalize size transform 00454 POINT_SUB(req.size, vsout.point, psout.pose.position); 00455 POINT_ABS(req.size, req.size); 00456 00457 } 00458 00459 tBoxPoint center, size; 00460 00461 // Convert point type 00462 center.x = req.pose.position.x; center.y = req.pose.position.y; center.z = req.pose.position.z; 00463 size.x = req.size.x; size.y = req.size.y; size.z = req.size.z; 00464 00465 // Remove boxes 00466 long count = 00467 removeInsideBox( center, size, m_data->boxes ); 00468 00469 std::cerr << "Removed " << count << " boxes..." << std::endl; 00470 00471 invalidate(); 00472 00473 return true; 00474 } 00475 00481 void srs_env_model::CCMapPlugin::addBox( const tBoxPoint & center, const tBoxPoint & size, tBoxVec & boxes ) 00482 { 00483 boost::mutex::scoped_lock lock( m_lockData ); 00484 tBox box; 00485 00486 // PERROR( "Adding box: " << std::endl << center << std::endl << size << std::endl ) 00487 00488 box.extents.x = size.x / 2.0; 00489 box.extents.y = size.y / 2.0; 00490 box.extents.z = size.z / 2.0; 00491 box.axis.x = box.axis.y = 0.0; 00492 box.axis.z = 1.0; 00493 box.angle = 0.0; 00494 box.center.x = center.x; 00495 box.center.y = center.y; 00496 box.center.z = center.z; 00497 00498 boxes.push_back(box); 00499 } 00500 00506 bool srs_env_model::CCMapPlugin::addBoxCallback( srs_env_model::RemoveCube::Request & req, srs_env_model::RemoveCube::Response & res ) 00507 { 00508 // Test frame id 00509 if (req.frame_id != m_cmapFrameId) 00510 { 00511 // Transform size 00512 geometry_msgs::PointStamped vs, vsout; 00513 vs.header.frame_id = req.frame_id; 00514 vs.header.stamp = m_mapTime; 00515 POINT_ADD( vs.point, req.size, req.pose.position ); 00516 m_tfListener.transformPoint(m_cmapFrameId, vs, vsout); 00517 00518 // Transform pose 00519 geometry_msgs::PoseStamped ps, psout; 00520 ps.header.frame_id = req.frame_id; 00521 ps.header.stamp = m_mapTime; 00522 ps.pose = req.pose; 00523 00524 m_tfListener.transformPose(m_cmapFrameId, ps, psout); 00525 req.pose = psout.pose; 00526 00527 // Finalize size transform 00528 POINT_SUB(req.size, vsout.point, psout.pose.position); 00529 POINT_ABS(req.size, req.size); 00530 00531 } 00532 00533 00534 tBoxPoint center, size; 00535 00536 // Convert point type 00537 center.x = req.pose.position.x; center.y = req.pose.position.y; center.z = req.pose.position.z; 00538 size.x = req.size.x; size.y = req.size.y; size.z = req.size.z; 00539 00540 // Add box 00541 // std::cerr << "Adding box. " << m_data->boxes.size() << " -> "; 00542 addBox( center, size, m_data->boxes ); 00543 // std::cerr << m_data->boxes.size() << std::endl; 00544 00545 invalidate(); 00546 00547 return true; 00548 } 00549 00554 void srs_env_model::CCMapPlugin::retransformTF( const ros::Time & currentTime ) 00555 { 00556 // Listen and store current collision map transform matrix 00557 tf::StampedTransform lockedToCurrentTF; 00558 00559 try 00560 { 00561 m_tfListener.waitForTransform( m_cmapFrameId, currentTime, m_cmapFrameId, m_mapTime, FIXED_FRAME, ros::Duration(5) ); 00562 m_tfListener.lookupTransform( m_cmapFrameId, currentTime, m_cmapFrameId, m_mapTime, FIXED_FRAME, lockedToCurrentTF ); 00563 } 00564 catch (tf::TransformException& ex) { 00565 ROS_ERROR_STREAM("Transform error: " << ex.what() << ", quitting callback"); 00566 PERROR( "Transform error."); 00567 return; 00568 } 00569 00570 Eigen::Matrix4f m; 00571 00572 // Get transform matrix 00573 pcl_ros::transformAsMatrix( lockedToCurrentTF, m ); 00574 00575 // Transform data front buffer 00576 tBoxVec::iterator it, itEnd( m_data->boxes.end() ); 00577 for( it = m_data->boxes.begin(); it != itEnd; ++it ) 00578 { 00579 // Copy values to the eigen vectors 00580 Eigen::Vector4f position( it->center.x, it->center.y, it->center.z, 1.0f ); 00581 Eigen::Vector4f extents( it->extents.x, it->extents.y, it->extents.z, 1.0f ); 00582 extents += position; 00583 00584 // Retransform 00585 position = m * position; 00586 extents = m * extents; 00587 00588 // Copy values back to the box 00589 it->center.x = position[0]; 00590 it->center.y = position[1]; 00591 it->center.z = position[2]; 00592 00593 // Recompute extents 00594 extents = extents - position; 00595 it->extents.x = abs(extents[0]); 00596 it->extents.y = abs(extents[1]); 00597 it->extents.z = abs(extents[2]); 00598 } 00599 00600 // Transform back buffer 00601 itEnd = m_dataBuffer->boxes.end(); 00602 for( it = m_dataBuffer->boxes.begin(); it != itEnd; ++it ) 00603 { 00604 // Copy values to the eigen vectors 00605 Eigen::Vector4f position( it->center.x, it->center.y, it->center.z, 1.0f ); 00606 Eigen::Vector4f extents( it->extents.x, it->extents.y, it->extents.z, 1.0f ); 00607 extents += position; 00608 00609 // Retransform 00610 position = m * position; 00611 extents = m * extents; 00612 00613 // Copy values back to the box 00614 it->center.x = position[0]; 00615 it->center.y = position[1]; 00616 it->center.z = position[2]; 00617 00618 // Recompute extents 00619 extents -= position; 00620 it->extents.x = abs(extents[0]); 00621 it->extents.y = abs(extents[1]); 00622 it->extents.z = abs(extents[2]); 00623 } 00624 00625 // Store timestamp 00626 m_mapTime = currentTime; 00627 } 00628 00629 00630 00631 00632