Nav2 Navigation Stack - humble  humble
ROS 2 Navigation Stack
particle_cloud_display.cpp
1 /*
2  * Copyright (c) 2012, Willow Garage, Inc.
3  * Copyright (c) 2018, Bosch Software Innovations GmbH.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  * * Neither the name of the Willow Garage, Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 // Copyright (c) 2019 Intel Corporation
32 // Copyright (c) 2020 Sarthak Mittal
33 //
34 // Licensed under the Apache License, Version 2.0 (the "License");
35 // you may not use this file except in compliance with the License.
36 // You may obtain a copy of the License at
37 //
38 // http://www.apache.org/licenses/LICENSE-2.0
39 //
40 // Unless required by applicable law or agreed to in writing, software
41 // distributed under the License is distributed on an "AS IS" BASIS,
42 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
43 // See the License for the specific language governing permissions and
44 // limitations under the License.
45 
46 #include "nav2_rviz_plugins/particle_cloud_display/particle_cloud_display.hpp"
47 
48 #include <memory>
49 #include <string>
50 
51 #include <OgreManualObject.h>
52 #include <OgreMaterialManager.h>
53 #include <OgreTechnique.h>
54 
55 #include "rviz_common/logging.hpp"
56 #include "rviz_common/msg_conversions.hpp"
57 #include "rviz_common/properties/enum_property.hpp"
58 #include "rviz_common/properties/color_property.hpp"
59 #include "rviz_common/properties/float_property.hpp"
60 #include "rviz_common/validate_floats.hpp"
61 
62 #include "rviz_rendering/objects/arrow.hpp"
63 #include "rviz_rendering/objects/axes.hpp"
64 
65 #include "nav2_rviz_plugins/particle_cloud_display/flat_weighted_arrows_array.hpp"
66 
67 namespace nav2_rviz_plugins
68 {
69 namespace
70 {
71 struct ShapeType
72 {
73  enum
74  {
75  Arrow2d,
76  Arrow3d,
77  Axes,
78  };
79 };
80 
81 } // namespace
82 
83 ParticleCloudDisplay::ParticleCloudDisplay(
84  rviz_common::DisplayContext * display_context,
85  Ogre::SceneNode * scene_node)
86 : ParticleCloudDisplay()
87 {
88  context_ = display_context;
89  scene_node_ = scene_node;
90  scene_manager_ = context_->getSceneManager();
91 
92  arrows2d_ = std::make_unique<FlatWeightedArrowsArray>(scene_manager_);
93  arrows2d_->createAndAttachManualObject(scene_node);
94  arrow_node_ = scene_node_->createChildSceneNode();
95  axes_node_ = scene_node_->createChildSceneNode();
96  updateShapeChoice();
97 }
98 
99 ParticleCloudDisplay::ParticleCloudDisplay()
100 : min_length_(0.02f), max_length_(0.3f)
101 {
102  initializeProperties();
103 
104  shape_property_->addOption("Arrow (Flat)", ShapeType::Arrow2d);
105  shape_property_->addOption("Arrow (3D)", ShapeType::Arrow3d);
106  shape_property_->addOption("Axes", ShapeType::Axes);
107  arrow_alpha_property_->setMin(0);
108  arrow_alpha_property_->setMax(1);
109  arrow_min_length_property_->setMax(max_length_);
110  arrow_max_length_property_->setMin(min_length_);
111 }
112 
113 void ParticleCloudDisplay::initializeProperties()
114 {
115  shape_property_ = new rviz_common::properties::EnumProperty(
116  "Shape", "Arrow (Flat)", "Shape to display the pose as.", this, SLOT(updateShapeChoice()));
117 
118  arrow_color_property_ = new rviz_common::properties::ColorProperty(
119  "Color", QColor(255, 25, 0), "Color to draw the arrows.", this, SLOT(updateArrowColor()));
120 
121  arrow_alpha_property_ = new rviz_common::properties::FloatProperty(
122  "Alpha",
123  1.0f,
124  "Amount of transparency to apply to the displayed poses.",
125  this,
126  SLOT(updateArrowColor()));
127 
128  arrow_min_length_property_ = new rviz_common::properties::FloatProperty(
129  "Min Arrow Length", min_length_, "Minimum length of the arrows.", this, SLOT(updateGeometry()));
130 
131  arrow_max_length_property_ = new rviz_common::properties::FloatProperty(
132  "Max Arrow Length", max_length_, "Maximum length of the arrows.", this, SLOT(updateGeometry()));
133 
134  // Scales are set based on initial values
135  length_scale_ = max_length_ - min_length_;
136  shaft_radius_scale_ = 0.0435;
137  head_length_scale_ = 0.3043;
138  head_radius_scale_ = 0.1304;
139 }
140 
141 ParticleCloudDisplay::~ParticleCloudDisplay()
142 {
143  // because of forward declaration of arrow and axes, destructor cannot be declared in .hpp as
144  // default
145 }
146 
147 void ParticleCloudDisplay::onInitialize()
148 {
149  MFDClass::onInitialize();
150  arrows2d_ = std::make_unique<FlatWeightedArrowsArray>(scene_manager_);
151  arrows2d_->createAndAttachManualObject(scene_node_);
152  arrow_node_ = scene_node_->createChildSceneNode();
153  axes_node_ = scene_node_->createChildSceneNode();
154  updateShapeChoice();
155 }
156 
157 void ParticleCloudDisplay::processMessage(const nav2_msgs::msg::ParticleCloud::ConstSharedPtr msg)
158 {
159  if (!validateFloats(*msg)) {
160  setStatus(
161  rviz_common::properties::StatusProperty::Error,
162  "Topic",
163  "Message contained invalid floating point values (nans or infs)");
164  return;
165  }
166 
167  if (!setTransform(msg->header)) {
168  return;
169  }
170 
171  poses_.resize(msg->particles.size());
172 
173  for (std::size_t i = 0; i < msg->particles.size(); ++i) {
174  poses_[i].position = rviz_common::pointMsgToOgre(msg->particles[i].pose.position);
175  poses_[i].orientation = rviz_common::quaternionMsgToOgre(msg->particles[i].pose.orientation);
176  poses_[i].weight = static_cast<float>(msg->particles[i].weight);
177  }
178 
179  updateDisplay();
180 
181  context_->queueRender();
182 }
183 
184 bool ParticleCloudDisplay::validateFloats(const nav2_msgs::msg::ParticleCloud & msg)
185 {
186  for (auto & particle : msg.particles) {
187  if (!rviz_common::validateFloats(particle.pose) ||
188  !rviz_common::validateFloats(particle.weight))
189  {
190  return false;
191  }
192  }
193  return true;
194 }
195 
196 bool ParticleCloudDisplay::setTransform(std_msgs::msg::Header const & header)
197 {
198  Ogre::Vector3 position;
199  Ogre::Quaternion orientation;
200  if (!context_->getFrameManager()->getTransform(header, position, orientation)) {
201  setMissingTransformToFixedFrame(header.frame_id);
202  return false;
203  }
204  setTransformOk();
205 
206  scene_node_->setPosition(position);
207  scene_node_->setOrientation(orientation);
208  return true;
209 }
210 
211 void ParticleCloudDisplay::updateDisplay()
212 {
213  int shape = shape_property_->getOptionInt();
214  switch (shape) {
215  case ShapeType::Arrow2d:
216  updateArrows2d();
217  arrows3d_.clear();
218  axes_.clear();
219  break;
220  case ShapeType::Arrow3d:
221  updateArrows3d();
222  arrows2d_->clear();
223  axes_.clear();
224  break;
225  case ShapeType::Axes:
226  updateAxes();
227  arrows2d_->clear();
228  arrows3d_.clear();
229  break;
230  }
231 }
232 
233 void ParticleCloudDisplay::updateArrows2d()
234 {
235  arrows2d_->updateManualObject(
236  arrow_color_property_->getOgreColor(),
237  arrow_alpha_property_->getFloat(),
238  min_length_,
239  max_length_,
240  poses_);
241 }
242 
243 void ParticleCloudDisplay::updateArrows3d()
244 {
245  while (arrows3d_.size() < poses_.size()) {
246  arrows3d_.push_back(makeArrow3d());
247  }
248  while (arrows3d_.size() > poses_.size()) {
249  arrows3d_.pop_back();
250  }
251 
252  Ogre::Quaternion adjust_orientation(Ogre::Degree(-90), Ogre::Vector3::UNIT_Y);
253  float shaft_length;
254  for (std::size_t i = 0; i < poses_.size(); ++i) {
255  shaft_length = std::min(
256  std::max(
257  poses_[i].weight * length_scale_ + min_length_,
258  min_length_), max_length_);
259  arrows3d_[i]->set(
260  shaft_length,
261  shaft_length * shaft_radius_scale_,
262  shaft_length * head_length_scale_,
263  shaft_length * head_radius_scale_
264  );
265  arrows3d_[i]->setPosition(poses_[i].position);
266  arrows3d_[i]->setOrientation(poses_[i].orientation * adjust_orientation);
267  }
268 }
269 
270 void ParticleCloudDisplay::updateAxes()
271 {
272  while (axes_.size() < poses_.size()) {
273  axes_.push_back(makeAxes());
274  }
275  while (axes_.size() > poses_.size()) {
276  axes_.pop_back();
277  }
278  float shaft_length;
279  for (std::size_t i = 0; i < poses_.size(); ++i) {
280  shaft_length = std::min(
281  std::max(
282  poses_[i].weight * length_scale_ + min_length_,
283  min_length_), max_length_);
284  axes_[i]->set(shaft_length, shaft_length * shaft_radius_scale_);
285  axes_[i]->setPosition(poses_[i].position);
286  axes_[i]->setOrientation(poses_[i].orientation);
287  }
288 }
289 
290 std::unique_ptr<rviz_rendering::Arrow> ParticleCloudDisplay::makeArrow3d()
291 {
292  Ogre::ColourValue color = arrow_color_property_->getOgreColor();
293  color.a = arrow_alpha_property_->getFloat();
294 
295  auto arrow = std::make_unique<rviz_rendering::Arrow>(
296  scene_manager_,
297  arrow_node_,
298  min_length_,
299  min_length_ * shaft_radius_scale_,
300  min_length_ * head_length_scale_,
301  min_length_ * head_radius_scale_
302  );
303 
304  arrow->setColor(color);
305  return arrow;
306 }
307 
308 std::unique_ptr<rviz_rendering::Axes> ParticleCloudDisplay::makeAxes()
309 {
310  return std::make_unique<rviz_rendering::Axes>(
311  scene_manager_,
312  axes_node_,
313  min_length_,
314  min_length_ * shaft_radius_scale_
315  );
316 }
317 
318 void ParticleCloudDisplay::reset()
319 {
320  MFDClass::reset();
321  arrows2d_->clear();
322  arrows3d_.clear();
323  axes_.clear();
324 }
325 
326 void ParticleCloudDisplay::updateShapeChoice()
327 {
328  int shape = shape_property_->getOptionInt();
329  bool use_axes = shape == ShapeType::Axes;
330 
331  arrow_color_property_->setHidden(use_axes);
332  arrow_alpha_property_->setHidden(use_axes);
333 
334  if (initialized()) {
335  updateDisplay();
336  }
337 }
338 
339 void ParticleCloudDisplay::updateArrowColor()
340 {
341  int shape = shape_property_->getOptionInt();
342  Ogre::ColourValue color = arrow_color_property_->getOgreColor();
343  color.a = arrow_alpha_property_->getFloat();
344 
345  if (shape == ShapeType::Arrow2d) {
346  updateArrows2d();
347  } else if (shape == ShapeType::Arrow3d) {
348  for (const auto & arrow : arrows3d_) {
349  arrow->setColor(color);
350  }
351  }
352  context_->queueRender();
353 }
354 
355 void ParticleCloudDisplay::updateGeometry()
356 {
357  min_length_ = arrow_min_length_property_->getFloat();
358  max_length_ = arrow_max_length_property_->getFloat();
359  length_scale_ = max_length_ - min_length_;
360 
361  arrow_min_length_property_->setMax(max_length_);
362  arrow_max_length_property_->setMin(min_length_);
363 
364  int shape = shape_property_->getOptionInt();
365  switch (shape) {
366  case ShapeType::Arrow2d:
367  updateArrows2d();
368  arrows3d_.clear();
369  axes_.clear();
370  break;
371  case ShapeType::Arrow3d:
372  updateArrow3dGeometry();
373  arrows2d_->clear();
374  axes_.clear();
375  break;
376  case ShapeType::Axes:
377  updateAxesGeometry();
378  arrows2d_->clear();
379  arrows3d_.clear();
380  break;
381  }
382 
383  context_->queueRender();
384 }
385 
386 void ParticleCloudDisplay::updateArrow3dGeometry()
387 {
388  float shaft_length;
389  for (std::size_t i = 0; i < poses_.size() && i < arrows3d_.size(); ++i) {
390  shaft_length = std::min(
391  std::max(
392  poses_[i].weight * length_scale_ + min_length_,
393  min_length_), max_length_);
394  arrows3d_[i]->set(
395  shaft_length,
396  shaft_length * shaft_radius_scale_,
397  shaft_length * head_length_scale_,
398  shaft_length * head_radius_scale_
399  );
400  }
401 }
402 
403 void ParticleCloudDisplay::updateAxesGeometry()
404 {
405  float shaft_length;
406  for (std::size_t i = 0; i < poses_.size() && i < axes_.size(); ++i) {
407  shaft_length = std::min(
408  std::max(
409  poses_[i].weight * length_scale_ + min_length_,
410  min_length_), max_length_);
411  axes_[i]->set(shaft_length, shaft_length * shaft_radius_scale_);
412  }
413 }
414 
415 void ParticleCloudDisplay::setShape(QString shape)
416 {
417  shape_property_->setValue(shape);
418 }
419 
420 } // namespace nav2_rviz_plugins
421 
422 #include <pluginlib/class_list_macros.hpp> // NOLINT
423 PLUGINLIB_EXPORT_CLASS(nav2_rviz_plugins::ParticleCloudDisplay, rviz_common::Display)
Displays a nav2_msgs/ParticleCloud message as a bunch of line-drawn weighted arrows.