AStarPathPlanner.cpp
Go to the documentation of this file.
1 /*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package RobotComponents::PathPlanner
17 * @author Raphael Grimm ( raphael dot grimm at kit dot edu )
18 * @date 2015 Humanoids Group, H2T, KIT
19 * @license http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22 
23 #include "AStarPathPlanner.h"
24 
25 #include <cmath>
26 #include <map>
27 
28 armarx::Vector3IBase
29 operator+(const armarx::Vector3IBase& lhs, const armarx::Vector3IBase& rhs)
30 {
31  return armarx::Vector3IBase{lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z};
32 }
33 
35  AStarPathPlannerBase(250.f, //tStepSize
36  {0.f, 5500.f}, //xBoundingRange
37  {0.f, 11000.f}, //yBoundingRange
38  {
39  {+1, 0, 0}, //step in x
40  {-1, 0, 0}, //step in -x
41  {0, +1, 0}, //step in x
42  {0, -1, 0}, //step in -y
43  {0, 0, +1}, //rotate +
44  {0, 0, -1}, //rotate -
45  {+1, +1, 0}, //step in +x, +y
46  {+1, -1, 0}, //step in +x, -y
47  {-1, +1, 0}, //step in -x, +y
48  {-1, -1, 0} //step in -x, -y
49  }, //neighbourDeltas
50  100000000.f, //rotationFactorForHeuristic
51  1.f //distanceFactorForHeuristic
52  ),
53  stepSizeDeg{5}
54 {
55 }
56 
57 //slice impl from PathPlannerBase
58 void
60 {
61  if (step < 0)
62  {
63  std::stringstream s;
64  s << "Invalid translation step size: " << step << " < 0.0";
65  ARMARX_ERROR_S << s.str();
66  throw armarx::InvalidArgumentException{s.str()};
67  }
68 
69  tStepSize = step;
70 }
71 
72 void
74 {
75  //step is in rad
76  //we need values in [0, 360) but enforce this at a later point
77  //(we add stepSizeDeg so we have to normalize the result to [0, 360) anyway.
78  stepSizeDeg = static_cast<int>(step / M_PI * 180);
79 }
80 
81 void
82 armarx::AStarPathPlanner::setBoundingXRange(const ::armarx::FloatRange& range,
83  const ::Ice::Current&)
84 {
85  if (range.max < range.min)
86  {
87  std::stringstream s;
88  s << "Invalid bounding range for x dimension: min = " << range.min
89  << ", max = " << range.max;
90  ARMARX_ERROR_S << s.str();
91  throw armarx::InvalidArgumentException{s.str()};
92  }
93 
94  xBoundingRange = range;
95 }
96 
97 void
98 armarx::AStarPathPlanner::setBoundingYRange(const ::armarx::FloatRange& range,
99  const ::Ice::Current&)
100 {
101  if (range.max < range.min)
102  {
103  std::stringstream s;
104  s << "Invalid bounding range for y dimension: min = " << range.min
105  << ", max = " << range.max;
106  ARMARX_ERROR_S << s.str();
107  throw armarx::InvalidArgumentException{s.str()};
108  }
109 
110  yBoundingRange = range;
111 }
112 
113 void
114 armarx::AStarPathPlanner::setNeighbourSteps(const ::armarx::Vector3IBaseList& neighbours,
115  const ::Ice::Current&)
116 {
117  neighbourDeltas = neighbours;
118 }
119 
120 void
122 {
123  rotationFactorForHeuristic = factor;
124 }
125 
126 void
128 {
129  distanceFactorForHeuristic = factor;
130 }
131 
132 float
134 {
135  const Eigen::Vector3f delta = to.toEigen() - from.toEigen();
136  return distanceFactorForHeuristic * std::hypot(delta(0), delta(1)) +
137  rotationFactorForHeuristic * std::abs(delta(2));
138 }
139 
140 float
141 armarx::AStarPathPlanner::heuristic(const armarx::Vector3IBase& from,
142  const armarx::Vector3IBase& to) const
143 {
144  return distanceFactorForHeuristic * std::hypot(to.x - from.x, to.y - from.y) +
145  rotationFactorForHeuristic * std::abs(to.z - from.z);
146 }
147 
148 //slice impl from PathPlannerBase
149 ::armarx::Vector3BaseList
150 armarx::AStarPathPlanner::getPath(const ::armarx::Vector3BasePtr& from,
151  const ::armarx::Vector3BasePtr& to,
152  const ::Ice::Current&) const
153 {
154  /*
155  * in this context the values of a Vector3IBase are interpreted as:
156  * x => steps of size tStepSize in x direction from from.x
157  * y => steps of size tStepSize in y direction from from.y
158  * z => rotation in deg (in [0, 360)) with 0 <=> from->z
159  * so (0, 0, 0) is the starting point from
160  */
161 
162  if (!(isPositionValid(Vector3{from->x, from->y, from->z}) &&
163  isPositionValid(Vector3{to->x, to->y, to->z})))
164  {
165  return Vector3BaseList{};
166  }
167 
168  assert(from);
169  assert(to);
170  assert(agent);
171 
172  ARMARX_VERBOSE << "Planning path from (" << from->x << ", " << from->y << ", " << from->z
173  << ") to (" << to->x << ", " << to->y << ", " << to->z << ")";
174 
175  auto toGlobal = [&](const armarx::Vector3IBase& node)
176  {
177  return armarx::Vector3{from->x + node.x * tStepSize,
178  from->y + node.y * tStepSize,
179  from->z + /*deg to rad*/ static_cast<float>(node.z * M_PI / 180)};
180  };
181 
182  auto getNeighbours = [&](const armarx::Vector3IBase& node)
183  {
184  armarx::Vector3IBaseList neighbours;
185 
186  for (const auto& delta : neighbourDeltas)
187  {
188  armarx::Vector3IBase neighbour{
189  node.x + delta.x,
190  node.y + delta.y,
191  (((node.z + delta.z * stepSizeDeg) % 360) + 360) %
192  360 /*is rotation in deg (in [0, 360))*/
193  };
194  const armarx::Vector3 globalNeighbour = toGlobal(neighbour);
195 
196  if (globalNeighbour.x <= xBoundingRange.max &&
197  globalNeighbour.x >= xBoundingRange.min &&
198  globalNeighbour.y <= yBoundingRange.max &&
199  globalNeighbour.y >= yBoundingRange.min && isPositionValid(globalNeighbour))
200  {
201  neighbours.push_back(neighbour);
202  }
203  }
204 
205  return neighbours;
206  };
207 
208  // auto drawDebugPoint = [&](const armarx::Vector3IBase & point, armarx::DrawColor color)
209  // {
210  // //visualize current
211  // std::stringstream s;
212  // s << "node" << point.x << "_" << point.y << "_" << point.z;
213  // armarx::Vector3BasePtr positionToDraw {new Vector3{toGlobal(point).toEigen()}};
214  // positionToDraw->z = point.z * 10;
215  // debugDrawer->setSphereDebugLayerVisu(s.str(), positionToDraw, color, 30.f);
216  // };
217 
218  const armarx::Vector3IBase start{0, 0, 0};
219 
220  armarx::Vector3 fromToDelta{to->x - from->x, to->y - from->y, to->z - from->z};
221 
222  armarx::Vector3IBase goal{static_cast<int>(std::lround(fromToDelta.x / tStepSize)),
223  static_cast<int>(std::lround(fromToDelta.y / tStepSize)),
224  ((static_cast<int>(fromToDelta.z / M_PI * 180) % 360) + 360) %
225  360}; //we need values in [0, 360)
226 
227  //if goal.z == 10, start.z == 0 and stepSizeDeg == 20 we can't reach the goal.z value => we have to round goal.z
228  int goalZMod = goal.z % stepSizeDeg;
229  goal.z = goal.z - goalZMod; //floor
230  goal.z += ((goalZMod >= stepSizeDeg / 2) ? stepSizeDeg : 0); //round up if needed
231 
232  const armarx::Vector3Ptr globalGoalPtr = armarx::Vector3Ptr::dynamicCast(to);
233  assert(globalGoalPtr);
234 
235  ::armarx::Vector3BaseList path{};
236  std::vector<armarx::Vector3IBase> openSet{};
237  std::vector<armarx::Vector3IBase> closedSet{};
238 
239  openSet.push_back(start);
240 
241  std::map<armarx::Vector3IBase, float> fScore;
242  std::map<armarx::Vector3IBase, float> gScore;
243  gScore[start] = 0.f;
244  fScore[start] = gScore.at(start) + heuristic(toGlobal(start), *globalGoalPtr);
245 
246  std::map<armarx::Vector3IBase, armarx::Vector3IBase> cameFrom;
247  cameFrom[goal] = start; //in case start==goal
248 
249  ARMARX_VERBOSE << "Setup done. Star planning...";
250 
251  // drawDebugPoint(start, armarx::DrawColor {0.f, 1.f, 0.f, 1.f});
252  // drawDebugPoint(goal, armarx::DrawColor {0.f, 0.f, 1.f, 1.f});
253 
254  while (!openSet.empty())
255  {
256  //find best
257  auto currentIT =
258  std::min_element(openSet.begin(),
259  openSet.end(),
260  [&](const armarx::Vector3IBase& a, const armarx::Vector3IBase& b)
261  { return fScore.at(a) < fScore.at(b); });
262  assert(currentIT != openSet.end());
263  const armarx::Vector3IBase current = *currentIT;
264 
265  //check if done
266  if ((current.x == goal.x) && (current.y == goal.y) && (current.z == goal.z))
267  {
268  ARMARX_VERBOSE << "Assembling path";
269 
270  armarx::Vector3IBase cameFromNode = goal;
271 
272  while (cameFromNode != start)
273  {
274  armarx::Vector3BasePtr globalCameFromNodePtr =
275  armarx::Vector3BasePtr{new armarx::Vector3{toGlobal(cameFromNode)}};
276  path.insert(path.begin(), globalCameFromNodePtr);
277  cameFromNode = cameFrom.at(cameFromNode);
278  }
279 
280  path.insert(path.begin(), from);
281  break;
282  }
283 
284  openSet.erase(currentIT);
285  closedSet.push_back(current);
286 
287  // drawDebugPoint(current, armarx::DrawColor {1.f, 0.f, 1.f, 1.f});
288 
289  //update
290  for (const armarx::Vector3IBase& neighbour : getNeighbours(current))
291  {
292  if (std::find(closedSet.begin(), closedSet.end(), neighbour) != closedSet.end())
293  {
294  continue;
295  }
296 
297  //const armarx::Vector3 globalCurrent = toGlobal(current);
298  //const armarx::Vector3 globalNeighbour = toGlobal(neighbour);
299 
300  //float tentativeGScore = gScore.at(current) + heuristic(globalCurrent, globalNeighbour);
301  float tentativeGScore = gScore.at(current) + heuristic(current, neighbour);
302  bool notInOS = std::find(openSet.begin(), openSet.end(), neighbour) == openSet.end();
303 
304  if (notInOS || tentativeGScore < gScore.at(neighbour))
305  {
306  cameFrom[neighbour] = current;
307  gScore[neighbour] = tentativeGScore;
308  //fScore[neighbour] = tentativeGScore + heuristic(globalNeighbour, *globalGoalPtr);
309  fScore[neighbour] = tentativeGScore + heuristic(neighbour, goal);
310 
311  if (notInOS)
312  {
313  // drawDebugPoint(neighbour, armarx::DrawColor {1.f, 0.f, 0.f, 0.5f});
314 
315  openSet.push_back(neighbour);
316  }
317  }
318  }
319  }
320 
321  return path;
322 }
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:187
armarx::VariantType::Float
const VariantTypeId Float
Definition: Variant.h:919
armarx::AStarPathPlanner::setDistanceFactorForHeuristic
void setDistanceFactorForHeuristic(::Ice::Float factor, const ::Ice::Current &=Ice::emptyCurrent) override
Definition: AStarPathPlanner.cpp:127
armarx::AStarPathPlanner::setRotationStepSize
void setRotationStepSize(::Ice::Float step, const ::Ice::Current &=Ice::emptyCurrent) override
Definition: AStarPathPlanner.cpp:73
armarx::AStarPathPlanner::getPath
::armarx::Vector3BaseList getPath(const ::armarx::Vector3BasePtr &from, const ::armarx::Vector3BasePtr &to, const ::Ice::Current &=Ice::emptyCurrent) const override
Definition: AStarPathPlanner.cpp:150
IceInternal::Handle< Vector3 >
armarx::ctrlutil::a
double a(double t, double a0, double j)
Definition: CtrlUtil.h:45
armarx::abs
std::vector< T > abs(const std::vector< T > &v)
Definition: VectorHelpers.h:281
M_PI
#define M_PI
Definition: MathTools.h:17
ARMARX_ERROR_S
#define ARMARX_ERROR_S
Definition: Logging.h:216
armarx::armem::locations::toGlobal
void toGlobal(armarx::navigation::location::arondto::Location &framedLoc, const Eigen::Matrix4f &framedToGlobal)
Definition: frame_conversions.h:14
armarx::Vector3
The Vector3 class.
Definition: Pose.h:112
operator+
armarx::Vector3IBase operator+(const armarx::Vector3IBase &lhs, const armarx::Vector3IBase &rhs)
Definition: AStarPathPlanner.cpp:29
armarx::AStarPathPlanner::AStarPathPlanner
AStarPathPlanner()
Definition: AStarPathPlanner.cpp:34
armarx::AStarPathPlanner::setNeighbourSteps
void setNeighbourSteps(const ::armarx::Vector3IBaseList &neighbours, const ::Ice::Current &=Ice::emptyCurrent) override
Definition: AStarPathPlanner.cpp:114
armarx::Vector3::toEigen
virtual Eigen::Vector3f toEigen() const
Definition: Pose.cpp:134
armarx::AStarPathPlanner::setRotationFactorForHeuristic
void setRotationFactorForHeuristic(::Ice::Float factor, const ::Ice::Current &=Ice::emptyCurrent) override
Definition: AStarPathPlanner.cpp:121
AStarPathPlanner.h
armarx::AStarPathPlanner::heuristic
float heuristic(const armarx::Vector3 &from, const armarx::Vector3 &to) const
The heuristic used for A*.
Definition: AStarPathPlanner.cpp:133
armarx::AStarPathPlanner::setBoundingXRange
void setBoundingXRange(const ::armarx::FloatRange &range, const ::Ice::Current &=Ice::emptyCurrent) override
Definition: AStarPathPlanner.cpp:82
armarx::AStarPathPlanner::setTranslationtStepSize
void setTranslationtStepSize(::Ice::Float step, const ::Ice::Current &=Ice::emptyCurrent) override
Definition: AStarPathPlanner.cpp:59
armarx::ctrlutil::s
double s(double t, double s0, double v0, double a0, double j)
Definition: CtrlUtil.h:33
armarx::AStarPathPlanner::setBoundingYRange
void setBoundingYRange(const ::armarx::FloatRange &range, const ::Ice::Current &=Ice::emptyCurrent) override
Definition: AStarPathPlanner.cpp:98