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