moveit2
The MoveIt Motion Planning Framework for ROS 2.
collision_octomap_filter.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2012, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 /* Author: Adam Leeper */
36 
39 #include <octomap/math/Vector3.h>
40 #include <octomap/math/Utils.h>
41 #include <octomap/octomap.h>
42 #include <geometric_shapes/shapes.h>
43 #include <rclcpp/logger.hpp>
44 #include <rclcpp/logging.hpp>
45 #include <memory>
46 
47 // Logger
48 static const rclcpp::Logger LOGGER = rclcpp::get_logger("moveit_collision_detection.collision_octomap_filter");
49 
50 // forward declarations
51 bool getMetaballSurfaceProperties(const octomap::point3d_list& cloud, const double& spacing, const double& iso_value,
52  const double& r_multiple, const octomath::Vector3& contact_point,
53  octomath::Vector3& normal, double& depth, bool estimate_depth);
54 
55 bool findSurface(const octomap::point3d_list& cloud, const double& spacing, const double& iso_value,
56  const double& r_multiple, const octomath::Vector3& seed, octomath::Vector3& surface_point,
57  octomath::Vector3& normal);
58 
59 bool sampleCloud(const octomap::point3d_list& cloud, const double& spacing, const double& r_multiple,
60  const octomath::Vector3& position, double& intensity, octomath::Vector3& gradient);
61 
62 int collision_detection::refineContactNormals(const World::ObjectConstPtr& object, CollisionResult& res,
63  const double cell_bbx_search_distance,
64  const double allowed_angle_divergence, const bool estimate_depth,
65  const double iso_value, const double metaball_radius_multiple)
66 {
67  if (!object)
68  {
69  RCLCPP_ERROR(LOGGER, "No valid Object passed in, cannot refine Normals!");
70  return 0;
71  }
72  if (res.contact_count < 1)
73  {
74  RCLCPP_WARN(LOGGER, "There do not appear to be any contacts, so there is nothing to refine!");
75  return 0;
76  }
77 
78  int modified = 0;
79 
80  // iterate through contacts
81  for (auto& contact : res.contacts)
82  {
83  const std::string contact1 = contact.first.first;
84  const std::string contact2 = contact.first.second;
85  std::string octomap_name = "";
86  std::vector<collision_detection::Contact>& contact_vector = contact.second;
87 
88  if (contact1.find("octomap") != std::string::npos)
89  octomap_name = contact1;
90  else if (contact2.find("octomap") != std::string::npos)
91  octomap_name = contact2;
92  else
93  {
94  continue;
95  }
96 
97  double cell_size = 0;
98  if (!object->shapes_.empty())
99  {
100  const shapes::ShapeConstPtr& shape = object->shapes_[0];
101  const std::shared_ptr<const shapes::OcTree> shape_octree = std::dynamic_pointer_cast<const shapes::OcTree>(shape);
102  if (shape_octree)
103  {
104  std::shared_ptr<const octomap::OcTree> octree = shape_octree->octree;
105  cell_size = octree->getResolution();
106  for (auto& contact_info : contact_vector)
107  {
108  const Eigen::Vector3d& point = contact_info.pos;
109  const Eigen::Vector3d& normal = contact_info.normal;
110 
111  const octomath::Vector3 contact_point(point[0], point[1], point[2]);
112  const octomath::Vector3 contact_normal(normal[0], normal[1], normal[2]);
113  const octomath::Vector3 diagonal = octomath::Vector3(1, 1, 1);
114  const octomath::Vector3 bbx_min = contact_point - diagonal * cell_size * cell_bbx_search_distance;
115  const octomath::Vector3 bbx_max = contact_point + diagonal * cell_size * cell_bbx_search_distance;
116  octomap::point3d_list node_centers;
117  octomap::OcTreeBaseImpl<octomap::OcTreeNode, octomap::AbstractOccupancyOcTree>::leaf_bbx_iterator it =
118  octree->begin_leafs_bbx(bbx_min, bbx_max);
119  octomap::OcTreeBaseImpl<octomap::OcTreeNode, octomap::AbstractOccupancyOcTree>::leaf_bbx_iterator leafs_end =
120  octree->end_leafs_bbx();
121  int count = 0;
122  for (; it != leafs_end; ++it)
123  {
124  const octomap::point3d pt = it.getCoordinate();
125  // double prob = it->getOccupancy();
126  if (octree->isNodeOccupied(*it)) // magic number!
127  {
128  count++;
129  node_centers.push_back(pt);
130  // RCLCPP_INFO(LOGGER, "Adding point %d with prob %.3f at [%.3f, %.3f, %.3f]",
131  // count, prob, pt.x(), pt.y(), pt.z());
132  }
133  }
134  // RCLCPP_INFO(LOGGER, "Contact point at [%.3f, %.3f, %.3f], cell size %.3f, occupied cells
135  // %d",
136  // contact_point.x(), contact_point.y(), contact_point.z(), cell_size, count);
137 
138  // octree->getOccupiedLeafsBBX(node_centers, bbx_min, bbx_max);
139  // RCLCPP_ERROR(LOGGER, "bad stuff in collision_octomap_filter.cpp; need to port octomap
140  // call for groovy");
141 
142  octomath::Vector3 n;
143  double depth;
144  if (getMetaballSurfaceProperties(node_centers, cell_size, iso_value, metaball_radius_multiple, contact_point,
145  n, depth, estimate_depth))
146  {
147  // only modify normal if the refinement predicts a "very different" result.
148  const double divergence = contact_normal.angleTo(n);
149  if (divergence > allowed_angle_divergence)
150  {
151  modified++;
152  // RCLCPP_INFO(LOGGER, "Normals differ by %.3f, changing: [%.3f, %.3f, %.3f] -> [%.3f,
153  // %.3f, %.3f]",
154  // divergence, contact_normal.x(), contact_normal.y(), contact_normal.z(),
155  // n.x(), n.y(), n.z());
156  contact_info.normal = Eigen::Vector3d(n.x(), n.y(), n.z());
157  }
158 
159  if (estimate_depth)
160  contact_info.depth = depth;
161  }
162  }
163  }
164  }
165  }
166  return modified;
167 }
168 
169 bool getMetaballSurfaceProperties(const octomap::point3d_list& cloud, const double& spacing, const double& iso_value,
170  const double& r_multiple, const octomath::Vector3& contact_point,
171  octomath::Vector3& normal, double& depth, const bool estimate_depth)
172 {
173  double intensity;
174  if (estimate_depth)
175  {
176  octomath::Vector3 surface_point;
177  if (findSurface(cloud, spacing, iso_value, r_multiple, contact_point, surface_point, normal))
178  {
179  depth = normal.dot(surface_point - contact_point); // do we prefer this, or magnitude of surface - contact?
180  return true;
181  }
182  else
183  {
184  return false;
185  }
186  }
187  else // just get normals, no depth
188  {
189  octomath::Vector3 gradient;
190  if (sampleCloud(cloud, spacing, r_multiple, contact_point, intensity, gradient))
191  {
192  normal = gradient.normalized();
193  return true;
194  }
195  else
196  {
197  return false;
198  }
199  }
200 }
201 
202 // --------------------------------------------------------------------------
203 // This algorithm is from Salisbury & Tarr's 1997 paper. It will find the
204 // closest point on the surface starting from a seed point that is close by
205 // following the direction of the field gradient.
206 bool findSurface(const octomap::point3d_list& cloud, const double& spacing, const double& iso_value,
207  const double& r_multiple, const octomath::Vector3& seed, octomath::Vector3& surface_point,
208  octomath::Vector3& normal)
209 {
210  const double epsilon = 1e-10;
211  const int iterations = 10;
212  double intensity = 0;
213 
214  octomath::Vector3 p = seed, dp, gs;
215  for (int i = 0; i < iterations; ++i)
216  {
217  if (!sampleCloud(cloud, spacing, r_multiple, p, intensity, gs))
218  return false;
219  const double s = iso_value - intensity;
220  dp = (gs * -s) * (1.0 / std::max(gs.dot(gs), epsilon));
221  p = p + dp;
222  if (dp.dot(dp) < epsilon)
223  {
224  surface_point = p;
225  normal = gs.normalized();
226  return true;
227  }
228  }
229  return false;
230  // return p;
231 }
232 
233 bool sampleCloud(const octomap::point3d_list& cloud, const double& spacing, const double& r_multiple,
234  const octomath::Vector3& position, double& intensity, octomath::Vector3& gradient)
235 {
236  intensity = 0.f;
237  gradient = octomath::Vector3(0, 0, 0);
238 
239  const double r = r_multiple * spacing; // TODO magic number!
240  // double T = 0.5; // TODO magic number!
241 
242  const int nn = cloud.size();
243  if (nn == 0)
244  {
245  return false;
246  }
247 
248  // variables for Wyvill
249  double a = 0, b = 0, c = 0, r2 = 0, r4 = 0, r6 = 0, a1 = 0, b1 = 0, c1 = 0, a2 = 0, b2 = 0, c2 = 0;
250  const bool wyvill = true;
251 
252  for (const octomath::Vector3& v : cloud)
253  {
254  if (wyvill)
255  {
256  r2 = r * r;
257  r4 = r2 * r2;
258  r6 = r4 * r2;
259  a = -4.0 / 9.0;
260  b = 17.0 / 9.0;
261  c = -22.0 / 9.0;
262  a1 = a / r6;
263  b1 = b / r4;
264  c1 = c / r2;
265  a2 = 6 * a1;
266  b2 = 4 * b1;
267  c2 = 2 * c1;
268  }
269  else
270  {
271  RCLCPP_ERROR(LOGGER, "This should not be called!");
272  }
273 
274  double f_val = 0;
275  octomath::Vector3 f_grad(0, 0, 0);
276 
277  octomath::Vector3 pos = position - v;
278  const double r = pos.norm();
279  pos = pos * (1.0 / r);
280  if (r > r) // must skip points outside valid bounds.
281  {
282  continue;
283  }
284  const double r2 = r * r;
285  const double r3 = r * r2;
286  const double r4 = r2 * r2;
287  const double r5 = r3 * r2;
288  const double r6 = r3 * r3;
289 
290  if (wyvill)
291  {
292  f_val = (a1 * r6 + b1 * r4 + c1 * r2 + 1);
293  f_grad = pos * (a2 * r5 + b2 * r3 + c2 * r);
294  }
295  else
296  {
297  RCLCPP_ERROR(LOGGER, "This should not be called!");
298  const double r_scaled = r / r;
299  // TODO still need to address the scaling...
300  f_val = pow((1 - r_scaled), 4) * (4 * r_scaled + 1);
301  f_grad = pos * (-4.0 / r * pow(1.0 - r_scaled, 3) * (4.0 * r_scaled + 1.0) + 4.0 / r * pow(1 - r_scaled, 4));
302  }
303 
304  // TODO: The whole library should be overhauled to follow the "gradient points out"
305  // convention of implicit functions.
306  intensity += f_val;
307  gradient += f_grad;
308  }
309  // implicit surface gradient convention points out, so we flip it.
310  gradient *= -1.0;
311  return true; // it worked
312 }
bool getMetaballSurfaceProperties(const octomap::point3d_list &cloud, const double &spacing, const double &iso_value, const double &r_multiple, const octomath::Vector3 &contact_point, octomath::Vector3 &normal, double &depth, bool estimate_depth)
bool sampleCloud(const octomap::point3d_list &cloud, const double &spacing, const double &r_multiple, const octomath::Vector3 &position, double &intensity, octomath::Vector3 &gradient)
bool findSurface(const octomap::point3d_list &cloud, const double &spacing, const double &iso_value, const double &r_multiple, const octomath::Vector3 &seed, octomath::Vector3 &surface_point, octomath::Vector3 &normal)
int refineContactNormals(const World::ObjectConstPtr &object, CollisionResult &res, double cell_bbx_search_distance=1.0, double allowed_angle_divergence=0.0, bool estimate_depth=false, double iso_value=0.5, double metaball_radius_multiple=1.5)
Re-proceses contact normals for an octomap by estimating a metaball iso-surface using the centers of ...
Vec3fX< details::Vec3Data< double > > Vector3d
Definition: fcl_compat.h:89
p
Definition: pick.py:62
a
Definition: plan.py:54
r
Definition: plan.py:56
Representation of a collision checking result.
ContactMap contacts
A map returning the pairs of body ids in contact, plus their contact details.
std::size_t contact_count
Number of contacts returned.