00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include <QObject>
00033
00034 #include "octomap_rviz_plugins/occupancy_grid_display.h"
00035
00036 #include <boost/bind.hpp>
00037 #include <boost/shared_ptr.hpp>
00038
00039 #include <OGRE/OgreSceneNode.h>
00040 #include <OGRE/OgreSceneManager.h>
00041
00042 #include "rviz/visualization_manager.h"
00043 #include "rviz/frame_manager.h"
00044 #include "rviz/properties/int_property.h"
00045 #include "rviz/properties/ros_topic_property.h"
00046 #include "rviz/properties/enum_property.h"
00047
00048 #include <octomap/octomap.h>
00049 #include <octomap_msgs/Octomap.h>
00050 #include <octomap_msgs/conversions.h>
00051
00052 #include <sstream>
00053
00054 using namespace rviz;
00055
00056 namespace octomap_rviz_plugin
00057 {
00058
00059 static const std::size_t max_octree_depth_ = sizeof(unsigned short) * 8;
00060
00061 enum OctreeVoxelRenderMode
00062 {
00063 OCTOMAP_FREE_VOXELS = 1,
00064 OCTOMAP_OCCUPIED_VOXELS = 2
00065 };
00066
00067 enum OctreeVoxelColorMode
00068 {
00069 OCTOMAP_Z_AXIS_COLOR,
00070 OCTOMAP_PROBABLILTY_COLOR,
00071 };
00072
00073 OccupancyGridDisplay::OccupancyGridDisplay() :
00074 rviz::Display(),
00075 new_points_received_(false),
00076 messages_received_(0),
00077 queue_size_(5),
00078 color_factor_(0.8),
00079 octree_depth_(0)
00080 {
00081
00082 octomap_topic_property_ = new RosTopicProperty( "Octomap Topic",
00083 "",
00084 QString::fromStdString(ros::message_traits::datatype<octomap_msgs::Octomap>()),
00085 "octomap_msgs::Octomap topic to subscribe to (binary or full probability map)",
00086 this,
00087 SLOT( updateTopic() ));
00088
00089 queue_size_property_ = new IntProperty( "Queue Size",
00090 queue_size_,
00091 "Advanced: set the size of the incoming message queue. Increasing this "
00092 "is useful if your incoming TF data is delayed significantly from your"
00093 " image data, but it can greatly increase memory usage if the messages are big.",
00094 this,
00095 SLOT( updateQueueSize() ));
00096 queue_size_property_->setMin(1);
00097
00098 octree_render_property_ = new rviz::EnumProperty( "Voxel Rendering", "Occupied Voxels",
00099 "Select voxel type.",
00100 this,
00101 SLOT( updateOctreeRenderMode() ) );
00102
00103 octree_render_property_->addOption( "Occupied Voxels", OCTOMAP_OCCUPIED_VOXELS );
00104 octree_render_property_->addOption( "Free Voxels", OCTOMAP_FREE_VOXELS );
00105 octree_render_property_->addOption( "All Voxels", OCTOMAP_FREE_VOXELS | OCTOMAP_OCCUPIED_VOXELS);
00106
00107 octree_coloring_property_ = new rviz::EnumProperty( "Voxel Coloring", "Z-Axis",
00108 "Select voxel coloring mode",
00109 this,
00110 SLOT( updateOctreeColorMode() ) );
00111
00112 octree_coloring_property_->addOption( "Z-Axis", OCTOMAP_Z_AXIS_COLOR );
00113 octree_coloring_property_->addOption( "Cell Probability", OCTOMAP_PROBABLILTY_COLOR );
00114
00115 tree_depth_property_ = new IntProperty("Max. Octree Depth",
00116 max_octree_depth_,
00117 "Defines the maximum tree depth",
00118 this,
00119 SLOT (updateTreeDepth() ));
00120 tree_depth_property_->setMin(0);
00121 }
00122
00123 void OccupancyGridDisplay::onInitialize()
00124 {
00125 boost::mutex::scoped_lock lock(mutex_);
00126
00127 box_size_.resize(max_octree_depth_);
00128 cloud_.resize(max_octree_depth_);
00129 point_buf_.resize(max_octree_depth_);
00130 new_points_.resize(max_octree_depth_);
00131
00132 for (std::size_t i = 0; i < max_octree_depth_; ++i)
00133 {
00134 std::stringstream sname;
00135 sname << "PointCloud Nr." << i;
00136 cloud_[i] = new rviz::PointCloud();
00137 cloud_[i]->setName(sname.str());
00138 cloud_[i]->setRenderMode(rviz::PointCloud::RM_BOXES);
00139 scene_node_->attachObject(cloud_[i]);
00140 }
00141 }
00142
00143 OccupancyGridDisplay::~OccupancyGridDisplay()
00144 {
00145 std::size_t i;
00146
00147 unsubscribe();
00148
00149 for (std::vector<rviz::PointCloud*>::iterator it = cloud_.begin(); it != cloud_.end(); ++it) {
00150 delete *(it);
00151 }
00152
00153 if (scene_node_)
00154 scene_node_->detachAllObjects();
00155 }
00156
00157 void OccupancyGridDisplay::updateQueueSize()
00158 {
00159 queue_size_ = queue_size_property_->getInt();
00160
00161 subscribe();
00162 }
00163
00164 void OccupancyGridDisplay::onEnable()
00165 {
00166 scene_node_->setVisible(true);
00167 subscribe();
00168 }
00169
00170 void OccupancyGridDisplay::onDisable()
00171 {
00172 scene_node_->setVisible(false);
00173 unsubscribe();
00174
00175 clear();
00176 }
00177
00178 void OccupancyGridDisplay::subscribe()
00179 {
00180 if (!isEnabled())
00181 {
00182 return;
00183 }
00184
00185 try
00186 {
00187 unsubscribe();
00188
00189 const std::string& topicStr = octomap_topic_property_->getStdString();
00190
00191 if (!topicStr.empty())
00192 {
00193
00194 sub_.reset(new message_filters::Subscriber<octomap_msgs::Octomap>());
00195
00196 sub_->subscribe(threaded_nh_, topicStr, queue_size_);
00197 sub_->registerCallback(boost::bind(&OccupancyGridDisplay::incomingMessageCallback, this, _1));
00198
00199 }
00200 }
00201 catch (ros::Exception& e)
00202 {
00203 setStatus(StatusProperty::Error, "Topic", (std::string("Error subscribing: ") + e.what()).c_str());
00204 }
00205
00206 }
00207
00208 void OccupancyGridDisplay::unsubscribe()
00209 {
00210 clear();
00211
00212 try
00213 {
00214
00215 sub_.reset();
00216 }
00217 catch (ros::Exception& e)
00218 {
00219 setStatus(StatusProperty::Error, "Topic", (std::string("Error unsubscribing: ") + e.what()).c_str());
00220 }
00221
00222 }
00223
00224
00225 void OccupancyGridDisplay::setColor(double z_pos, double min_z, double max_z, double color_factor,
00226 rviz::PointCloud::Point& point)
00227 {
00228 int i;
00229 double m, n, f;
00230
00231 double s = 1.0;
00232 double v = 1.0;
00233
00234 double h = (1.0 - std::min(std::max((z_pos - min_z) / (max_z - min_z), 0.0), 1.0)) * color_factor;
00235
00236 h -= floor(h);
00237 h *= 6;
00238 i = floor(h);
00239 f = h - i;
00240 if (!(i & 1))
00241 f = 1 - f;
00242 m = v * (1 - s);
00243 n = v * (1 - s * f);
00244
00245 switch (i)
00246 {
00247 case 6:
00248 case 0:
00249 point.setColor(v, n, m);
00250 break;
00251 case 1:
00252 point.setColor(n, v, m);
00253 break;
00254 case 2:
00255 point.setColor(m, v, n);
00256 break;
00257 case 3:
00258 point.setColor(m, n, v);
00259 break;
00260 case 4:
00261 point.setColor(n, m, v);
00262 break;
00263 case 5:
00264 point.setColor(v, m, n);
00265 break;
00266 default:
00267 point.setColor(1, 0.5, 0.5);
00268 break;
00269 }
00270 }
00271
00272 void OccupancyGridDisplay::incomingMessageCallback(const octomap_msgs::OctomapConstPtr& msg)
00273 {
00274 ++messages_received_;
00275 setStatus(StatusProperty::Ok, "Messages", QString::number(messages_received_) + " octomap messages received");
00276
00277 ROS_DEBUG("Received OctomapBinary message (size: %d bytes)", (int)msg->data.size());
00278
00279
00280 Ogre::Vector3 pos;
00281 Ogre::Quaternion orient;
00282 if (!context_->getFrameManager()->getTransform(msg->header, pos, orient))
00283 {
00284 std::stringstream ss;
00285 ss << "Failed to transform from frame [" << msg->header.frame_id << "] to frame ["
00286 << context_->getFrameManager()->getFixedFrame() << "]";
00287 this->setStatusStd(StatusProperty::Error, "Message", ss.str());
00288 return;
00289 }
00290
00291 scene_node_->setOrientation(orient);
00292 scene_node_->setPosition(pos);
00293
00294
00295 octomap::OcTree* octomap = NULL;
00296 octomap::AbstractOcTree* tree = octomap_msgs::msgToMap(*msg);
00297 if (tree){
00298 octomap = dynamic_cast<octomap::OcTree*>(tree);
00299 }
00300
00301 if (!octomap)
00302 {
00303 this->setStatusStd(StatusProperty::Error, "Message", "Failed to create octree structure");
00304 return;
00305 }
00306
00307 std::size_t octree_depth = octomap->getTreeDepth();
00308 tree_depth_property_->setMax(octomap->getTreeDepth());
00309
00310
00311
00312 double minX, minY, minZ, maxX, maxY, maxZ;
00313 octomap->getMetricMin(minX, minY, minZ);
00314 octomap->getMetricMax(maxX, maxY, maxZ);
00315
00316
00317 for (std::size_t i = 0; i < max_octree_depth_; ++i)
00318 {
00319 point_buf_[i].clear();
00320 box_size_[i] = octomap->getNodeSize(i + 1);
00321 }
00322
00323 size_t pointCount = 0;
00324 {
00325
00326 unsigned int treeDepth = std::min<unsigned int>(tree_depth_property_->getInt(), octomap->getTreeDepth());
00327 for (octomap::OcTree::iterator it = octomap->begin(treeDepth), end = octomap->end(); it != end; ++it)
00328 {
00329
00330 if (octomap->isNodeOccupied(*it))
00331 {
00332
00333 int render_mode_mask = octree_render_property_->getOptionInt();
00334
00335 bool display_voxel = false;
00336
00337
00338 if (((int)octomap->isNodeOccupied(*it) + 1) & render_mode_mask)
00339 {
00340
00341 bool allNeighborsFound = true;
00342
00343 octomap::OcTreeKey key;
00344 octomap::OcTreeKey nKey = it.getKey();
00345
00346 for (key[2] = nKey[2] - 1; allNeighborsFound && key[2] <= nKey[2] + 1; ++key[2])
00347 {
00348 for (key[1] = nKey[1] - 1; allNeighborsFound && key[1] <= nKey[1] + 1; ++key[1])
00349 {
00350 for (key[0] = nKey[0] - 1; allNeighborsFound && key[0] <= nKey[0] + 1; ++key[0])
00351 {
00352 if (key != nKey)
00353 {
00354 octomap::OcTreeNode* node = octomap->search(key);
00355
00356
00357 if (!(node && (((int)octomap->isNodeOccupied(node)) + 1) & render_mode_mask))
00358 {
00359
00360 allNeighborsFound = false;
00361 }
00362 }
00363 }
00364 }
00365 }
00366
00367 display_voxel |= !allNeighborsFound;
00368 }
00369
00370
00371 if (display_voxel)
00372 {
00373 PointCloud::Point newPoint;
00374
00375 newPoint.position.x = it.getX();
00376 newPoint.position.y = it.getY();
00377 newPoint.position.z = it.getZ();
00378
00379 float cell_probability;
00380
00381 OctreeVoxelColorMode octree_color_mode = static_cast<OctreeVoxelColorMode>(octree_coloring_property_->getOptionInt());
00382
00383 switch (octree_color_mode)
00384 {
00385 case OCTOMAP_Z_AXIS_COLOR:
00386 setColor(newPoint.position.z, minZ, maxZ, color_factor_, newPoint);
00387 break;
00388 case OCTOMAP_PROBABLILTY_COLOR:
00389 cell_probability = it->getOccupancy();
00390 newPoint.setColor((1.0f-cell_probability), cell_probability, 0.0);
00391 break;
00392 default:
00393 break;
00394 }
00395
00396
00397 unsigned int depth = it.getDepth();
00398 point_buf_[depth - 1].push_back(newPoint);
00399
00400 ++pointCount;
00401 }
00402 }
00403 }
00404 }
00405
00406 if (pointCount)
00407 {
00408 boost::mutex::scoped_lock lock(mutex_);
00409
00410 new_points_received_ = true;
00411
00412 for (size_t i = 0; i < max_octree_depth_; ++i)
00413 new_points_[i].swap(point_buf_[i]);
00414
00415 }
00416 delete octomap;
00417 }
00418
00419 void OccupancyGridDisplay::updateTreeDepth()
00420 {
00421 }
00422
00423 void OccupancyGridDisplay::updateOctreeRenderMode()
00424 {
00425 }
00426
00427 void OccupancyGridDisplay::updateOctreeColorMode()
00428 {
00429 }
00430
00431 void OccupancyGridDisplay::clear()
00432 {
00433
00434 boost::mutex::scoped_lock lock(mutex_);
00435
00436
00437 for (size_t i = 0; i < cloud_.size(); ++i)
00438 {
00439 cloud_[i]->clear();
00440 }
00441 }
00442
00443 void OccupancyGridDisplay::update(float wall_dt, float ros_dt)
00444 {
00445 if (new_points_received_)
00446 {
00447 boost::mutex::scoped_lock lock(mutex_);
00448
00449 for (size_t i = 0; i < max_octree_depth_; ++i)
00450 {
00451 double size = box_size_[i];
00452
00453 cloud_[i]->clear();
00454 cloud_[i]->setDimensions(size, size, size);
00455
00456 cloud_[i]->addPoints(&new_points_[i].front(), new_points_[i].size());
00457 new_points_[i].clear();
00458
00459 }
00460 new_points_received_ = false;
00461 }
00462 }
00463
00464 void OccupancyGridDisplay::reset()
00465 {
00466 clear();
00467 messages_received_ = 0;
00468 setStatus(StatusProperty::Ok, "Messages", QString("0 binary octomap messages received"));
00469 }
00470
00471 void OccupancyGridDisplay::updateTopic()
00472 {
00473 unsubscribe();
00474 reset();
00475 subscribe();
00476 context_->queueRender();
00477 }
00478
00479
00480 }
00481
00482 #include <pluginlib/class_list_macros.h>
00483
00484 PLUGINLIB_EXPORT_CLASS( octomap_rviz_plugin::OccupancyGridDisplay, rviz::Display)
00485