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 operator +(const armarx::Vector3IBase& lhs, const armarx::Vector3IBase& rhs)
29 {
30  return armarx::Vector3IBase {lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z};
31 }
32 
33 
35  AStarPathPlannerBase(
36  250.f, //tStepSize
37 {
38  0.f, 5500.f
39 }, //xBoundingRange
40 {0.f, 11000.f}, //yBoundingRange
41 {
42  { +1, 0, 0}, //step in x
43  { -1, 0, 0}, //step in -x
44  {0, +1, 0}, //step in x
45  {0, -1, 0}, //step in -y
46  {0, 0, +1}, //rotate +
47  {0, 0, -1}, //rotate -
48  { +1, +1, 0}, //step in +x, +y
49  { +1, -1, 0}, //step in +x, -y
50  { -1, +1, 0}, //step in -x, +y
51  { -1, -1, 0} //step in -x, -y
52 }, //neighbourDeltas
53 100000000.f, //rotationFactorForHeuristic
54 1.f //distanceFactorForHeuristic
55 ),
56 stepSizeDeg {5}
57 {
58 }
59 
60 
61 //slice impl from PathPlannerBase
63 {
64  if (step < 0)
65  {
66  std::stringstream s;
67  s << "Invalid translation step size: " << step << " < 0.0";
68  ARMARX_ERROR_S << s.str();
69  throw armarx::InvalidArgumentException {s.str()};
70  }
71 
72  tStepSize = step;
73 }
74 
75 void armarx::AStarPathPlanner::setRotationStepSize(::Ice::Float step, const ::Ice::Current&)
76 {
77  //step is in rad
78  //we need values in [0, 360) but enforce this at a later point
79  //(we add stepSizeDeg so we have to normalize the result to [0, 360) anyway.
80  stepSizeDeg = static_cast<int>(step / M_PI * 180);
81 
82 }
83 
84 void armarx::AStarPathPlanner::setBoundingXRange(const ::armarx::FloatRange& range, const ::Ice::Current&)
85 {
86  if (range.max < range.min)
87  {
88  std::stringstream s;
89  s << "Invalid bounding range for x dimension: min = " << range.min << ", max = " << range.max;
90  ARMARX_ERROR_S << s.str();
91  throw armarx::InvalidArgumentException {s.str()};
92  }
93 
94  xBoundingRange = range;
95 }
96 
97 void armarx::AStarPathPlanner::setBoundingYRange(const ::armarx::FloatRange& range, const ::Ice::Current&)
98 {
99  if (range.max < range.min)
100  {
101  std::stringstream s;
102  s << "Invalid bounding range for y dimension: min = " << range.min << ", max = " << range.max;
103  ARMARX_ERROR_S << s.str();
104  throw armarx::InvalidArgumentException {s.str()};
105  }
106 
107  yBoundingRange = range;
108 }
109 
110 void armarx::AStarPathPlanner::setNeighbourSteps(const ::armarx::Vector3IBaseList& neighbours, const ::Ice::Current&)
111 {
112  neighbourDeltas = neighbours;
113 }
114 
116 {
117  rotationFactorForHeuristic = factor;
118 }
119 
121 {
122  distanceFactorForHeuristic = factor;
123 }
124 
126 {
127  const Eigen::Vector3f delta = to.toEigen() - from.toEigen();
128  return distanceFactorForHeuristic * std::hypot(delta(0), delta(1)) + rotationFactorForHeuristic * std::abs(delta(2));
129 }
130 
131 float armarx::AStarPathPlanner::heuristic(const armarx::Vector3IBase& from, const armarx::Vector3IBase& to) const
132 {
133  return distanceFactorForHeuristic * std::hypot(to.x - from.x, to.y - from.y) + rotationFactorForHeuristic * std::abs(to.z - from.z);
134 }
135 
136 //slice impl from PathPlannerBase
137 ::armarx::Vector3BaseList armarx::AStarPathPlanner::getPath(const ::armarx::Vector3BasePtr& from, const ::armarx::Vector3BasePtr& to, const ::Ice::Current&) const
138 {
139  /*
140  * in this context the values of a Vector3IBase are interpreted as:
141  * x => steps of size tStepSize in x direction from from.x
142  * y => steps of size tStepSize in y direction from from.y
143  * z => rotation in deg (in [0, 360)) with 0 <=> from->z
144  * so (0, 0, 0) is the starting point from
145  */
146 
147  if (!(isPositionValid(Vector3 {from->x, from->y, from->z}) && isPositionValid(Vector3 {to->x, to->y, to->z})))
148  {
149  return Vector3BaseList {};
150  }
151 
152  assert(from);
153  assert(to);
154  assert(agent);
155 
156  ARMARX_VERBOSE << "Planning path from (" << from->x << ", " << from->y << ", " << from->z << ") to (" << to->x << ", " << to->y << ", " << to->z << ")";
157 
158  auto toGlobal = [&](const armarx::Vector3IBase & node)
159  {
160  return armarx::Vector3
161  {
162  from->x + node.x * tStepSize,
163  from->y + node.y * tStepSize,
164  from->z + /*deg to rad*/ static_cast<float>(node.z* M_PI / 180)
165  };
166  };
167 
168  auto getNeighbours = [&](const armarx::Vector3IBase & node)
169  {
170  armarx::Vector3IBaseList neighbours;
171 
172  for (const auto& delta : neighbourDeltas)
173  {
174  armarx::Vector3IBase neighbour {node.x + delta.x, node.y + delta.y,
175  (((node.z + delta.z * stepSizeDeg) % 360) + 360) % 360 /*is rotation in deg (in [0, 360))*/
176  };
177  const armarx::Vector3 globalNeighbour = toGlobal(neighbour);
178 
179  if (globalNeighbour.x <= xBoundingRange.max &&
180  globalNeighbour.x >= xBoundingRange.min &&
181  globalNeighbour.y <= yBoundingRange.max &&
182  globalNeighbour.y >= yBoundingRange.min &&
183  isPositionValid(globalNeighbour))
184  {
185  neighbours.push_back(neighbour);
186  }
187  }
188 
189  return neighbours;
190  };
191 
192  // auto drawDebugPoint = [&](const armarx::Vector3IBase & point, armarx::DrawColor color)
193  // {
194  // //visualize current
195  // std::stringstream s;
196  // s << "node" << point.x << "_" << point.y << "_" << point.z;
197  // armarx::Vector3BasePtr positionToDraw {new Vector3{toGlobal(point).toEigen()}};
198  // positionToDraw->z = point.z * 10;
199  // debugDrawer->setSphereDebugLayerVisu(s.str(), positionToDraw, color, 30.f);
200  // };
201 
202  const armarx::Vector3IBase start
203  {
204  0, 0, 0
205  };
206 
207  armarx::Vector3 fromToDelta {to->x - from->x, to->y - from->y, to->z - from->z};
208 
209  armarx::Vector3IBase goal {static_cast<int>(std::lround(fromToDelta.x / tStepSize)),
210  static_cast<int>(std::lround(fromToDelta.y / tStepSize)),
211  ((static_cast<int>(fromToDelta.z / M_PI * 180) % 360) + 360) % 360
212  };//we need values in [0, 360)
213 
214  //if goal.z == 10, start.z == 0 and stepSizeDeg == 20 we can't reach the goal.z value => we have to round goal.z
215  int goalZMod = goal.z % stepSizeDeg;
216  goal.z = goal.z - goalZMod; //floor
217  goal.z += ((goalZMod >= stepSizeDeg / 2) ? stepSizeDeg : 0); //round up if needed
218 
219  const armarx::Vector3Ptr globalGoalPtr = armarx::Vector3Ptr::dynamicCast(to);
220  assert(globalGoalPtr);
221 
222  ::armarx::Vector3BaseList path {};
223  std::vector<armarx::Vector3IBase> openSet {};
224  std::vector<armarx::Vector3IBase> closedSet {};
225 
226  openSet.push_back(start);
227 
228  std::map<armarx::Vector3IBase, float> fScore;
229  std::map<armarx::Vector3IBase, float> gScore;
230  gScore[start] = 0.f;
231  fScore[start] = gScore.at(start) + heuristic(toGlobal(start), *globalGoalPtr);
232 
233  std::map<armarx::Vector3IBase, armarx::Vector3IBase> cameFrom;
234  cameFrom[goal] = start; //in case start==goal
235 
236  ARMARX_VERBOSE << "Setup done. Star planning...";
237 
238  // drawDebugPoint(start, armarx::DrawColor {0.f, 1.f, 0.f, 1.f});
239  // drawDebugPoint(goal, armarx::DrawColor {0.f, 0.f, 1.f, 1.f});
240 
241  while (!openSet.empty())
242  {
243  //find best
244  auto currentIT =
245  std::min_element(openSet.begin(), openSet.end(),
246  [&](const armarx::Vector3IBase & a, const armarx::Vector3IBase & b)
247  {
248  return fScore.at(a) < fScore.at(b);
249  }
250  );
251  assert(currentIT != openSet.end());
252  const armarx::Vector3IBase current = *currentIT;
253 
254  //check if done
255  if ((current.x == goal.x) && (current.y == goal.y) && (current.z == goal.z))
256  {
257  ARMARX_VERBOSE << "Assembling path";
258 
259  armarx::Vector3IBase cameFromNode = goal;
260 
261  while (cameFromNode != start)
262  {
263  armarx::Vector3BasePtr globalCameFromNodePtr = armarx::Vector3BasePtr {new armarx::Vector3{toGlobal(cameFromNode)}};
264  path.insert(path.begin(), globalCameFromNodePtr);
265  cameFromNode = cameFrom.at(cameFromNode);
266  }
267 
268  path.insert(path.begin(), from);
269  break;
270  }
271 
272  openSet.erase(currentIT);
273  closedSet.push_back(current);
274 
275  // drawDebugPoint(current, armarx::DrawColor {1.f, 0.f, 1.f, 1.f});
276 
277  //update
278  for (const armarx::Vector3IBase& neighbour : getNeighbours(current))
279  {
280  if (std::find(closedSet.begin(), closedSet.end(), neighbour) != closedSet.end())
281  {
282  continue;
283  }
284 
285  //const armarx::Vector3 globalCurrent = toGlobal(current);
286  //const armarx::Vector3 globalNeighbour = toGlobal(neighbour);
287 
288  //float tentativeGScore = gScore.at(current) + heuristic(globalCurrent, globalNeighbour);
289  float tentativeGScore = gScore.at(current) + heuristic(current, neighbour);
290  bool notInOS = std::find(openSet.begin(), openSet.end(), neighbour) == openSet.end();
291 
292  if (notInOS || tentativeGScore < gScore.at(neighbour))
293  {
294  cameFrom[neighbour] = current;
295  gScore[neighbour] = tentativeGScore;
296  //fScore[neighbour] = tentativeGScore + heuristic(globalNeighbour, *globalGoalPtr);
297  fScore[neighbour] = tentativeGScore + heuristic(neighbour, goal);
298 
299  if (notInOS)
300  {
301  // drawDebugPoint(neighbour, armarx::DrawColor {1.f, 0.f, 0.f, 0.5f});
302 
303  openSet.push_back(neighbour);
304  }
305  }
306  }
307  }
308 
309  return path;
310 }
311 
312 
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:180
armarx::VariantType::Float
const VariantTypeId Float
Definition: Variant.h:918
armarx::AStarPathPlanner::setDistanceFactorForHeuristic
void setDistanceFactorForHeuristic(::Ice::Float factor, const ::Ice::Current &=Ice::emptyCurrent) override
Definition: AStarPathPlanner.cpp:120
armarx::AStarPathPlanner::setRotationStepSize
void setRotationStepSize(::Ice::Float step, const ::Ice::Current &=Ice::emptyCurrent) override
Definition: AStarPathPlanner.cpp:75
armarx::AStarPathPlanner::getPath
::armarx::Vector3BaseList getPath(const ::armarx::Vector3BasePtr &from, const ::armarx::Vector3BasePtr &to, const ::Ice::Current &=Ice::emptyCurrent) const override
Definition: AStarPathPlanner.cpp:137
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:253
M_PI
#define M_PI
Definition: MathTools.h:17
ARMARX_ERROR_S
#define ARMARX_ERROR_S
Definition: Logging.h:209
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:28
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:110
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:115
AStarPathPlanner.h
armarx::AStarPathPlanner::heuristic
float heuristic(const armarx::Vector3 &from, const armarx::Vector3 &to) const
The heuristic used for A*.
Definition: AStarPathPlanner.cpp:125
armarx::AStarPathPlanner::setBoundingXRange
void setBoundingXRange(const ::armarx::FloatRange &range, const ::Ice::Current &=Ice::emptyCurrent) override
Definition: AStarPathPlanner.cpp:84
armarx::AStarPathPlanner::setTranslationtStepSize
void setTranslationtStepSize(::Ice::Float step, const ::Ice::Current &=Ice::emptyCurrent) override
Definition: AStarPathPlanner.cpp:62
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:97