00001
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include <octomap_server/OctomapServer.h>
00039
00040 using namespace octomap;
00041 using octomap_msgs::Octomap;
00042
00043 namespace octomap_server{
00044
00045 OctomapServer::OctomapServer(ros::NodeHandle private_nh_)
00046 : m_nh(),
00047 m_pointCloudSub(NULL),
00048 m_tfPointCloudSub(NULL),
00049 m_octree(NULL),
00050 m_maxRange(-1.0),
00051 m_worldFrameId("/map"), m_baseFrameId("base_footprint"),
00052 m_useHeightMap(true),
00053 m_colorFactor(0.8),
00054 m_latchedTopics(true),
00055 m_publishFreeSpace(false),
00056 m_res(0.05),
00057 m_treeDepth(0),
00058 m_maxTreeDepth(0),
00059 m_probHit(0.7), m_probMiss(0.4),
00060 m_thresMin(0.12), m_thresMax(0.97),
00061 m_pointcloudMinZ(-std::numeric_limits<double>::max()),
00062 m_pointcloudMaxZ(std::numeric_limits<double>::max()),
00063 m_occupancyMinZ(-std::numeric_limits<double>::max()),
00064 m_occupancyMaxZ(std::numeric_limits<double>::max()),
00065 m_minSizeX(0.0), m_minSizeY(0.0),
00066 m_filterSpeckles(false), m_filterGroundPlane(false),
00067 m_groundFilterDistance(0.04), m_groundFilterAngle(0.15), m_groundFilterPlaneDistance(0.07),
00068 m_compressMap(true),
00069 m_incrementalUpdate(false)
00070 {
00071 ros::NodeHandle private_nh(private_nh_);
00072 private_nh.param("frame_id", m_worldFrameId, m_worldFrameId);
00073 private_nh.param("base_frame_id", m_baseFrameId, m_baseFrameId);
00074 private_nh.param("height_map", m_useHeightMap, m_useHeightMap);
00075 private_nh.param("color_factor", m_colorFactor, m_colorFactor);
00076
00077 private_nh.param("pointcloud_min_z", m_pointcloudMinZ,m_pointcloudMinZ);
00078 private_nh.param("pointcloud_max_z", m_pointcloudMaxZ,m_pointcloudMaxZ);
00079 private_nh.param("occupancy_min_z", m_occupancyMinZ,m_occupancyMinZ);
00080 private_nh.param("occupancy_max_z", m_occupancyMaxZ,m_occupancyMaxZ);
00081 private_nh.param("min_x_size", m_minSizeX,m_minSizeX);
00082 private_nh.param("min_y_size", m_minSizeY,m_minSizeY);
00083
00084 private_nh.param("filter_speckles", m_filterSpeckles, m_filterSpeckles);
00085 private_nh.param("filter_ground", m_filterGroundPlane, m_filterGroundPlane);
00086
00087 private_nh.param("ground_filter/distance", m_groundFilterDistance, m_groundFilterDistance);
00088
00089 private_nh.param("ground_filter/angle", m_groundFilterAngle, m_groundFilterAngle);
00090
00091 private_nh.param("ground_filter/plane_distance", m_groundFilterPlaneDistance, m_groundFilterPlaneDistance);
00092
00093 private_nh.param("sensor_model/max_range", m_maxRange, m_maxRange);
00094
00095 private_nh.param("resolution", m_res, m_res);
00096 private_nh.param("sensor_model/hit", m_probHit, m_probHit);
00097 private_nh.param("sensor_model/miss", m_probMiss, m_probMiss);
00098 private_nh.param("sensor_model/min", m_thresMin, m_thresMin);
00099 private_nh.param("sensor_model/max", m_thresMax, m_thresMax);
00100 private_nh.param("compress_map", m_compressMap, m_compressMap);
00101 private_nh.param("incremental_2D_projection", m_incrementalUpdate, m_incrementalUpdate);
00102
00103 if (m_filterGroundPlane && (m_pointcloudMinZ > 0.0 || m_pointcloudMaxZ < 0.0)){
00104 ROS_WARN_STREAM("You enabled ground filtering but incoming pointclouds will be pre-filtered in ["
00105 <<m_pointcloudMinZ <<", "<< m_pointcloudMaxZ << "], excluding the ground level z=0. "
00106 << "This will not work.");
00107
00108 }
00109
00110
00111
00112
00113 m_octree = new OcTree(m_res);
00114 m_octree->setProbHit(m_probHit);
00115 m_octree->setProbMiss(m_probMiss);
00116 m_octree->setClampingThresMin(m_thresMin);
00117 m_octree->setClampingThresMax(m_thresMax);
00118 m_treeDepth = m_octree->getTreeDepth();
00119 m_maxTreeDepth = m_treeDepth;
00120 m_gridmap.info.resolution = m_res;
00121
00122 double r, g, b, a;
00123 private_nh.param("color/r", r, 0.0);
00124 private_nh.param("color/g", g, 0.0);
00125 private_nh.param("color/b", b, 1.0);
00126 private_nh.param("color/a", a, 1.0);
00127 m_color.r = r;
00128 m_color.g = g;
00129 m_color.b = b;
00130 m_color.a = a;
00131
00132 private_nh.param("color_free/r", r, 0.0);
00133 private_nh.param("color_free/g", g, 1.0);
00134 private_nh.param("color_free/b", b, 0.0);
00135 private_nh.param("color_free/a", a, 1.0);
00136 m_colorFree.r = r;
00137 m_colorFree.g = g;
00138 m_colorFree.b = b;
00139 m_colorFree.a = a;
00140
00141 private_nh.param("publish_free_space", m_publishFreeSpace, m_publishFreeSpace);
00142
00143 private_nh.param("latch", m_latchedTopics, m_latchedTopics);
00144 if (m_latchedTopics){
00145 ROS_INFO("Publishing latched (single publish will take longer, all topics are prepared)");
00146 } else
00147 ROS_INFO("Publishing non-latched (topics are only prepared as needed, will only be re-published on map change");
00148
00149 m_markerPub = m_nh.advertise<visualization_msgs::MarkerArray>("occupied_cells_vis_array", 1, m_latchedTopics);
00150 m_binaryMapPub = m_nh.advertise<Octomap>("octomap_binary", 1, m_latchedTopics);
00151 m_fullMapPub = m_nh.advertise<Octomap>("octomap_full", 1, m_latchedTopics);
00152 m_pointCloudPub = m_nh.advertise<sensor_msgs::PointCloud2>("octomap_point_cloud_centers", 1, m_latchedTopics);
00153 m_collisionObjectPub = m_nh.advertise<arm_navigation_msgs::CollisionObject>("octomap_collision_object", 1, m_latchedTopics);
00154 m_mapPub = m_nh.advertise<nav_msgs::OccupancyGrid>("projected_map", 5, m_latchedTopics);
00155 m_cmapPub = m_nh.advertise<arm_navigation_msgs::CollisionMap>("collision_map_out", 1, m_latchedTopics);
00156 m_fmapPub = m_nh.advertise<arm_navigation_msgs::CollisionMap>("free_map_out", 1, m_latchedTopics);
00157 m_fmarkerPub = m_nh.advertise<visualization_msgs::MarkerArray>("free_cells_vis_array", 1, m_latchedTopics);
00158
00159 m_pointCloudSub = new message_filters::Subscriber<sensor_msgs::PointCloud2> (m_nh, "cloud_in", 5);
00160 m_tfPointCloudSub = new tf::MessageFilter<sensor_msgs::PointCloud2> (*m_pointCloudSub, m_tfListener, m_worldFrameId, 5);
00161 m_tfPointCloudSub->registerCallback(boost::bind(&OctomapServer::insertCloudCallback, this, _1));
00162
00163 m_octomapBinaryService = m_nh.advertiseService("octomap_binary", &OctomapServer::octomapBinarySrv, this);
00164 m_octomapFullService = m_nh.advertiseService("octomap_full", &OctomapServer::octomapFullSrv, this);
00165 m_clearBBXService = private_nh.advertiseService("clear_bbx", &OctomapServer::clearBBXSrv, this);
00166 m_resetService = private_nh.advertiseService("reset", &OctomapServer::resetSrv, this);
00167
00168 dynamic_reconfigure::Server<OctomapServerConfig>::CallbackType f;
00169
00170 f = boost::bind(&OctomapServer::reconfigureCallback, this, _1, _2);
00171 m_reconfigureServer.setCallback(f);
00172 }
00173
00174 OctomapServer::~OctomapServer(){
00175 if (m_tfPointCloudSub){
00176 delete m_tfPointCloudSub;
00177 m_tfPointCloudSub = NULL;
00178 }
00179
00180 if (m_pointCloudSub){
00181 delete m_pointCloudSub;
00182 m_pointCloudSub = NULL;
00183 }
00184
00185
00186 if (m_octree){
00187 delete m_octree;
00188 m_octree = NULL;
00189 }
00190
00191 }
00192
00193 bool OctomapServer::openFile(const std::string& filename){
00194 if (filename.length() <= 3)
00195 return false;
00196
00197 std::string suffix = filename.substr(filename.length()-3, 3);
00198 if (suffix== ".bt"){
00199 if (!m_octree->readBinary(filename)){
00200 return false;
00201 }
00202 } else if (suffix == ".ot"){
00203 AbstractOcTree* tree = AbstractOcTree::read(filename);
00204 if (!tree){
00205 return false;
00206 }
00207 if (m_octree){
00208 delete m_octree;
00209 m_octree = NULL;
00210 }
00211 m_octree = dynamic_cast<OcTree*>(tree);
00212 if (!m_octree){
00213 ROS_ERROR("Could not read OcTree in file, currently there are no other types supported in .ot");
00214 return false;
00215 }
00216
00217 } else{
00218 return false;
00219 }
00220
00221 ROS_INFO("Octomap file %s loaded (%zu nodes).", filename.c_str(),m_octree->size());
00222
00223 m_treeDepth = m_octree->getTreeDepth();
00224 m_maxTreeDepth = m_treeDepth;
00225 m_res = m_octree->getResolution();
00226 m_gridmap.info.resolution = m_res;
00227 double minX, minY, minZ;
00228 double maxX, maxY, maxZ;
00229 m_octree->getMetricMin(minX, minY, minZ);
00230 m_octree->getMetricMax(maxX, maxY, maxZ);
00231
00232 m_updateBBXMin[0] = m_octree->coordToKey(minX);
00233 m_updateBBXMin[1] = m_octree->coordToKey(minY);
00234 m_updateBBXMin[2] = m_octree->coordToKey(minZ);
00235
00236 m_updateBBXMax[0] = m_octree->coordToKey(maxX);
00237 m_updateBBXMax[1] = m_octree->coordToKey(maxY);
00238 m_updateBBXMax[2] = m_octree->coordToKey(maxZ);
00239
00240 publishAll();
00241
00242 return true;
00243
00244 }
00245
00246 void OctomapServer::insertCloudCallback(const sensor_msgs::PointCloud2::ConstPtr& cloud){
00247 ros::WallTime startTime = ros::WallTime::now();
00248
00249
00250
00251
00252
00253 PCLPointCloud pc;
00254 pcl::fromROSMsg(*cloud, pc);
00255
00256 tf::StampedTransform sensorToWorldTf;
00257 try {
00258 m_tfListener.lookupTransform(m_worldFrameId, cloud->header.frame_id, cloud->header.stamp, sensorToWorldTf);
00259 } catch(tf::TransformException& ex){
00260 ROS_ERROR_STREAM( "Transform error of sensor data: " << ex.what() << ", quitting callback");
00261 return;
00262 }
00263
00264 Eigen::Matrix4f sensorToWorld;
00265 pcl_ros::transformAsMatrix(sensorToWorldTf, sensorToWorld);
00266
00267
00268
00269 pcl::PassThrough<pcl::PointXYZ> pass;
00270 pass.setFilterFieldName("z");
00271 pass.setFilterLimits(m_pointcloudMinZ, m_pointcloudMaxZ);
00272
00273 PCLPointCloud pc_ground;
00274 PCLPointCloud pc_nonground;
00275
00276 if (m_filterGroundPlane){
00277 tf::StampedTransform sensorToBaseTf, baseToWorldTf;
00278 try{
00279 m_tfListener.waitForTransform(m_baseFrameId, cloud->header.frame_id, cloud->header.stamp, ros::Duration(0.2));
00280 m_tfListener.lookupTransform(m_baseFrameId, cloud->header.frame_id, cloud->header.stamp, sensorToBaseTf);
00281 m_tfListener.lookupTransform(m_worldFrameId, m_baseFrameId, cloud->header.stamp, baseToWorldTf);
00282
00283
00284 }catch(tf::TransformException& ex){
00285 ROS_ERROR_STREAM( "Transform error for ground plane filter: " << ex.what() << ", quitting callback.\n"
00286 "You need to set the base_frame_id or disable filter_ground.");
00287 }
00288
00289
00290 Eigen::Matrix4f sensorToBase, baseToWorld;
00291 pcl_ros::transformAsMatrix(sensorToBaseTf, sensorToBase);
00292 pcl_ros::transformAsMatrix(baseToWorldTf, baseToWorld);
00293
00294
00295 pcl::transformPointCloud(pc, pc, sensorToBase);
00296 pass.setInputCloud(pc.makeShared());
00297 pass.filter(pc);
00298 filterGroundPlane(pc, pc_ground, pc_nonground);
00299
00300
00301 pcl::transformPointCloud(pc_ground, pc_ground, baseToWorld);
00302 pcl::transformPointCloud(pc_nonground, pc_nonground, baseToWorld);
00303 } else {
00304
00305 pcl::transformPointCloud(pc, pc, sensorToWorld);
00306
00307
00308 pass.setInputCloud(pc.makeShared());
00309 pass.filter(pc);
00310
00311 pc_nonground = pc;
00312
00313 pc_ground.header = pc.header;
00314 pc_nonground.header = pc.header;
00315 }
00316
00317
00318 insertScan(sensorToWorldTf.getOrigin(), pc_ground, pc_nonground);
00319
00320 double total_elapsed = (ros::WallTime::now() - startTime).toSec();
00321 ROS_DEBUG("Pointcloud insertion in OctomapServer done (%zu+%zu pts (ground/nonground), %f sec)", pc_ground.size(), pc_nonground.size(), total_elapsed);
00322
00323 publishAll(cloud->header.stamp);
00324 }
00325
00326 void OctomapServer::insertScan(const tf::Point& sensorOriginTf, const PCLPointCloud& ground, const PCLPointCloud& nonground){
00327 point3d sensorOrigin = pointTfToOctomap(sensorOriginTf);
00328
00329 if (!m_octree->coordToKeyChecked(sensorOrigin, m_updateBBXMin)
00330 || !m_octree->coordToKeyChecked(sensorOrigin, m_updateBBXMax))
00331 {
00332 ROS_ERROR_STREAM("Could not generate Key for origin "<<sensorOrigin);
00333 }
00334
00335
00336
00337
00338 KeySet free_cells, occupied_cells;
00339
00340 for (PCLPointCloud::const_iterator it = ground.begin(); it != ground.end(); ++it){
00341 point3d point(it->x, it->y, it->z);
00342
00343 if ((m_maxRange > 0.0) && ((point - sensorOrigin).norm() > m_maxRange) ) {
00344 point = sensorOrigin + (point - sensorOrigin).normalized() * m_maxRange;
00345 }
00346
00347
00348 if (m_octree->computeRayKeys(sensorOrigin, point, m_keyRay)){
00349 free_cells.insert(m_keyRay.begin(), m_keyRay.end());
00350 }
00351
00352 octomap::OcTreeKey endKey;
00353 if (m_octree->coordToKeyChecked(point, endKey)){
00354 updateMinKey(endKey, m_updateBBXMin);
00355 updateMaxKey(endKey, m_updateBBXMax);
00356 } else{
00357 ROS_ERROR_STREAM("Could not generate Key for endpoint "<<point);
00358 }
00359 }
00360
00361
00362 for (PCLPointCloud::const_iterator it = nonground.begin(); it != nonground.end(); ++it){
00363 point3d point(it->x, it->y, it->z);
00364
00365 if ((m_maxRange < 0.0) || ((point - sensorOrigin).norm() <= m_maxRange) ) {
00366
00367
00368 if (m_octree->computeRayKeys(sensorOrigin, point, m_keyRay)){
00369 free_cells.insert(m_keyRay.begin(), m_keyRay.end());
00370 }
00371
00372 OcTreeKey key;
00373 if (m_octree->coordToKeyChecked(point, key)){
00374 occupied_cells.insert(key);
00375
00376 updateMinKey(key, m_updateBBXMin);
00377 updateMaxKey(key, m_updateBBXMax);
00378 }
00379 } else {
00380 point3d new_end = sensorOrigin + (point - sensorOrigin).normalized() * m_maxRange;
00381 if (m_octree->computeRayKeys(sensorOrigin, new_end, m_keyRay)){
00382 free_cells.insert(m_keyRay.begin(), m_keyRay.end());
00383
00384 octomap::OcTreeKey endKey;
00385 if (m_octree->coordToKeyChecked(new_end, endKey)){
00386 updateMinKey(endKey, m_updateBBXMin);
00387 updateMaxKey(endKey, m_updateBBXMax);
00388 } else{
00389 ROS_ERROR_STREAM("Could not generate Key for endpoint "<<new_end);
00390 }
00391
00392
00393 }
00394 }
00395 }
00396
00397
00398 for(KeySet::iterator it = free_cells.begin(), end=free_cells.end(); it!= end; ++it){
00399 if (occupied_cells.find(*it) == occupied_cells.end()){
00400 m_octree->updateNode(*it, false);
00401 }
00402 }
00403
00404
00405 for (KeySet::iterator it = occupied_cells.begin(), end=free_cells.end(); it!= end; it++) {
00406 m_octree->updateNode(*it, true);
00407 }
00408
00409
00410
00411
00412 octomap::point3d minPt, maxPt;
00413 ROS_DEBUG_STREAM("Bounding box keys (before): " << m_updateBBXMin[0] << " " <<m_updateBBXMin[1] << " " << m_updateBBXMin[2] << " / " <<m_updateBBXMax[0] << " "<<m_updateBBXMax[1] << " "<< m_updateBBXMax[2]);
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428 minPt = m_octree->keyToCoord(m_updateBBXMin);
00429 maxPt = m_octree->keyToCoord(m_updateBBXMax);
00430 ROS_DEBUG_STREAM("Updated area bounding box: "<< minPt << " - "<<maxPt);
00431 ROS_DEBUG_STREAM("Bounding box keys (after): " << m_updateBBXMin[0] << " " <<m_updateBBXMin[1] << " " << m_updateBBXMin[2] << " / " <<m_updateBBXMax[0] << " "<<m_updateBBXMax[1] << " "<< m_updateBBXMax[2]);
00432
00433 if (m_compressMap)
00434 m_octree->prune();
00435
00436
00437 }
00438
00439
00440
00441 void OctomapServer::publishAll(const ros::Time& rostime){
00442 ros::WallTime startTime = ros::WallTime::now();
00443 size_t octomapSize = m_octree->size();
00444
00445 if (octomapSize <= 1){
00446 ROS_WARN("Nothing to publish, octree is empty");
00447 return;
00448 }
00449
00450 bool publishFreeMap = m_publishFreeSpace && (m_latchedTopics || m_fmapPub.getNumSubscribers() > 0);
00451 bool publishFreeMarkerArray = m_publishFreeSpace && (m_latchedTopics || m_fmarkerPub.getNumSubscribers() > 0);
00452 bool publishCollisionMap = (m_latchedTopics || m_cmapPub.getNumSubscribers() > 0);
00453 bool publishCollisionObject = (m_latchedTopics || m_collisionObjectPub.getNumSubscribers() > 0);
00454 bool publishMarkerArray = (m_latchedTopics || m_markerPub.getNumSubscribers() > 0);
00455 bool publishPointCloud = (m_latchedTopics || m_pointCloudPub.getNumSubscribers() > 0);
00456 bool publishBinaryMap = (m_latchedTopics || m_binaryMapPub.getNumSubscribers() > 0);
00457 bool publishFullMap = (m_latchedTopics || m_fullMapPub.getNumSubscribers() > 0);
00458 m_publish2DMap = (m_latchedTopics || m_mapPub.getNumSubscribers() > 0);
00459
00460
00461 arm_navigation_msgs::CollisionObject collisionObject;
00462 collisionObject.header.frame_id = m_worldFrameId;
00463 collisionObject.header.stamp = rostime;
00464 collisionObject.id = "map";
00465 arm_navigation_msgs::OrientedBoundingBox collObjBox;
00466 collObjBox.axis.x = collObjBox.axis.y = 0.0;
00467 collObjBox.axis.z = 1.0;
00468 collObjBox.angle = 0.0;
00469
00470
00471 arm_navigation_msgs::CollisionMap freeMap;
00472 freeMap.header.frame_id = m_worldFrameId;
00473 freeMap.header.stamp = rostime;
00474 arm_navigation_msgs::OrientedBoundingBox freeObjBox;
00475 freeObjBox.axis.x = freeObjBox.axis.y = 0.0;
00476 freeObjBox.axis.z = 1.0;
00477 freeObjBox.angle = 0.0;
00478
00479
00480 visualization_msgs::MarkerArray freeNodesVis;
00481
00482 freeNodesVis.markers.resize(m_treeDepth+1);
00483
00484
00485
00486
00487 arm_navigation_msgs::CollisionMap collisionMap;
00488 collisionMap.header.frame_id = m_worldFrameId;
00489 collisionMap.header.stamp = rostime;
00490 arm_navigation_msgs::Shape collObjShape;
00491 collObjShape.type = arm_navigation_msgs::Shape::BOX;
00492 collObjShape.dimensions.resize(3);
00493
00494 geometry_msgs::Pose pose;
00495 pose.orientation = tf::createQuaternionMsgFromYaw(0.0);
00496
00497
00498 visualization_msgs::MarkerArray occupiedNodesVis;
00499
00500 occupiedNodesVis.markers.resize(m_treeDepth+1);
00501
00502
00503 pcl::PointCloud<pcl::PointXYZ> pclCloud;
00504
00505
00506 handlePreNodeTraversal(rostime);
00507
00508
00509 for (OcTree::iterator it = m_octree->begin(m_maxTreeDepth),
00510 end = m_octree->end(); it != end; ++it)
00511 {
00512 bool inUpdateBBX = isInUpdateBBX(it);
00513
00514
00515 handleNode(it);
00516 if (inUpdateBBX)
00517 handleNodeInBBX(it);
00518
00519 if (m_octree->isNodeOccupied(*it)){
00520 double z = it.getZ();
00521 if (z > m_occupancyMinZ && z < m_occupancyMaxZ)
00522 {
00523 double size = it.getSize();
00524 double x = it.getX();
00525 double y = it.getY();
00526
00527
00528 if (m_filterSpeckles && (it.getDepth() == m_treeDepth +1) && isSpeckleNode(it.getKey())){
00529 ROS_DEBUG("Ignoring single speckle at (%f,%f,%f)", x, y, z);
00530 continue;
00531 }
00532
00533 handleOccupiedNode(it);
00534 if (inUpdateBBX)
00535 handleOccupiedNodeInBBX(it);
00536
00537
00538 if (publishCollisionObject){
00539 collObjShape.dimensions[0] = collObjShape.dimensions[1] = collObjShape.dimensions[2] = size;
00540 collisionObject.shapes.push_back(collObjShape);
00541 pose.position.x = x;
00542 pose.position.y = y;
00543 pose.position.z = z;
00544 collisionObject.poses.push_back(pose);
00545 }
00546
00547 if (publishCollisionMap){
00548 collObjBox.extents.x = collObjBox.extents.y = collObjBox.extents.z = size;
00549
00550 collObjBox.center.x = x;
00551 collObjBox.center.y = y;
00552 collObjBox.center.z = z;
00553 collisionMap.boxes.push_back(collObjBox);
00554
00555 }
00556
00557
00558 if (publishMarkerArray){
00559 unsigned idx = it.getDepth();
00560 assert(idx < occupiedNodesVis.markers.size());
00561
00562 geometry_msgs::Point cubeCenter;
00563 cubeCenter.x = x;
00564 cubeCenter.y = y;
00565 cubeCenter.z = z;
00566
00567 occupiedNodesVis.markers[idx].points.push_back(cubeCenter);
00568 if (m_useHeightMap){
00569 double minX, minY, minZ, maxX, maxY, maxZ;
00570 m_octree->getMetricMin(minX, minY, minZ);
00571 m_octree->getMetricMax(maxX, maxY, maxZ);
00572
00573 double h = (1.0 - std::min(std::max((cubeCenter.z-minZ)/ (maxZ - minZ), 0.0), 1.0)) *m_colorFactor;
00574 occupiedNodesVis.markers[idx].colors.push_back(heightMapColor(h));
00575 }
00576 }
00577
00578
00579 if (publishPointCloud)
00580 pclCloud.push_back(pcl::PointXYZ(x, y, z));
00581
00582 }
00583 } else{
00584 double z = it.getZ();
00585 if (z > m_occupancyMinZ && z < m_occupancyMaxZ)
00586 {
00587 handleFreeNode(it);
00588 if (inUpdateBBX)
00589 handleFreeNodeInBBX(it);
00590
00591 if (m_publishFreeSpace){
00592 double x = it.getX();
00593 double y = it.getY();
00594
00595 if (publishFreeMap){
00596 freeObjBox.extents.x = freeObjBox.extents.y = freeObjBox.extents.z = it.getSize();
00597 freeObjBox.center.x = x;
00598 freeObjBox.center.y = y;
00599 freeObjBox.center.z = z;
00600 freeMap.boxes.push_back(freeObjBox);
00601 }
00602
00603
00604 if (publishFreeMarkerArray){
00605 unsigned idx = it.getDepth();
00606 assert(idx < freeNodesVis.markers.size());
00607
00608 geometry_msgs::Point cubeCenter;
00609 cubeCenter.x = x;
00610 cubeCenter.y = y;
00611 cubeCenter.z = z;
00612
00613 freeNodesVis.markers[idx].points.push_back(cubeCenter);
00614 }
00615 }
00616
00617 }
00618 }
00619 }
00620
00621
00622 handlePostNodeTraversal(rostime);
00623
00624
00625 if (publishMarkerArray){
00626 for (unsigned i= 0; i < occupiedNodesVis.markers.size(); ++i){
00627 double size = m_octree->getNodeSize(i);
00628
00629 occupiedNodesVis.markers[i].header.frame_id = m_worldFrameId;
00630 occupiedNodesVis.markers[i].header.stamp = rostime;
00631 occupiedNodesVis.markers[i].ns = "map";
00632 occupiedNodesVis.markers[i].id = i;
00633 occupiedNodesVis.markers[i].type = visualization_msgs::Marker::CUBE_LIST;
00634 occupiedNodesVis.markers[i].scale.x = size;
00635 occupiedNodesVis.markers[i].scale.y = size;
00636 occupiedNodesVis.markers[i].scale.z = size;
00637 occupiedNodesVis.markers[i].color = m_color;
00638
00639
00640 if (occupiedNodesVis.markers[i].points.size() > 0)
00641 occupiedNodesVis.markers[i].action = visualization_msgs::Marker::ADD;
00642 else
00643 occupiedNodesVis.markers[i].action = visualization_msgs::Marker::DELETE;
00644 }
00645
00646 m_markerPub.publish(occupiedNodesVis);
00647 }
00648
00649
00650
00651 if (publishFreeMarkerArray){
00652 for (unsigned i= 0; i < freeNodesVis.markers.size(); ++i){
00653 double size = m_octree->getNodeSize(i);
00654
00655 freeNodesVis.markers[i].header.frame_id = m_worldFrameId;
00656 freeNodesVis.markers[i].header.stamp = rostime;
00657 freeNodesVis.markers[i].ns = "map";
00658 freeNodesVis.markers[i].id = i;
00659 freeNodesVis.markers[i].type = visualization_msgs::Marker::CUBE_LIST;
00660 freeNodesVis.markers[i].scale.x = size;
00661 freeNodesVis.markers[i].scale.y = size;
00662 freeNodesVis.markers[i].scale.z = size;
00663 freeNodesVis.markers[i].color = m_colorFree;
00664
00665
00666 if (freeNodesVis.markers[i].points.size() > 0)
00667 freeNodesVis.markers[i].action = visualization_msgs::Marker::ADD;
00668 else
00669 freeNodesVis.markers[i].action = visualization_msgs::Marker::DELETE;
00670 }
00671
00672 m_fmarkerPub.publish(freeNodesVis);
00673 }
00674
00675
00676
00677 if (publishPointCloud){
00678 sensor_msgs::PointCloud2 cloud;
00679 pcl::toROSMsg (pclCloud, cloud);
00680 cloud.header.frame_id = m_worldFrameId;
00681 cloud.header.stamp = rostime;
00682 m_pointCloudPub.publish(cloud);
00683 }
00684
00685 if (publishCollisionObject)
00686 m_collisionObjectPub.publish(collisionObject);
00687
00688 if (publishCollisionMap)
00689 m_cmapPub.publish(collisionMap);
00690
00691 if (publishBinaryMap)
00692 publishBinaryOctoMap(rostime);
00693
00694 if (publishFullMap)
00695 publishFullOctoMap(rostime);
00696
00697 if (publishFreeMap)
00698 m_fmapPub.publish(freeMap);
00699
00700
00701
00702 double total_elapsed = (ros::WallTime::now() - startTime).toSec();
00703 ROS_DEBUG("Map publishing in OctomapServer took %f sec", total_elapsed);
00704
00705 }
00706
00707
00708 bool OctomapServer::octomapBinarySrv(OctomapSrv::Request &req,
00709 OctomapSrv::Response &res)
00710 {
00711 ROS_INFO("Sending binary map data on service request");
00712 res.map.header.frame_id = m_worldFrameId;
00713 res.map.header.stamp = ros::Time::now();
00714 if (!octomap_msgs::binaryMapToMsg(*m_octree, res.map))
00715 return false;
00716
00717 return true;
00718 }
00719
00720 bool OctomapServer::octomapFullSrv(OctomapSrv::Request &req,
00721 OctomapSrv::Response &res)
00722 {
00723 ROS_INFO("Sending full map data on service request");
00724 res.map.header.frame_id = m_worldFrameId;
00725 res.map.header.stamp = ros::Time::now();
00726
00727
00728 if (!octomap_msgs::fullMapToMsg(*m_octree, res.map))
00729 return false;
00730
00731 return true;
00732 }
00733
00734 bool OctomapServer::clearBBXSrv(BBXSrv::Request& req, BBXSrv::Response& resp){
00735 point3d min = pointMsgToOctomap(req.min);
00736 point3d max = pointMsgToOctomap(req.max);
00737
00738 for(OcTree::leaf_bbx_iterator it = m_octree->begin_leafs_bbx(min,max),
00739 end=m_octree->end_leafs_bbx(); it!= end; ++it){
00740
00741 it->setLogOdds(octomap::logodds(m_thresMin));
00742
00743 }
00744
00745 m_octree->updateInnerOccupancy();
00746
00747 publishAll(ros::Time::now());
00748
00749 return true;
00750 }
00751
00752 bool OctomapServer::resetSrv(std_srvs::Empty::Request& req, std_srvs::Empty::Response& resp) {
00753 visualization_msgs::MarkerArray occupiedNodesVis;
00754 occupiedNodesVis.markers.resize(m_treeDepth +1);
00755 ros::Time rostime = ros::Time::now();
00756 m_octree->clear();
00757
00758 m_gridmap.data.clear();
00759 m_gridmap.info.height = 0.0;
00760 m_gridmap.info.width = 0.0;
00761 m_gridmap.info.resolution = 0.0;
00762 m_gridmap.info.origin.position.x = 0.0;
00763 m_gridmap.info.origin.position.y = 0.0;
00764
00765 ROS_INFO("Cleared octomap");
00766 publishAll(rostime);
00767
00768 publishBinaryOctoMap(rostime);
00769 for (unsigned i= 0; i < occupiedNodesVis.markers.size(); ++i){
00770
00771 occupiedNodesVis.markers[i].header.frame_id = m_worldFrameId;
00772 occupiedNodesVis.markers[i].header.stamp = rostime;
00773 occupiedNodesVis.markers[i].ns = "map";
00774 occupiedNodesVis.markers[i].id = i;
00775 occupiedNodesVis.markers[i].type = visualization_msgs::Marker::CUBE_LIST;
00776 occupiedNodesVis.markers[i].action = visualization_msgs::Marker::DELETE;
00777 }
00778
00779
00780 m_markerPub.publish(occupiedNodesVis);
00781
00782
00783 visualization_msgs::MarkerArray freeNodesVis;
00784 freeNodesVis.markers.resize(m_treeDepth +1);
00785
00786 for (unsigned i= 0; i < freeNodesVis.markers.size(); ++i){
00787
00788 freeNodesVis.markers[i].header.frame_id = m_worldFrameId;
00789 freeNodesVis.markers[i].header.stamp = rostime;
00790 freeNodesVis.markers[i].ns = "map";
00791 freeNodesVis.markers[i].id = i;
00792 freeNodesVis.markers[i].type = visualization_msgs::Marker::CUBE_LIST;
00793 freeNodesVis.markers[i].action = visualization_msgs::Marker::DELETE;
00794 }
00795 m_fmarkerPub.publish(freeNodesVis);
00796
00797 return true;
00798 }
00799
00800 void OctomapServer::publishBinaryOctoMap(const ros::Time& rostime) const{
00801
00802 Octomap map;
00803 map.header.frame_id = m_worldFrameId;
00804 map.header.stamp = rostime;
00805
00806 if (octomap_msgs::binaryMapToMsg(*m_octree, map))
00807 m_binaryMapPub.publish(map);
00808 else
00809 ROS_ERROR("Error serializing OctoMap");
00810 }
00811
00812 void OctomapServer::publishFullOctoMap(const ros::Time& rostime) const{
00813
00814 Octomap map;
00815 map.header.frame_id = m_worldFrameId;
00816 map.header.stamp = rostime;
00817
00818 if (octomap_msgs::fullMapToMsg(*m_octree, map))
00819 m_fullMapPub.publish(map);
00820 else
00821 ROS_ERROR("Error serializing OctoMap");
00822
00823 }
00824
00825
00826 void OctomapServer::filterGroundPlane(const PCLPointCloud& pc, PCLPointCloud& ground, PCLPointCloud& nonground) const{
00827 ground.header = pc.header;
00828 nonground.header = pc.header;
00829
00830 if (pc.size() < 50){
00831 ROS_WARN("Pointcloud in OctomapServer too small, skipping ground plane extraction");
00832 nonground = pc;
00833 } else {
00834
00835 pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
00836 pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
00837
00838
00839 pcl::SACSegmentation<pcl::PointXYZ> seg;
00840 seg.setOptimizeCoefficients (true);
00841
00842 seg.setModelType(pcl::SACMODEL_PERPENDICULAR_PLANE);
00843 seg.setMethodType(pcl::SAC_RANSAC);
00844 seg.setMaxIterations(200);
00845 seg.setDistanceThreshold (m_groundFilterDistance);
00846 seg.setAxis(Eigen::Vector3f(0,0,1));
00847 seg.setEpsAngle(m_groundFilterAngle);
00848
00849
00850 PCLPointCloud cloud_filtered(pc);
00851
00852 pcl::ExtractIndices<pcl::PointXYZ> extract;
00853 bool groundPlaneFound = false;
00854
00855 while(cloud_filtered.size() > 10 && !groundPlaneFound){
00856 seg.setInputCloud(cloud_filtered.makeShared());
00857 seg.segment (*inliers, *coefficients);
00858 if (inliers->indices.size () == 0){
00859 ROS_INFO("PCL segmentation did not find any plane.");
00860
00861 break;
00862 }
00863
00864 extract.setInputCloud(cloud_filtered.makeShared());
00865 extract.setIndices(inliers);
00866
00867 if (std::abs(coefficients->values.at(3)) < m_groundFilterPlaneDistance){
00868 ROS_DEBUG("Ground plane found: %zu/%zu inliers. Coeff: %f %f %f %f", inliers->indices.size(), cloud_filtered.size(),
00869 coefficients->values.at(0), coefficients->values.at(1), coefficients->values.at(2), coefficients->values.at(3));
00870 extract.setNegative (false);
00871 extract.filter (ground);
00872
00873
00874
00875 if(inliers->indices.size() != cloud_filtered.size()){
00876 extract.setNegative(true);
00877 PCLPointCloud cloud_out;
00878 extract.filter(cloud_out);
00879 nonground += cloud_out;
00880 cloud_filtered = cloud_out;
00881 }
00882
00883 groundPlaneFound = true;
00884 } else{
00885 ROS_DEBUG("Horizontal plane (not ground) found: %zu/%zu inliers. Coeff: %f %f %f %f", inliers->indices.size(), cloud_filtered.size(),
00886 coefficients->values.at(0), coefficients->values.at(1), coefficients->values.at(2), coefficients->values.at(3));
00887 pcl::PointCloud<pcl::PointXYZ> cloud_out;
00888 extract.setNegative (false);
00889 extract.filter(cloud_out);
00890 nonground +=cloud_out;
00891
00892
00893
00894
00895
00896
00897 if(inliers->indices.size() != cloud_filtered.size()){
00898 extract.setNegative(true);
00899 cloud_out.points.clear();
00900 extract.filter(cloud_out);
00901 cloud_filtered = cloud_out;
00902 } else{
00903 cloud_filtered.points.clear();
00904 }
00905 }
00906
00907 }
00908
00909 if (!groundPlaneFound){
00910 ROS_WARN("No ground plane found in scan");
00911
00912
00913 pcl::PassThrough<pcl::PointXYZ> second_pass;
00914 second_pass.setFilterFieldName("z");
00915 second_pass.setFilterLimits(-m_groundFilterPlaneDistance, m_groundFilterPlaneDistance);
00916 second_pass.setInputCloud(pc.makeShared());
00917 second_pass.filter(ground);
00918
00919 second_pass.setFilterLimitsNegative (true);
00920 second_pass.filter(nonground);
00921 }
00922
00923
00924
00925
00926
00927
00928
00929
00930 }
00931
00932
00933 }
00934
00935 void OctomapServer::handlePreNodeTraversal(const ros::Time& rostime){
00936 if (m_publish2DMap){
00937
00938 m_gridmap.header.frame_id = m_worldFrameId;
00939 m_gridmap.header.stamp = rostime;
00940 nav_msgs::MapMetaData oldMapInfo = m_gridmap.info;
00941
00942
00943 double minX, minY, minZ, maxX, maxY, maxZ;
00944 m_octree->getMetricMin(minX, minY, minZ);
00945 m_octree->getMetricMax(maxX, maxY, maxZ);
00946
00947 octomap::point3d minPt(minX, minY, minZ);
00948 octomap::point3d maxPt(maxX, maxY, maxZ);
00949 octomap::OcTreeKey minKey = m_octree->coordToKey(minPt, m_maxTreeDepth);
00950 octomap::OcTreeKey maxKey = m_octree->coordToKey(maxPt, m_maxTreeDepth);
00951
00952 ROS_DEBUG("MinKey: %d %d %d / MaxKey: %d %d %d", minKey[0], minKey[1], minKey[2], maxKey[0], maxKey[1], maxKey[2]);
00953
00954
00955 double halfPaddedX = 0.5*m_minSizeX;
00956 double halfPaddedY = 0.5*m_minSizeY;
00957 minX = std::min(minX, -halfPaddedX);
00958 maxX = std::max(maxX, halfPaddedX);
00959 minY = std::min(minY, -halfPaddedY);
00960 maxY = std::max(maxY, halfPaddedY);
00961 minPt = octomap::point3d(minX, minY, minZ);
00962 maxPt = octomap::point3d(maxX, maxY, maxZ);
00963
00964 OcTreeKey paddedMaxKey;
00965 if (!m_octree->coordToKeyChecked(minPt, m_maxTreeDepth, m_paddedMinKey)){
00966 ROS_ERROR("Could not create padded min OcTree key at %f %f %f", minPt.x(), minPt.y(), minPt.z());
00967 return;
00968 }
00969 if (!m_octree->coordToKeyChecked(maxPt, m_maxTreeDepth, paddedMaxKey)){
00970 ROS_ERROR("Could not create padded max OcTree key at %f %f %f", maxPt.x(), maxPt.y(), maxPt.z());
00971 return;
00972 }
00973
00974 ROS_DEBUG("Padded MinKey: %d %d %d / padded MaxKey: %d %d %d", m_paddedMinKey[0], m_paddedMinKey[1], m_paddedMinKey[2], paddedMaxKey[0], paddedMaxKey[1], paddedMaxKey[2]);
00975 assert(paddedMaxKey[0] >= maxKey[0] && paddedMaxKey[1] >= maxKey[1]);
00976
00977 m_multires2DScale = 1 << (m_treeDepth - m_maxTreeDepth);
00978 m_gridmap.info.width = (paddedMaxKey[0] - m_paddedMinKey[0])/m_multires2DScale +1;
00979 m_gridmap.info.height = (paddedMaxKey[1] - m_paddedMinKey[1])/m_multires2DScale +1;
00980
00981 int mapOriginX = minKey[0] - m_paddedMinKey[0];
00982 int mapOriginY = minKey[1] - m_paddedMinKey[1];
00983 assert(mapOriginX >= 0 && mapOriginY >= 0);
00984
00985
00986 octomap::point3d origin = m_octree->keyToCoord(m_paddedMinKey, m_treeDepth);
00987 double gridRes = m_octree->getNodeSize(m_maxTreeDepth);
00988 m_projectCompleteMap = (!m_incrementalUpdate || (std::abs(gridRes-m_gridmap.info.resolution) > 1e-6));
00989 m_gridmap.info.resolution = gridRes;
00990 m_gridmap.info.origin.position.x = origin.x() - gridRes*0.5;
00991 m_gridmap.info.origin.position.y = origin.y() - gridRes*0.5;
00992 if (m_maxTreeDepth != m_treeDepth){
00993 m_gridmap.info.origin.position.x -= m_res/2.0;
00994 m_gridmap.info.origin.position.y -= m_res/2.0;
00995 }
00996
00997
00998
00999 if (m_maxTreeDepth < m_treeDepth)
01000 m_projectCompleteMap = true;
01001
01002
01003 if(m_projectCompleteMap){
01004 ROS_DEBUG("Rebuilding complete 2D map");
01005 m_gridmap.data.clear();
01006
01007 m_gridmap.data.resize(m_gridmap.info.width * m_gridmap.info.height, -1);
01008
01009 } else {
01010
01011 if (mapChanged(oldMapInfo, m_gridmap.info)){
01012 ROS_DEBUG("2D grid map size changed to %dx%d", m_gridmap.info.width, m_gridmap.info.height);
01013 adjustMapData(m_gridmap, oldMapInfo);
01014 }
01015 nav_msgs::OccupancyGrid::_data_type::iterator startIt;
01016 size_t mapUpdateBBXMinX = std::max(0, (int(m_updateBBXMin[0]) - int(m_paddedMinKey[0]))/int(m_multires2DScale));
01017 size_t mapUpdateBBXMinY = std::max(0, (int(m_updateBBXMin[1]) - int(m_paddedMinKey[1]))/int(m_multires2DScale));
01018 size_t mapUpdateBBXMaxX = std::min(int(m_gridmap.info.width-1), (int(m_updateBBXMax[0]) - int(m_paddedMinKey[0]))/int(m_multires2DScale));
01019 size_t mapUpdateBBXMaxY = std::min(int(m_gridmap.info.height-1), (int(m_updateBBXMax[1]) - int(m_paddedMinKey[1]))/int(m_multires2DScale));
01020
01021 assert(mapUpdateBBXMaxX > mapUpdateBBXMinX);
01022 assert(mapUpdateBBXMaxY > mapUpdateBBXMinY);
01023
01024 size_t numCols = mapUpdateBBXMaxX-mapUpdateBBXMinX +1;
01025
01026
01027 uint max_idx = m_gridmap.info.width*mapUpdateBBXMaxY + mapUpdateBBXMaxX;
01028 if (max_idx >= m_gridmap.data.size())
01029 ROS_ERROR("BBX index not valid: %d (max index %zu for size %d x %d) update-BBX is: [%zu %zu]-[%zu %zu]", max_idx, m_gridmap.data.size(), m_gridmap.info.width, m_gridmap.info.height, mapUpdateBBXMinX, mapUpdateBBXMinY, mapUpdateBBXMaxX, mapUpdateBBXMaxY);
01030
01031
01032 for (unsigned int j = mapUpdateBBXMinY; j <= mapUpdateBBXMaxY; ++j){
01033 std::fill_n(m_gridmap.data.begin() + m_gridmap.info.width*j+mapUpdateBBXMinX,
01034 numCols, -1);
01035 }
01036
01037 }
01038
01039
01040
01041 }
01042
01043 }
01044
01045 void OctomapServer::handlePostNodeTraversal(const ros::Time& rostime){
01046
01047 if (m_publish2DMap)
01048 m_mapPub.publish(m_gridmap);
01049 }
01050
01051 void OctomapServer::handleOccupiedNode(const OcTreeT::iterator& it){
01052
01053 if (m_publish2DMap && m_projectCompleteMap){
01054 update2DMap(it, true);
01055 }
01056 }
01057
01058 void OctomapServer::handleFreeNode(const OcTreeT::iterator& it){
01059
01060 if (m_publish2DMap && m_projectCompleteMap){
01061 update2DMap(it, false);
01062 }
01063 }
01064
01065 void OctomapServer::handleOccupiedNodeInBBX(const OcTreeT::iterator& it){
01066
01067 if (m_publish2DMap && !m_projectCompleteMap){
01068 update2DMap(it, true);
01069 }
01070 }
01071
01072 void OctomapServer::handleFreeNodeInBBX(const OcTreeT::iterator& it){
01073
01074 if (m_publish2DMap && !m_projectCompleteMap){
01075 update2DMap(it, false);
01076 }
01077 }
01078
01079 void OctomapServer::update2DMap(const OcTreeT::iterator& it, bool occupied){
01080
01081
01082
01083 if (it.getDepth() == m_maxTreeDepth){
01084 unsigned idx = mapIdx(it.getKey());
01085 if (occupied)
01086 m_gridmap.data[mapIdx(it.getKey())] = 100;
01087 else if (m_gridmap.data[idx] == -1){
01088 m_gridmap.data[idx] = 0;
01089 }
01090
01091 } else{
01092 int intSize = 1 << (m_maxTreeDepth - it.getDepth());
01093 octomap::OcTreeKey minKey=it.getIndexKey();
01094 for(int dx=0; dx < intSize; dx++){
01095 int i = (minKey[0]+dx - m_paddedMinKey[0])/m_multires2DScale;
01096 for(int dy=0; dy < intSize; dy++){
01097 unsigned idx = mapIdx(i, (minKey[1]+dy - m_paddedMinKey[1])/m_multires2DScale);
01098 if (occupied)
01099 m_gridmap.data[idx] = 100;
01100 else if (m_gridmap.data[idx] == -1){
01101 m_gridmap.data[idx] = 0;
01102 }
01103 }
01104 }
01105 }
01106
01107
01108 }
01109
01110
01111
01112 bool OctomapServer::isSpeckleNode(const OcTreeKey&nKey) const {
01113 OcTreeKey key;
01114 bool neighborFound = false;
01115 for (key[2] = nKey[2] - 1; !neighborFound && key[2] <= nKey[2] + 1; ++key[2]){
01116 for (key[1] = nKey[1] - 1; !neighborFound && key[1] <= nKey[1] + 1; ++key[1]){
01117 for (key[0] = nKey[0] - 1; !neighborFound && key[0] <= nKey[0] + 1; ++key[0]){
01118 if (key != nKey){
01119 OcTreeNode* node = m_octree->search(key);
01120 if (node && m_octree->isNodeOccupied(node)){
01121
01122 neighborFound = true;
01123 }
01124 }
01125 }
01126 }
01127 }
01128
01129 return neighborFound;
01130 }
01131
01132 void OctomapServer::reconfigureCallback(octomap_server::OctomapServerConfig& config, uint32_t level){
01133 if (m_maxTreeDepth != unsigned(config.max_depth)){
01134 m_maxTreeDepth = unsigned(config.max_depth);
01135
01136 publishAll();
01137 }
01138
01139
01140 }
01141
01142 void OctomapServer::adjustMapData(nav_msgs::OccupancyGrid& map, const nav_msgs::MapMetaData& oldMapInfo) const{
01143 if (map.info.resolution != oldMapInfo.resolution){
01144 ROS_ERROR("Resolution of map changed, cannot be adjusted");
01145 return;
01146 }
01147
01148 int i_off = int((oldMapInfo.origin.position.x - map.info.origin.position.x)/map.info.resolution +0.5);
01149 int j_off = int((oldMapInfo.origin.position.y - map.info.origin.position.y)/map.info.resolution +0.5);
01150
01151 if (i_off < 0 || j_off < 0
01152 || oldMapInfo.width + i_off > map.info.width
01153 || oldMapInfo.height + j_off > map.info.height)
01154 {
01155 ROS_ERROR("New 2D map does not contain old map area, this case is not implemented");
01156 return;
01157 }
01158
01159 nav_msgs::OccupancyGrid::_data_type oldMapData = map.data;
01160
01161 map.data.clear();
01162
01163 map.data.resize(map.info.width * map.info.height, -1);
01164
01165 nav_msgs::OccupancyGrid::_data_type::iterator fromStart, fromEnd, toStart;
01166
01167 for (int j =0; j < int(oldMapInfo.height); ++j ){
01168
01169 fromStart = oldMapData.begin() + j*oldMapInfo.width;
01170 fromEnd = fromStart + oldMapInfo.width;
01171 toStart = map.data.begin() + ((j+j_off)*m_gridmap.info.width + i_off);
01172 copy(fromStart, fromEnd, toStart);
01173
01174
01175
01176
01177
01178 }
01179
01180 }
01181
01182
01183 std_msgs::ColorRGBA OctomapServer::heightMapColor(double h) {
01184
01185 std_msgs::ColorRGBA color;
01186 color.a = 1.0;
01187
01188
01189 double s = 1.0;
01190 double v = 1.0;
01191
01192 h -= floor(h);
01193 h *= 6;
01194 int i;
01195 double m, n, f;
01196
01197 i = floor(h);
01198 f = h - i;
01199 if (!(i & 1))
01200 f = 1 - f;
01201 m = v * (1 - s);
01202 n = v * (1 - s * f);
01203
01204 switch (i) {
01205 case 6:
01206 case 0:
01207 color.r = v; color.g = n; color.b = m;
01208 break;
01209 case 1:
01210 color.r = n; color.g = v; color.b = m;
01211 break;
01212 case 2:
01213 color.r = m; color.g = v; color.b = n;
01214 break;
01215 case 3:
01216 color.r = m; color.g = n; color.b = v;
01217 break;
01218 case 4:
01219 color.r = n; color.g = m; color.b = v;
01220 break;
01221 case 5:
01222 color.r = v; color.g = m; color.b = n;
01223 break;
01224 default:
01225 color.r = 1; color.g = 0.5; color.b = 0.5;
01226 break;
01227 }
01228
01229 return color;
01230 }
01231 }
01232
01233
01234