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 2016
21  * @copyright http://www.gnu.org/licenses/gpl.txt
22  * GNU General Public License
23  */
24 
25 #include "Task.h"
26 
27 #include <random>
28 
29 #include <VirtualRobot/RobotNodeSet.h>
30 
32 
33 #include "../../util/CollisionCheckUtil.h"
34 #include <MotionPlanning/CSpace/CSpaceSampled.h>
35 
37 {
38  Task::Task(MotionPlanningTaskBasePtr previousStep,
39  const std::string& taskName,
40  Ice::Long maxTimeForPostprocessingInSeconds,
41  Ice::Float dcdStep,
42  Ice::Long maxTries,
43  Ice::Float minShortcutImprovementRatio,
44  Ice::Float minPathImprovementRatio) :
45  MotionPlanningTaskBase(taskName),
46  PostprocessingMotionPlanningTaskBase(taskName, previousStep),
47  PostprocessingMotionPlanningTask(previousStep, taskName),
48  TaskBase(taskName,
49  previousStep,
50  dcdStep,
51  maxTries,
52  maxTimeForPostprocessingInSeconds,
53  minShortcutImprovementRatio,
54  minPathImprovementRatio)
55  {
56  if (!this->previousStep)
57  {
58  throw std::invalid_argument{"previousStep is NULL"};
59  }
60  if (this->dcdStep <= 0)
61  {
62  throw std::invalid_argument{"dcdStep <= 0"};
63  }
64  if (this->maxTimeForPostprocessingInSeconds <= 0)
65  {
66  throw std::invalid_argument{"maxTimeForPostprocessingInSeconds <= 0"};
67  }
68  if (this->minShortcutImprovementRatio >= 1)
69  {
70  throw std::invalid_argument{"minShortcutImprovementRatio >=1"};
71  }
72  if (this->minShortcutImprovementRatio < 0.01)
73  {
74  ARMARX_VERBOSE_S << "Changed minShortcutImprovementRatio from "
75  << this->minShortcutImprovementRatio << " to " << 0.01;
76  this->minShortcutImprovementRatio = 0.01;
77  }
78  if (this->minPathImprovementRatio >= 1)
79  {
80  throw std::invalid_argument{"minPathImprovementRatio >=1"};
81  }
82  if (this->minPathImprovementRatio < 0.001)
83  {
84  ARMARX_VERBOSE_S << "Changed minShortcutImprovementRatio from "
85  << this->minShortcutImprovementRatio << " to " << 0.001;
86  this->minPathImprovementRatio = 0.001;
87  }
88  }
89 
90  void
91  Task::abortTask(const Ice::Current&)
92  {
93  if (!previousStep->finishedRunning())
94  {
95  previousStep->abortTask();
96  }
97  taskIsAborted = true;
98  }
99 
100  void
101  Task::run(const RemoteObjectNodePrxList& nodes, const Ice::Current&)
102  {
103  auto previousStep = MotionPlanningTaskPtr::dynamicCast(this->previousStep);
104  ARMARX_CHECK_EXPRESSION(previousStep);
105  previousStep->addTaskStatusCallback(
106  [this](TaskStatus::Status s)
107  {
108  if (s != TaskStatus::eDone)
109  {
110  setTaskStatus(s);
111  }
112  });
113  auto endTime = std::chrono::high_resolution_clock::now() +
114  std::chrono::seconds{getMaximalPlanningTimeInSeconds()};
115  previousStep->run(nodes);
116  endTime = std::min(endTime,
117  std::chrono::high_resolution_clock::now() +
118  std::chrono::seconds{maxTimeForPostprocessingInSeconds});
119  {
120  std::lock_guard<std::mutex> lock{mtx};
121  paths.reserve(2);
122  auto p = previousStep->getPath();
123  paths.emplace_back(p.nodes, 0, "smooth_" + p.pathName);
124  paths.emplace_back(std::move(p.nodes), 0, std::move(p.pathName));
125  //the path length is wrong!
126  //we calculate it anyway
127  }
128 
129  //setup the length vars.
130  std::vector<float> lengths;
131  auto updateLengths = [&, this]()
132  {
133  lengths.clear();
134  lengths.reserve(paths.front().nodes.size() + 1);
135  lengths.emplace_back(0);
136  for (std::size_t i = 0; i + 1 < paths.front().nodes.size(); ++i)
137  {
138  auto& from = paths.front().nodes.at(i);
139  auto& to = paths.front().nodes.at(i + 1);
140  lengths.emplace_back(lengths.back() +
141  euclideanDistance(from.begin(), from.end(), to.begin()));
142  }
143  };
144  updateLengths();
145 
146  paths.at(0).cost = lengths.back();
147  paths.at(1).cost = lengths.back();
148  if (finishedRunning())
149  {
150  return;
151  }
152  ARMARX_CHECK_EXPRESSION(!paths.front().nodes.empty());
153  if (getTaskStatus() != TaskStatus::eRefining)
154  {
155  setTaskStatus(TaskStatus::eRefining);
156  }
157  if (paths.front().nodes.size() == 2)
158  {
159  //this was a trivial path
160  setTaskStatus(TaskStatus::eDone);
161  return;
162  }
163 
164  getCSpace()->initCollisionTest();
165 
166  std::mt19937 gen{std::random_device{}()};
167  Ice::Long iteration = 0;
168  for (; iteration < maxTries;)
169  //don't increment the iteration count here.
170  // we only want to count iterations that do collision checking.
171  {
172  ARMARX_CHECK_EXPRESSION(paths.front().nodes.size() == lengths.size());
173  //stop?
174  if (taskIsAborted)
175  {
176  setTaskStatus(TaskStatus::eRefinementAborted);
177  return;
178  }
179  if (endTime <= std::chrono::high_resolution_clock::now())
180  {
181  break;
182  }
183 
184  auto segmentOffsets = calcOffsets(lengths.back(), gen);
185  float segmentLength = segmentOffsets.second - segmentOffsets.first;
186 
187  VectorXf from(getCSpace()->getDimensionality());
188  VectorXf to(getCSpace()->getDimensionality());
189 
190  //nodes with idx < are kept
191  std::size_t takeLastOPTE = 0;
192  //nodes with idx >= are kept
193  std::size_t takeAgainFirstIdx = 0;
194  // select from and to bounding the segment to replace
195  // calc the last node to keep before the segment starts and the first node to keep after tehe segment ends
196  {
197  std::size_t i = 0;
198 
199  //search node i with: segmentPoints.first <= pathLenght(start,node_i)
200  for (; i < lengths.size() && segmentOffsets.first > lengths.at(i); ++i)
201  ;
202  takeLastOPTE = i;
203  if (takeLastOPTE + 1 == lengths.size())
204  {
205  //already at the end
206  continue;
207  }
208  ARMARX_CHECK_EXPRESSION(takeLastOPTE + 1 < lengths.size());
209  if (takeLastOPTE == 0)
210  {
211  ARMARX_CHECK_EXPRESSION(segmentOffsets.first == 0);
212  //start the segment to replace at the start
213  from = paths.front().nodes.at(0);
214  }
215  else
216  {
217  ARMARX_CHECK_EXPRESSION(takeLastOPTE);
218  const auto fromSubSegLen =
219  lengths.at(takeLastOPTE) - lengths.at(takeLastOPTE - 1);
220  const auto fromSubSegLenPart =
221  segmentOffsets.first - lengths.at(takeLastOPTE - 1);
222  if (fromSubSegLenPart < 1e-5)
223  {
224  //distance to prev node is low
225  //snap to node in path
226  from = paths.front().nodes.at(takeLastOPTE - 1);
227  --takeLastOPTE; //dont add the node a second time
228  }
229  else
230  {
231  const auto fromSubSegLenRatio = fromSubSegLenPart / fromSubSegLen;
232 
233  ARMARX_CHECK_EXPRESSION(from.size() ==
234  paths.front().nodes.at(takeLastOPTE - 1).size());
235  ARMARX_CHECK_EXPRESSION(from.size() ==
236  paths.front().nodes.at(takeLastOPTE).size());
237 
238  std::transform(paths.front().nodes.at(takeLastOPTE - 1).begin(),
239  paths.front().nodes.at(takeLastOPTE - 1).end(),
240  paths.front().nodes.at(takeLastOPTE).begin(),
241  from.begin(),
242  [fromSubSegLenRatio](float from, float to) {
243  return (1.f - fromSubSegLenRatio) * from +
244  fromSubSegLenRatio * to;
245  });
246  }
247  }
248 
249  for (; i < lengths.size() && segmentOffsets.second > lengths.at(i); ++i)
250  ;
251  takeAgainFirstIdx = i;
252  ARMARX_CHECK_EXPRESSION(takeAgainFirstIdx);
253  if (takeAgainFirstIdx >= lengths.size())
254  {
255  //to the end
256  takeAgainFirstIdx = paths.front().nodes.size();
257  to = paths.front().nodes.back();
258  }
259  else
260  {
261  ARMARX_CHECK_EXPRESSION(takeAgainFirstIdx < lengths.size());
262  //to either some node in the middle
263  const auto toSubSegLen =
264  lengths.at(takeAgainFirstIdx) - lengths.at(takeAgainFirstIdx - 1);
265  const auto toSubSegLenPart =
266  segmentOffsets.second - lengths.at(takeAgainFirstIdx - 1);
267  if (lengths.at(takeAgainFirstIdx) - toSubSegLenPart < 1e-5)
268  {
269  //snap to node in path
270  to = paths.front().nodes.at(takeAgainFirstIdx);
271  ++takeAgainFirstIdx; //dont add the node a second time
272  }
273  else
274  {
275  const auto toSubSegLenRatio = toSubSegLenPart / toSubSegLen;
276  ARMARX_CHECK_EXPRESSION(takeAgainFirstIdx > 0);
278  to.size() == paths.front().nodes.at(takeAgainFirstIdx - 1).size());
279  ARMARX_CHECK_EXPRESSION(to.size() ==
280  paths.front().nodes.at(takeAgainFirstIdx).size());
282  paths.front().nodes.at(takeAgainFirstIdx - 1).begin(),
283  paths.front().nodes.at(takeAgainFirstIdx - 1).end(),
284  paths.front().nodes.at(takeAgainFirstIdx).begin(),
285  to.begin(),
286  [toSubSegLenRatio](float from, float to)
287  { return (1.f - toSubSegLenRatio) * from + toSubSegLenRatio * to; });
288  }
289  }
290  }
291  if (takeLastOPTE == takeAgainFirstIdx)
292  {
293  continue;
294  }
295  float segmentShortcutLength = euclideanDistance(from.begin(), from.end(), to.begin());
296 
297  if (segmentShortcutLength > segmentLength * (1.f - minShortcutImprovementRatio))
298  {
299  // the possible improvement is not big enough
300  continue;
301  }
302  //we only count iterations with collision checks
303  ++iteration;
304  if (!isPathCollisionFree(from, to))
305  {
306  continue;
307  }
308 
309  ARMARX_CHECK_EXPRESSION(takeLastOPTE < takeAgainFirstIdx);
310  VectorXfSeq newPostprocessedSolution;
311  newPostprocessedSolution.reserve(paths.front().nodes.size() +
312  1); //max one node longer (upper bound for new length
313  std::copy(paths.front().nodes.begin(),
314  paths.front().nodes.begin() + takeLastOPTE,
315  std::back_inserter(newPostprocessedSolution));
316  newPostprocessedSolution.emplace_back(std::move(from));
317  newPostprocessedSolution.emplace_back(std::move(to));
318  std::copy(paths.front().nodes.begin() + takeAgainFirstIdx,
319  paths.front().nodes.end(),
320  std::back_inserter(newPostprocessedSolution));
321 
322  {
323  std::lock_guard<std::mutex> lock{mtx};
324  paths.front().nodes = std::move(newPostprocessedSolution);
325  }
326  //the path changed
327  updateLengths();
328  }
329  ARMARX_VERBOSE << "Smoothed with " << iteration << " tries";
330  setTaskStatus(TaskStatus::eDone);
331  }
332 
333  Ice::Long
334  Task::getPathCount(const Ice::Current&) const
335  {
336  std::lock_guard<std::mutex> lock{mtx};
337  return paths.size();
338  }
339 
340  PathWithCost
341  Task::getBestPath(const Ice::Current&) const
342  {
343  return getNthPathWithCost(0);
344  }
345 
346  PathWithCost
347  Task::getNthPathWithCost(Ice::Long n, const Ice::Current&) const
348  {
349  std::lock_guard<std::mutex> lock{mtx};
350  return paths.at(n);
351  }
352 
353  PathWithCostSeq
354  Task::getAllPathsWithCost(const Ice::Current&) const
355  {
356  std::lock_guard<std::mutex> lock{mtx};
357  return paths;
358  }
359 
360  bool
361  Task::isPathCollisionFree(const VectorXf& from, const VectorXf& to)
362  {
363  // it is usually either a simox cspace or an adapter, if not use standard interpolation
364 
365 
366  SimoxCSpacePtr simoxcspace = SimoxCSpacePtr::dynamicCast(getCSpace());
367  if (!simoxcspace)
368  {
369  CSpaceAdaptorBasePtr simoxcspaceadapter =
370  CSpaceAdaptorBasePtr::dynamicCast(getCSpace());
371  if (simoxcspaceadapter)
372  {
373  simoxcspace = SimoxCSpacePtr::dynamicCast(simoxcspaceadapter->getOriginalCSpace());
374  }
375  }
376  if (simoxcspace)
377  {
378  // do robot specific interpolation
379  VirtualRobot::RobotNodeSetPtr rns = VirtualRobot::RobotNodeSet::createRobotNodeSet(
380  simoxcspace->getAgentSceneObj(), "tmp", simoxcspace->getAgentJointNames());
381  Saba::CSpaceSampled tmpCSpace(
382  simoxcspace->getAgentSceneObj(),
383  VirtualRobot::CDManagerPtr(new VirtualRobot::CDManager(simoxcspace->getCD())),
384  rns);
385  auto distance = [&, this](Eigen::VectorXf const& from, Eigen::VectorXf const& to)
386  { return tmpCSpace.calcDist(from, to); };
387 
388  auto interpolation =
389  [&, this](Eigen::VectorXf const& from, Eigen::VectorXf const& to, float step)
390  { return tmpCSpace.interpolate(from, to, step); };
391  return dcdIsPathCollisionFree(
392  from,
393  to,
394  dcdStep,
395  [this](const VectorXf& cfg) {
396  return getCSpace()->isCollisionFree({cfg.data(), cfg.data() + cfg.size()});
397  },
398  false,
399  std::optional<decltype(distance)>(distance),
400  std::optional<decltype(interpolation)>(interpolation));
401  }
402  else
403  {
404  return dcdIsPathCollisionFree(
405  from,
406  to,
407  dcdStep,
408  [this](const VectorXf& cfg) {
409  return getCSpace()->isCollisionFree({cfg.data(), cfg.data() + cfg.size()});
410  },
411  false);
412  }
413  }
414 
415  std::pair<float, float>
416  Task::calcOffsets(float length, std::mt19937& gen)
417  {
418  std::uniform_real_distribution<float> distA{
419  0, std::nextafter(length, std::numeric_limits<float>::max())};
420  const auto a = distA(gen);
421  const auto minDelta = minPathImprovementRatio * length;
422  std::uniform_real_distribution<float> distB{
423  0, std::nextafter(length - 2 * minDelta, std::numeric_limits<float>::max())};
424  const auto bSample = distB(gen);
425  const auto b = (bSample <= (a - minDelta)) ? bSample : bSample + 2 * minDelta;
426 
427  return std::minmax(a, b);
428  }
429 } // namespace armarx::rngshortcut
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:187
armarx::VariantType::Float
const VariantTypeId Float
Definition: Variant.h:919
armarx::rngshortcut::Task::paths
PathWithCostSeq paths
Definition: Task.h:117
Task.h
armarx::MotionPlanningTaskCI::finishedRunning
bool finishedRunning(const Ice::Current &=Ice::emptyCurrent) const override
Returns whether the task has finished planning.
Definition: MotionPlanningTaskControlInterface.cpp:53
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
armarx::rngshortcut::Task::mtx
std::mutex mtx
Definition: Task.h:115
armarx::rngshortcut::Task::getMaximalPlanningTimeInSeconds
Ice::Long getMaximalPlanningTimeInSeconds(const Ice::Current &=Ice::emptyCurrent) const override
Definition: Task.h:94
armarx::rngshortcut::Task::getPathCount
Ice::Long getPathCount(const Ice::Current &=Ice::emptyCurrent) const override
Definition: Task.cpp:334
armarx::MotionPlanningTask::getTaskStatus
TaskStatus::Status getTaskStatus(const Ice::Current &=Ice::emptyCurrent) const override
Definition: MotionPlanningTask.h:101
armarx::PostprocessingMotionPlanningTask::getCSpace
CSpaceBasePtr getCSpace(const Ice::Current &=Ice::emptyCurrent) const override
Definition: MotionPlanningTask.h:258
armarx::rngshortcut::Task::calcOffsets
std::pair< float, float > calcOffsets(float length, std::mt19937 &gen)
Definition: Task.cpp:416
IceInternal::Handle< SimoxCSpace >
armarx::rngshortcut::Task::run
void run(const RemoteObjectNodePrxList &nodes, const Ice::Current &=Ice::emptyCurrent) override
Runs the task.
Definition: Task.cpp:101
armarx::rngshortcut::Task::getNthPathWithCost
PathWithCost getNthPathWithCost(Ice::Long n, const Ice::Current &=Ice::emptyCurrent) const override
Definition: Task.cpp:347
armarx::ctrlutil::a
double a(double t, double a0, double j)
Definition: CtrlUtil.h:45
copy
Use of this software is granted under one of the following two to be chosen freely by the user Boost Software License Version Marcin Kalicinski Permission is hereby free of to any person or organization obtaining a copy of the software and accompanying documentation covered by this and transmit the and to prepare derivative works of the and to permit third parties to whom the Software is furnished to do all subject to the including the above license this restriction and the following must be included in all copies of the in whole or in and all derivative works of the unless such copies or derivative works are solely in the form of machine executable object code generated by a source language processor THE SOFTWARE IS PROVIDED AS WITHOUT WARRANTY OF ANY EXPRESS OR INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF FITNESS FOR A PARTICULAR TITLE AND NON INFRINGEMENT IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER WHETHER IN TORT OR ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE The MIT Marcin Kalicinski Permission is hereby free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to copy
Definition: license.txt:39
armarx::VariantType::Long
const VariantTypeId Long
Definition: Variant.h:918
armarx::rngshortcut::Task::Task
Task()=default
Ctor used by object factories.
max
T max(T t1, T t2)
Definition: gdiam.h:51
armarx::rngshortcut::Task::taskIsAborted
std::atomic_bool taskIsAborted
Definition: Task.h:118
armarx::rngshortcut::Task::isPathCollisionFree
bool isPathCollisionFree(const VectorXf &from, const VectorXf &to)
Definition: Task.cpp:361
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
ExpressionException.h
armarx::rngshortcut::Task::getAllPathsWithCost
PathWithCostSeq getAllPathsWithCost(const Ice::Current &=Ice::emptyCurrent) const override
Definition: Task.cpp:354
armarx::euclideanDistance
float euclideanDistance(IteratorType1 first1, IteratorType1 last1, IteratorType2 first2)
Returns the euclidean distance.
Definition: Metrics.h:104
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::PostprocessingMotionPlanningTask
Definition: MotionPlanningTask.h:244
armarx::MotionPlanningTask::setTaskStatus
bool setTaskStatus(TaskStatus::Status newTaskStatus, const Ice::Current &=Ice::emptyCurrent) override
Definition: MotionPlanningTask.cpp:32
armarx::rngshortcut::Task::getBestPath
PathWithCost getBestPath(const Ice::Current &=Ice::emptyCurrent) const override
Definition: Task.cpp:341
ARMARX_VERBOSE_S
#define ARMARX_VERBOSE_S
Definition: Logging.h:207
distance
double distance(const Point &a, const Point &b)
Definition: point.hpp:95
min
T min(T t1, T t2)
Definition: gdiam.h:44
armarx::rngshortcut
Definition: Task.cpp:36
armarx::rngshortcut::Task::abortTask
void abortTask(const Ice::Current &=Ice::emptyCurrent) override
Aborts planning.
Definition: Task.cpp:91
armarx::ctrlutil::s
double s(double t, double s0, double v0, double a0, double j)
Definition: CtrlUtil.h:33