Task.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5  *
6  * ArmarX is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * ArmarX is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * @package RobotComponents
19  * @author Raphael Grimm ( raphael dot grimm at kit dot edu )
20  * @date 2015
21  * @copyright http://www.gnu.org/licenses/gpl.txt
22  * GNU General Public License
23  */
24 
25 #include "Task.h"
26 
27 #include <algorithm>
28 
29 #include "../../util/CollisionCheckUtil.h"
30 
31 namespace armarx::astar
32 {
33  Task::Task(const CSpaceBasePtr& cspace,
34  const VectorXf& startCfg,
35  const VectorXf& goalCfg,
36  const std::string& taskName,
37  float dcdStep,
38  float gridStepSize,
39  Ice::Long maximalPlanningTimeInSeconds)
40  {
41  this->cspace = cspace->clone();
42  this->startCfg = startCfg;
43  this->goalCfg = goalCfg;
44  this->dcdStep = dcdStep;
45  this->gridStepSize = gridStepSize;
46  this->maximalPlanningTimeInSeconds = maximalPlanningTimeInSeconds;
47  this->taskName = taskName;
48 
49  if (startCfg.size() != goalCfg.size() ||
50  startCfg.size() != static_cast<std::size_t>(this->cspace->getDimensionality()))
51  {
52  throw std::invalid_argument{
53  "start and goal have to be the size of the cspace's dimensionality"};
54  }
55 
56  if (gridStepSize < dcdStep)
57  {
58  throw std::invalid_argument{
59  "the step size of the implicit grid has to be larger than the DCD step size"};
60  }
61  }
62 
63  Path
64  Task::getPath(const Ice::Current&) const
65  {
66  std::lock_guard<std::mutex> lock{mtx};
67  return solution;
68  }
69 
70  void
71  Task::run(const RemoteObjectNodePrxList&, const Ice::Current&)
72  {
73  const std::size_t n = cspace->getDimensionality();
74  setTaskStatus(TaskStatus::ePlanning);
75 
76  //check trivial cases
77  cspace->initCollisionTest();
78 
79  const auto startIsCollisionFree =
80  cspace->isCollisionFree(std::pair<const Ice::Float*, const Ice::Float*>{
81  startCfg.data(), startCfg.data() + startCfg.size()});
82  const auto goalIscollisionFree =
83  cspace->isCollisionFree(std::pair<const Ice::Float*, const Ice::Float*>{
84  goalCfg.data(), goalCfg.data() + goalCfg.size()});
85  if (!startIsCollisionFree || !goalIscollisionFree)
86  {
87  setTaskStatus(TaskStatus::ePlanningFailed);
88  return;
89  }
90 
91  const auto distStartEnd =
92  euclideanDistance(startCfg.begin(), startCfg.end(), goalCfg.begin());
93 
94  if (distStartEnd <= dcdStep)
95  {
96  setTaskStatus(TaskStatus::eDone);
97  solution.nodes.emplace_back(startCfg);
98  solution.nodes.emplace_back(goalCfg);
99  return;
100  }
101 
102  // //define grid
103  using GridPointType = std::vector<long>;
104 
105  //grid bounds (the 0 gridpoint is the start cfg)
106  LongRangeSeq gridBounds;
107  gridBounds.reserve(n);
108  const auto&& cspaceBounds = cspace->getDimensionsBounds();
109  std::transform(cspaceBounds.begin(),
110  cspaceBounds.end(),
111  startCfg.begin(),
112  std::back_inserter(gridBounds),
113  [this](const FloatRange& dimBounds, float start)
114  {
115  const auto distLower = dimBounds.min - start;
116  const auto gridPointsLower = std::trunc(distLower / gridStepSize);
117 
118  const auto distHigher = dimBounds.max - start;
119  const auto gridPointsHigher = std::trunc(distHigher / gridStepSize);
120 
121  return LongRange{static_cast<Ice::Long>(gridPointsLower),
122  static_cast<Ice::Long>(gridPointsHigher)};
123  });
124 
125 
126  //calculates all neighbours in the grid (only along the cspace's coordinate axes)
127  auto getNeighbours = [gridBounds, n](const GridPointType& p)
128  {
129  std::vector<GridPointType> neighbours;
130  //allow diag neighbours
131  neighbours.push_back(p);
132  neighbours.reserve(std::pow(3, gridBounds.size()));
133  for (std::size_t i = 0; i < n; ++i)
134  {
135  const auto n = neighbours.size();
136  const auto generateLower = (gridBounds.at(i).min < p.at(i));
137  const auto generateHigher = (gridBounds.at(i).max > p.at(i));
138  neighbours.reserve(
139  n * (1 + generateHigher + generateLower)); //shold be already big enough
140  if (generateLower)
141  {
142  std::transform(neighbours.begin(),
143  neighbours.begin() + n,
144  std::back_inserter(neighbours),
145  [i](typename decltype(neighbours)::value_type v)
146  {
147  --v.at(i);
148  return v;
149  });
150  }
151  if (generateHigher)
152  {
153  std::transform(neighbours.begin(),
154  neighbours.begin() + n,
155  std::back_inserter(neighbours),
156  [i](typename decltype(neighbours)::value_type v)
157  {
158  ++v.at(i);
159  return v;
160  });
161  }
162  }
163  ARMARX_CHECK_EXPRESSION(p == neighbours.front());
164  return neighbours;
165  };
166 
167  //define grid conversion / distance meassure
168  auto toConfig = [this](const GridPointType& gridPoint)
169  {
170  VectorXf cfg;
171  cfg.reserve(gridPoint.size());
172  std::transform(gridPoint.begin(),
173  gridPoint.end(),
174  startCfg.begin(),
175  std::back_inserter(cfg),
176  [this](float g, float s) { return s + g * gridStepSize; });
177  return cfg;
178  };
179 
180  auto distance = [](const GridPointType& a, const GridPointType& b)
181  { return euclideanDistance(a.begin(), a.end(), b.begin()); };
182 
183  //project goal to grid
184  GridPointType goal;
185  goal.reserve(n);
186  for (std::size_t i = 0; i < n; ++i)
187  {
188  auto dist = goalCfg.at(i) - startCfg.at(i);
189  long gridPoint = std::round(dist / gridStepSize);
190  goal.emplace_back(std::clamp(gridPoint, gridBounds.at(i).min, gridBounds.at(i).max));
191  }
192  if (!isPathCollisionFree(goalCfg, toConfig(goal)))
193  {
194  setTaskStatus(TaskStatus::ePlanningFailed);
195  return;
196  }
197  setTaskStatus(TaskStatus::eDone);
198 
199  // //set up a*
200  struct NodeData
201  {
202  GridPointType node;
203  VectorXf cfg;
204  float fScore = 0; //cost to goal
205  float gScore = 0; //path cost
206  std::size_t predecessor = 0;
207  };
208 
209  //holds all created nodes
210  std::deque<NodeData> nodes;
211 
212  std::set<std::size_t> openSet; //holds ids to nodes (these nodes are not processed yet)
213  //add start to the open set
214  nodes.emplace_back();
215  nodes.back().cfg = startCfg;
216  nodes.back().node.assign(n, 0);
217  openSet.emplace(0);
218 
219  std::set<std::size_t> closedSet; //holds ids to nodes (these nodes are processed)
220 
221  auto assemblePath = [&](std::size_t toId) //returns the found path to a given node
222  {
223  std::deque<VectorXf> reversedPath;
224  reversedPath.emplace_back(goalCfg); //maybe it was not on a grid point
225  while (true)
226  {
227  auto& currentNode = nodes.at(toId);
228  reversedPath.emplace_back(std::move(currentNode.cfg));
229  if (toId == currentNode.predecessor)
230  {
231  break;
232  }
233  toId = currentNode.predecessor;
234  }
235  VectorXfSeq path;
236  path.reserve(reversedPath.size());
237  std::move(reversedPath.rbegin(), reversedPath.rend(), std::back_inserter(path));
238  return path;
239  };
240 
241  auto expandNode = [&](std::size_t idToExpand)
242  {
243  const auto& toExpand = nodes.at(idToExpand);
244  const auto successors =
245  getNeighbours(toExpand.node); //the first neighbour is the point itself
246  for (std::size_t sucIdx = 1; sucIdx < successors.size(); ++sucIdx)
247  {
248  auto& successor = successors.at(sucIdx);
249  //is the successor already in the open or closed set?
250  auto successorIt =
251  std::find_if(nodes.begin(),
252  nodes.end(),
253  [&](const NodeData& elem) { return successor == elem.node; });
254  auto successorId = std::distance(nodes.begin(), successorIt);
255 
256  if (closedSet.find(successorId) != closedSet.end())
257  {
258  continue; //already in closed set
259  }
260 
261  float tentativeGScore = toExpand.gScore + distance(successor, toExpand.node);
262 
263  bool successorIsInOpenSet = openSet.find(successorId) != openSet.end();
264  if (successorIsInOpenSet && tentativeGScore >= successorIt->gScore)
265  {
266  continue; // already a better path
267  }
268 
269  //if successorIsInOpenSet the cfg will be moved back (2 moves should be faster than alloc + calc)
270  auto successorCfg =
271  successorIsInOpenSet ? std::move(successorIt->cfg) : toConfig(successor);
272  //check path for collision
273  if (!isPathCollisionFree(toExpand.cfg, successorCfg))
274  {
275  continue;
276  }
277 
278  const auto newFScore = tentativeGScore + distance(successor, goal);
279  if (successorIsInOpenSet)
280  {
281  //update
282  successorIt->cfg = std::move(successorCfg);
283  successorIt->fScore = newFScore;
284  successorIt->gScore = tentativeGScore;
285  successorIt->predecessor = idToExpand;
286  }
287  else
288  {
289  //create entry
290  openSet.emplace(nodes.size());
291  nodes.emplace_back();
292  nodes.back().node = std::move(successor);
293  nodes.back().cfg = std::move(successorCfg);
294  nodes.back().fScore = newFScore;
295  nodes.back().gScore = tentativeGScore;
296  nodes.back().predecessor = idToExpand;
297  }
298  }
299  };
300 
301  //wait for done or timeout
302  const auto endTime =
303  std::chrono::system_clock::now() + std::chrono::seconds{maximalPlanningTimeInSeconds};
304 
305  while (true)
306  {
307  //check for exit condition
308  if (taskIsAborted)
309  {
310  setTaskStatus(TaskStatus::ePlanningAborted);
311  return;
312  }
313  if (endTime < std::chrono::system_clock::now())
314  {
315  setTaskStatus(TaskStatus::ePlanningFailed);
316  return;
317  }
318  if (openSet.empty())
319  {
320  //search done (but failed)!
321  setTaskStatus(TaskStatus::ePlanningFailed);
322  return;
323  }
324  auto currentIdIt =
325  std::min_element(openSet.begin(),
326  openSet.end(),
327  [&](std::size_t osId1, std::size_t osId2)
328  { return nodes.at(osId1).fScore < nodes.at(osId2).fScore; });
329  auto currentId = *currentIdIt;
330  openSet.erase(currentIdIt);
331 
332  if (nodes.at(currentId).node == goal)
333  {
334  solution.nodes = assemblePath(currentId);
335  setTaskStatus(TaskStatus::eDone);
336  return;
337  }
338 
339  closedSet.emplace(currentId);
340  expandNode(currentId);
341  }
342  ARMARX_CHECK_EXPRESSION(!"unreachable");
343  }
344 
345  bool
346  Task::isPathCollisionFree(const VectorXf& from, const VectorXf& to)
347  {
348  return dcdIsPathCollisionFree(
349  from,
350  to,
351  dcdStep,
352  [this](const VectorXf& cfg) {
353  return cspace->isCollisionFree({cfg.data(), cfg.data() + cfg.size()});
354  },
355  false);
356  }
357 } // namespace armarx::astar
armarx::astar::Task::run
void run(const RemoteObjectNodePrxList &, const Ice::Current &=Ice::emptyCurrent) override
Runs the task.
Definition: Task.cpp:71
armarx::astar::Task::solution
Path solution
Definition: Task.h:97
armarx::astar::Task::Task
Task()=default
Ctor used by object factories.
armarx::dcdIsPathCollisionFree
bool dcdIsPathCollisionFree(const ConfigType &from, const ConfigType &to, RealType dcdStepSize, CollisionChecker isCollisionFree, bool toIsCollisionFree=true, const std::optional< Distance > &distanceLambda=std::optional< Distance >(), const std::optional< Interpolate > &interpolationLambda=std::optional< Interpolate >(), std::vector< ConfigType > *resultPath=NULL)
Returns whether the line startCfg to to is collision free.
Definition: CollisionCheckUtil.h:122
clamp
double clamp(double x, double a, double b)
Definition: point.hpp:136
armarx::ctrlutil::a
double a(double t, double a0, double j)
Definition: CtrlUtil.h:45
armarx::VariantType::Long
const VariantTypeId Long
Definition: Variant.h:918
Task.h
armarx::astar::Task::getPath
Path getPath(const Ice::Current &=Ice::emptyCurrent) const override
Definition: Task.cpp:64
armarx::transform
auto transform(const Container< InputT, Alloc > &in, OutputT(*func)(InputT const &)) -> Container< OutputT, typename std::allocator_traits< Alloc >::template rebind_alloc< OutputT >>
Convenience function (with less typing) to transform a container of type InputT into the same contain...
Definition: algorithm.h:351
armarx::euclideanDistance
float euclideanDistance(IteratorType1 first1, IteratorType1 last1, IteratorType2 first2)
Returns the euclidean distance.
Definition: Metrics.h:104
armarx::ctrlutil::v
double v(double t, double v0, double a0, double j)
Definition: CtrlUtil.h:39
ARMARX_CHECK_EXPRESSION
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
Definition: ExpressionException.h:73
armarx::MotionPlanningTask::setTaskStatus
bool setTaskStatus(TaskStatus::Status newTaskStatus, const Ice::Current &=Ice::emptyCurrent) override
Definition: MotionPlanningTask.cpp:32
armarx::armem::server::ltm::detail::mixin::Path
std::filesystem::path Path
Definition: DiskStorageMixin.h:17
distance
double distance(const Point &a, const Point &b)
Definition: point.hpp:95
armarx::astar
Definition: Task.cpp:31
armarx::ctrlutil::s
double s(double t, double s0, double v0, double a0, double j)
Definition: CtrlUtil.h:33
armarx::astar::Task::mtx
std::mutex mtx
Definition: Task.h:96