planar_joint_model.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2013, Ioan A. Sucan
5  * Copyright (c) 2008-2013, Willow Garage, Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * * Neither the name of the Willow Garage nor the names of its
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *********************************************************************/
35 
36 /* Author: Ioan Sucan */
37 
40 #include <angles/angles.h>
41 #include <boost/math/constants/constants.hpp>
42 #include <limits>
43 #include <cmath>
44 
45 namespace moveit
46 {
47 namespace core
48 {
49 PlanarJointModel::PlanarJointModel(const std::string& name)
50  : JointModel(name), angular_distance_weight_(1.0), motion_model_(HOLONOMIC), min_translational_distance_(1e-5)
51 {
52  type_ = PLANAR;
53 
54  local_variable_names_.push_back("x");
55  local_variable_names_.push_back("y");
56  local_variable_names_.push_back("theta");
57  for (int i = 0; i < 3; ++i)
58  {
59  variable_names_.push_back(name_ + "/" + local_variable_names_[i]);
60  variable_index_map_[variable_names_.back()] = i;
61  }
62 
63  variable_bounds_.resize(3);
64  variable_bounds_[0].position_bounded_ = true;
65  variable_bounds_[1].position_bounded_ = true;
66  variable_bounds_[2].position_bounded_ = false;
67 
68  variable_bounds_[0].min_position_ = -std::numeric_limits<double>::infinity();
69  variable_bounds_[0].max_position_ = std::numeric_limits<double>::infinity();
70  variable_bounds_[1].min_position_ = -std::numeric_limits<double>::infinity();
71  variable_bounds_[1].max_position_ = std::numeric_limits<double>::infinity();
72  variable_bounds_[2].min_position_ = -boost::math::constants::pi<double>();
73  variable_bounds_[2].max_position_ = boost::math::constants::pi<double>();
74 
75  computeVariableBoundsMsg();
76 }
77 
78 unsigned int PlanarJointModel::getStateSpaceDimension() const
79 {
80  return 3;
81 }
82 
83 double PlanarJointModel::getMaximumExtent(const Bounds& other_bounds) const
84 {
85  double dx = other_bounds[0].max_position_ - other_bounds[0].min_position_;
86  double dy = other_bounds[1].max_position_ - other_bounds[1].min_position_;
87  return sqrt(dx * dx + dy * dy) + boost::math::constants::pi<double>() * angular_distance_weight_;
88 }
89 
90 void PlanarJointModel::getVariableDefaultPositions(double* values, const Bounds& bounds) const
91 {
92  for (unsigned int i = 0; i < 2; ++i)
93  {
94  // if zero is a valid value
95  if (bounds[i].min_position_ <= 0.0 && bounds[i].max_position_ >= 0.0)
96  values[i] = 0.0;
97  else
98  values[i] = (bounds[i].min_position_ + bounds[i].max_position_) / 2.0;
99  }
100  values[2] = 0.0;
101 }
102 
103 void PlanarJointModel::getVariableRandomPositions(random_numbers::RandomNumberGenerator& rng, double* values,
104  const Bounds& bounds) const
105 {
106  if (bounds[0].max_position_ >= std::numeric_limits<double>::infinity() ||
107  bounds[0].min_position_ <= -std::numeric_limits<double>::infinity())
108  values[0] = 0.0;
109  else
110  values[0] = rng.uniformReal(bounds[0].min_position_, bounds[0].max_position_);
111  if (bounds[1].max_position_ >= std::numeric_limits<double>::infinity() ||
112  bounds[1].min_position_ <= -std::numeric_limits<double>::infinity())
113  values[1] = 0.0;
114  else
115  values[1] = rng.uniformReal(bounds[1].min_position_, bounds[1].max_position_);
116  values[2] = rng.uniformReal(bounds[2].min_position_, bounds[2].max_position_);
117 }
118 
119 void PlanarJointModel::getVariableRandomPositionsNearBy(random_numbers::RandomNumberGenerator& rng, double* values,
120  const Bounds& bounds, const double* seed,
121  const double distance) const
122 {
123  if (bounds[0].max_position_ >= std::numeric_limits<double>::infinity() ||
124  bounds[0].min_position_ <= -std::numeric_limits<double>::infinity())
125  values[0] = 0.0;
126  else
127  values[0] = rng.uniformReal(std::max(bounds[0].min_position_, seed[0] - distance),
128  std::min(bounds[0].max_position_, seed[0] + distance));
129  if (bounds[1].max_position_ >= std::numeric_limits<double>::infinity() ||
130  bounds[1].min_position_ <= -std::numeric_limits<double>::infinity())
131  values[1] = 0.0;
132  else
133  values[1] = rng.uniformReal(std::max(bounds[1].min_position_, seed[1] - distance),
134  std::min(bounds[1].max_position_, seed[1] + distance));
135 
136  double da = angular_distance_weight_ * distance;
137  // limit the sampling range to 2pi to work correctly even if the distance is very large
138  if (da > boost::math::constants::pi<double>())
139  da = boost::math::constants::pi<double>();
140  values[2] = rng.uniformReal(seed[2] - da, seed[2] + da);
141  normalizeRotation(values);
142 }
143 
144 void computeTurnDriveTurnGeometry(const double* from, const double* to, const double min_translational_distance,
145  double& dx, double& dy, double& initial_turn, double& drive_angle, double& final_turn)
146 {
147  dx = to[0] - from[0];
148  dy = to[1] - from[1];
149  // If the translational distance between from & to states is very small, it will cause an unnecessary rotation since
150  // the robot will try to do the following rather than rotating directly to the orientation of `to` state
151  // 1- Align itself with the line connecting the origin of both states
152  // 2- Move to the origin of `to` state
153  // 3- Rotate so it have the same orientation as `to` state
154  // Example: from=[0.0, 0.0, 0.0] - to=[1e-31, 1e-31, -130°]
155  // here the robot will: rotate 45° -> move to the origin of `to` state -> rotate -175°, rather than rotating directly
156  // to -130°
157  // to fix this we added a joint property (default value is 1e-5) and make the movement pure rotation if the
158  // translational distance is less than this number
159  const double angle_straight_diff = std::hypot(dx, dy) > min_translational_distance ?
160  angles::shortest_angular_distance(from[2], std::atan2(dy, dx)) :
161  0.0;
162  const double angle_backward_diff =
163  angles::normalize_angle(angle_straight_diff - boost::math::constants::pi<double>());
164  const double move_straight_cost =
165  std::abs(angle_straight_diff) + std::abs(angles::shortest_angular_distance(from[2] + angle_straight_diff, to[2]));
166  const double move_backward_cost =
167  std::abs(angle_backward_diff) + std::abs(angles::shortest_angular_distance(from[2] + angle_backward_diff, to[2]));
168  if (move_straight_cost <= move_backward_cost)
169  {
170  initial_turn = angle_straight_diff;
171  }
172  else
173  {
174  initial_turn = angle_backward_diff;
175  }
176  drive_angle = from[2] + initial_turn;
177  final_turn = angles::shortest_angular_distance(drive_angle, to[2]);
178 }
179 
180 void PlanarJointModel::interpolate(const double* from, const double* to, const double t, double* state) const
181 {
182  if (motion_model_ == HOLONOMIC)
183  {
184  // interpolate position
185  state[0] = from[0] + (to[0] - from[0]) * t;
186  state[1] = from[1] + (to[1] - from[1]) * t;
187 
188  // interpolate angle
189  double diff = to[2] - from[2];
190  if (fabs(diff) <= boost::math::constants::pi<double>())
191  state[2] = from[2] + diff * t;
192  else
193  {
194  if (diff > 0.0)
195  diff = 2.0 * boost::math::constants::pi<double>() - diff;
196  else
197  diff = -2.0 * boost::math::constants::pi<double>() - diff;
198  state[2] = from[2] - diff * t;
199  // input states are within bounds, so the following check is sufficient
200  if (state[2] > boost::math::constants::pi<double>())
201  state[2] -= 2.0 * boost::math::constants::pi<double>();
202  else if (state[2] < -boost::math::constants::pi<double>())
203  state[2] += 2.0 * boost::math::constants::pi<double>();
204  }
205  }
206  else if (motion_model_ == DIFF_DRIVE)
207  {
208  double dx, dy, initial_turn, drive_angle, final_turn;
209  computeTurnDriveTurnGeometry(from, to, min_translational_distance_, dx, dy, initial_turn, drive_angle, final_turn);
210 
211  // approximate cost (distance traveled) doing initial turn
212  double initial_d = fabs(initial_turn) * angular_distance_weight_;
213  // approximate cost (distance traveled) doing straight drive
214  double drive_d = hypot(dx, dy);
215  // approximate cost (distance traveled) doing final turn
216  double final_d = fabs(final_turn) * angular_distance_weight_;
217 
218  // total cost of executing manuever
219  double total_d = initial_d + drive_d + final_d;
220 
221  // If the difference between `from` and `to` is so low that it is within floating point arithmetic error
222  // or if `from == to`, the following operations will result in nan
223  // Just set the return state to target state and don't interpolate.
224  if (total_d < std::numeric_limits<float>::epsilon())
225  {
226  state[0] = to[0];
227  state[1] = to[1];
228  state[2] = to[2];
229  return;
230  }
231 
232  // fraction of cost for each segment
233  double initial_frac = initial_d / total_d;
234  double drive_frac = drive_d / total_d;
235  double final_frac = final_d / total_d;
236 
237  double percent;
238 
239  // If the current time step is still in the initial rotation phase.
240  if (t <= initial_frac)
241  {
242  percent = t / initial_frac;
243  state[0] = from[0];
244  state[1] = from[1];
245  state[2] = from[2] + initial_turn * percent;
246  }
247  // If the current time step is doing the driving phase.
248  else if (t <= initial_frac + drive_frac)
249  {
250  percent = (t - initial_frac) / drive_frac;
251  state[0] = from[0] + dx * percent;
252  state[1] = from[1] + dy * percent;
253  state[2] = drive_angle;
254  }
255  // If the current time step is in the final rotation phase.
256  else
257  {
258  percent = (t - initial_frac - drive_frac) / final_frac;
259  state[0] = to[0];
260  state[1] = to[1];
261  state[2] = drive_angle + final_turn * percent;
262  }
263  }
264 }
265 
266 double PlanarJointModel::distance(const double* values1, const double* values2) const
267 {
268  if (motion_model_ == HOLONOMIC)
269  {
270  double dx = values1[0] - values2[0];
271  double dy = values1[1] - values2[1];
272 
273  double d = fabs(values1[2] - values2[2]);
274  d = (d > boost::math::constants::pi<double>()) ? 2.0 * boost::math::constants::pi<double>() - d : d;
275  return sqrt(dx * dx + dy * dy) + angular_distance_weight_ * d;
276  }
277  else if (motion_model_ == DIFF_DRIVE)
278  {
279  double dx, dy, initial_turn, drive_angle, final_turn;
280  computeTurnDriveTurnGeometry(values1, values2, min_translational_distance_, dx, dy, initial_turn, drive_angle,
281  final_turn);
282  return hypot(dx, dy) + angular_distance_weight_ * (fabs(initial_turn) + fabs(final_turn));
283  }
284  return 0.0;
285 }
286 
287 bool PlanarJointModel::satisfiesPositionBounds(const double* values, const Bounds& bounds, double margin) const
288 {
289  for (unsigned int i = 0; i < 3; ++i)
290  if (values[i] < bounds[i].min_position_ - margin || values[i] > bounds[i].max_position_ + margin)
291  return false;
292  return true;
293 }
294 
295 bool PlanarJointModel::normalizeRotation(double* values) const
296 {
297  double& v = values[2];
298  if (v >= -boost::math::constants::pi<double>() && v <= boost::math::constants::pi<double>())
299  return false;
300  v = fmod(v, 2.0 * boost::math::constants::pi<double>());
301  if (v < -boost::math::constants::pi<double>())
302  v += 2.0 * boost::math::constants::pi<double>();
303  else if (v > boost::math::constants::pi<double>())
304  v -= 2.0 * boost::math::constants::pi<double>();
305  return true;
306 }
307 
308 bool PlanarJointModel::enforcePositionBounds(double* values, const Bounds& bounds) const
309 {
310  bool result = normalizeRotation(values);
311  for (unsigned int i = 0; i < 2; ++i)
312  {
313  if (values[i] < bounds[i].min_position_)
314  {
315  values[i] = bounds[i].min_position_;
316  result = true;
317  }
318  else if (values[i] > bounds[i].max_position_)
319  {
320  values[i] = bounds[i].max_position_;
321  result = true;
322  }
323  }
324  return result;
325 }
326 
327 void PlanarJointModel::computeTransform(const double* joint_values, Eigen::Isometry3d& transf) const
328 {
329  transf = Eigen::Isometry3d(Eigen::Translation3d(joint_values[0], joint_values[1], 0.0) *
330  Eigen::AngleAxisd(joint_values[2], Eigen::Vector3d::UnitZ()));
331 }
332 
333 void PlanarJointModel::computeVariablePositions(const Eigen::Isometry3d& transf, double* joint_values) const
334 {
335  joint_values[0] = transf.translation().x();
336  joint_values[1] = transf.translation().y();
337 
338  ASSERT_ISOMETRY(transf) // unsanitized input, could contain a non-isometry
339  Eigen::Quaterniond q(transf.linear());
340  // taken from Bullet
341  double s_squared = 1.0 - (q.w() * q.w());
342  if (s_squared < 10.0 * std::numeric_limits<double>::epsilon())
343  joint_values[2] = 0.0;
344  else
345  {
346  double s = 1.0 / sqrt(s_squared);
347  joint_values[2] = (acos(q.w()) * 2.0f) * (q.z() * s);
348  }
349 }
350 
351 } // end of namespace core
352 } // end of namespace moveit
angles::shortest_angular_distance
static double shortest_angular_distance(double from, double to)
s
XmlRpcServer s
check_isometry.h
random_numbers::RandomNumberGenerator::uniformReal
double uniformReal(double lower_bound, double upper_bound)
angles::normalize_angle
def normalize_angle(angle)
planar_joint_model.h
name
std::string name
ASSERT_ISOMETRY
#define ASSERT_ISOMETRY(transform)
moveit::core::PlanarJointModel::PlanarJointModel
PlanarJointModel(const std::string &name)
Definition: planar_joint_model.cpp:115
setup.d
d
Definition: setup.py:8
random_numbers::RandomNumberGenerator
moveit
Main namespace for MoveIt.
Definition: background_processing.h:46
values
std::vector< double > values
pr2_arm_kinematics::distance
double distance(const urdf::Pose &transform)
Definition: pr2_arm_ik.h:86
t
geometry_msgs::TransformStamped t
moveit::core::computeTurnDriveTurnGeometry
void computeTurnDriveTurnGeometry(const double *from, const double *to, const double min_translational_distance, double &dx, double &dy, double &initial_turn, double &drive_angle, double &final_turn)
Compute the geometry to turn toward the target point, drive straight and then turn to target orientat...
Definition: planar_joint_model.cpp:210
angles.h


moveit_core
Author(s): Ioan Sucan , Sachin Chitta , Acorn Pooley
autogenerated on Sun Mar 3 2024 03:23:35