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
28armarx::Vector3IBase
29operator+(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
58void
59armarx::AStarPathPlanner::setTranslationtStepSize(::Ice::Float step, const ::Ice::Current&)
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
72void
73armarx::AStarPathPlanner::setRotationStepSize(::Ice::Float step, const ::Ice::Current&)
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
81void
82armarx::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
97void
98armarx::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
113void
114armarx::AStarPathPlanner::setNeighbourSteps(const ::armarx::Vector3IBaseList& neighbours,
115 const ::Ice::Current&)
116{
117 neighbourDeltas = neighbours;
118}
119
120void
121armarx::AStarPathPlanner::setRotationFactorForHeuristic(::Ice::Float factor, const ::Ice::Current&)
122{
123 rotationFactorForHeuristic = factor;
124}
125
126void
127armarx::AStarPathPlanner::setDistanceFactorForHeuristic(::Ice::Float factor, const ::Ice::Current&)
128{
129 distanceFactorForHeuristic = factor;
130}
131
132float
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
140float
141armarx::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
150armarx::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::Vector3IBase operator+(const armarx::Vector3IBase &lhs, const armarx::Vector3IBase &rhs)
#define M_PI
Definition MathTools.h:17
void setNeighbourSteps(const ::armarx::Vector3IBaseList &neighbours, const ::Ice::Current &=Ice::emptyCurrent) override
float heuristic(const armarx::Vector3 &from, const armarx::Vector3 &to) const
The heuristic used for A*.
void setBoundingYRange(const ::armarx::FloatRange &range, const ::Ice::Current &=Ice::emptyCurrent) override
void setRotationFactorForHeuristic(::Ice::Float factor, const ::Ice::Current &=Ice::emptyCurrent) override
void setDistanceFactorForHeuristic(::Ice::Float factor, const ::Ice::Current &=Ice::emptyCurrent) override
void setBoundingXRange(const ::armarx::FloatRange &range, const ::Ice::Current &=Ice::emptyCurrent) override
void setTranslationtStepSize(::Ice::Float step, const ::Ice::Current &=Ice::emptyCurrent) override
void setRotationStepSize(::Ice::Float step, const ::Ice::Current &=Ice::emptyCurrent) override
int stepSizeDeg
The step size in degree.
::armarx::Vector3BaseList getPath(const ::armarx::Vector3BasePtr &from, const ::armarx::Vector3BasePtr &to, const ::Ice::Current &=Ice::emptyCurrent) const override
bool isPositionValid(armarx::Vector3 position) const
VirtualRobot::RobotPtr agent
The Vector3 class.
Definition Pose.h:113
virtual Eigen::Vector3f toEigen() const
Definition Pose.cpp:134
#define ARMARX_ERROR_S
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:216
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
IceInternal::Handle< Vector3 > Vector3Ptr
Definition Pose.h:165