WorkerNode.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 <algorithm>
26 #include <chrono>
27 
29 
30 #include "../../util/CollisionCheckUtil.h"
31 
32 #include "WorkerNode.h"
33 #include "util.h"
34 
35 namespace armarx::addirrtstar
36 {
38  {
39  ARMARX_CHECK_EXPRESSION(manager);
40  //init values
41  killRequest = false;
42  workerPaused = false;
43  updateTopicName = topicPrefix + ADDIRRTSTAR_TREE_UPDATE_TOPIC_NAME;
44  //init collision test
45  cspace->initCollisionTest();
46  //register for armarx message passing
49 
50  //start worker
51  workerThread = std::thread {[this]{workerTask();}};
52  }
53 
55  {
56  globalWorkers = getTopic<TreeUpdateInterfacePrx>(updateTopicName);
57  }
58 
60  {
61  //kill worker and join it
62  killWorker();
63  workerThread.join();
64  }
65 
66  void WorkerNode::updateTree(const Update& u, const Ice::Current&)
67  {
68  std::lock_guard<std::mutex> lock {updateMutex};
70  }
71 
73  {
74  //init sampler
75  std::vector<std::pair<float, float>> dimensionBounds {};
76  dimensionBounds.reserve(getDimensionality());
77 
78  for (const auto& dim : cspace->getDimensionsBounds())
79  {
80  dimensionBounds.emplace_back(dim.min, dim.max);
81  }
82 
83  ARMARX_CHECK_EXPRESSION(getDimensionality() == goalCfg.size());
84  ARMARX_CHECK_EXPRESSION(getDimensionality() == startCfg.size());
85 
86  sampler.reset(
87  new InformedSampler
88  {
90  dimensionBounds.begin(), dimensionBounds.end(),
91  startCfg.begin(), startCfg.end(), goalCfg.begin(),
92  rotationMatrix
93  },
94  std::mt19937{std::random_device{}()}
95  }
96  );
97  rotationMatrix.clear();
98 
99  //after starting the requred proxies are set.
100  getObjectScheduler()->waitForObjectStateMinimum(eManagedIceObjectStarted);
101 
102  //some assertions
104  //init tree
105  //it is initialized after we subscribed to the topic
106  initTree();
107 
108  while (true)
109  {
110  //check for kill
111  if (killRequest)
112  {
113  break;
114  }
115 
116  if (workerPaused)
117  {
118  std::this_thread::sleep_for(std::chrono::seconds(2));
119  continue;
120  }
121 
122  doBatch();
123  }
124 
125  auto finalUpdateId = tree.getPreviousUpdateId();
126  ARMARX_CHECK_EXPRESSION(tree.getAppliedUpdateIds().at(workerId) == finalUpdateId);
127  //send the the final update id. Important: DON'T send size-1 since the last element is an empty update
128  manager->setWorkersFinalUpdateId(workerId, finalUpdateId);
129  }
130 
132  {
133  //update max cost for sampler
134  sampler->getDistribution().setMaximalCost(tree.getBestCost());
135 
136  //iterate batch
137  for (Ice::Long currentBatchIteration = 0; currentBatchIteration < batchSize; ++currentBatchIteration)
138  {
139  // //break on kill => sends last update and ends calculation
140  if (killRequest)
141  {
142  break;
143  }
144 
146  }//for batch
147 
148  //was the goal reached?
149  //try connect the nearest node of all newly (since the last check) added nodes to the goal
150  const auto& workersNodes = tree.getNodes().at(workerId);
151 
152  if (onePastLastGoalConnect + nodeCountDeltaForGoalConnectionTries <= workersNodes.size())
153  {
154  NodeId goalConnectNodeNearestId;
155  goalConnectNodeNearestId.workerId = workerId;
156  float goalConnectNodeNearestDistance = std::numeric_limits<float>::infinity();
157 
158  // const Update& u = tree.getCurrentUpdate();
159  for (std::size_t numberOfNode = onePastLastGoalConnect; numberOfNode < workersNodes.size(); ++numberOfNode)
160  {
161  const Tree::NodeType& currNode = workersNodes.at(numberOfNode);
162  const float currNodeDistance = distance(goalCfg, currNode.config);
163 
164  if (currNodeDistance < goalConnectNodeNearestDistance)
165  {
166  goalConnectNodeNearestDistance = currNodeDistance;
167  goalConnectNodeNearestId.numberOfNode = numberOfNode;
168  }
169  }
170 
171  if (goalConnectNodeNearestDistance < std::numeric_limits<float>::infinity())
172  {
173  const auto cfgReached = steer(tree.getNode(goalConnectNodeNearestId).config, goalCfg);
174 
175  if (cfgReached == tree.getNode(goalConnectNodeNearestId).config)
176  {
177  //this node is a boundary node!
178  tree.decreaseRadius(goalConnectNodeNearestId);
179  }
180  else if (cfgReached == goalCfg)
181  {
182  //node can reach goal node
183  //since this node was added to the tree it is rewired
184  //new rewiring could improve other paths but no path leading to the goal
185  //wiht a lower cost can include this node => rewiring would be a waste of time
186  tree.addGoalReached(goalConnectNodeNearestId, goalConnectNodeNearestDistance);
187  }
188  else
189  {
190  //reached some config => add it to the tree and rewire it
191  addAndRewireConfig(cfgReached, goalConnectNodeNearestId);
192  }
193  }
194  onePastLastGoalConnect = workersNodes.size();
195  }
196 
197  //send update
198  {
199  std::lock_guard<std::mutex> lock {updateMutex};
201  }
202  }
203 
205  {
206  {
207  //add updates
209  std::unique_lock<std::mutex> {updateMutex},
210  [this](Ice::Long workerNodeId, Ice::Long updateSubId)
211  {
212  return getRemoteUpdate(workerNodeId, updateSubId);
213  }
214  ); //this methode is thread save if the given mutex is correct
215  }
216  }
217 
219  {
221 
222  // //sample
223  ConfigType cfgRnd(getDimensionality());
224 
225  NodeId nodeNearestId;
226  float nodeNearestDistance;
227  //sample until the sample is in the dynamic domain of nearest
228 
229  {
230  u_int64_t samplingTries {0}; //should never overflow
231  do
232  {
233  //dont get stuck in this loop when killed
234  if (!(samplingTries % 1000))
235  {
236  //check every 1000th loop
237  if (killRequest)
238  {
239  return;
240  }
241 
242  //prevent getting stuck with a few nodes with min radius
243  //+1000 => worker will not try to apply updates at sampling try 0 (it just applied them!)
244  if (!((samplingTries + 1000) % 10000))
245  {
247  }
248  }
249  (*sampler)(cfgRnd.data());
250  std::tie(nodeNearestId, nodeNearestDistance) = tree.getNearestNeighbourAndDistance(cfgRnd);
251  ++samplingTries;
252  }
253  while (nodeNearestDistance > tree.getNode(nodeNearestId).radius);
254  }
255 
256  const auto cfgReached = steer(tree.getNode(nodeNearestId).config, cfgRnd);
257 
258  //if the dcd parameter is correct the path xNear -> xNew
259  //is collision free (per invariant), but may be the same as xNearest
260  //we do no continuous CD
261  if (tree.getNode(nodeNearestId).config == cfgReached)
262  {
263  //this node is a boundary node!
264  tree.decreaseRadius(nodeNearestId);
265  return;
266  }
267 
268  //no boundary node! find parent, add it to the tree, and rewire it
269  addAndRewireConfig(cfgReached, nodeNearestId);
270  }
271 
273  const ConfigType& cfgReached,
274  const NodeId& nodeNearestId)
275  {
276  NodeId nodeBestId;
277  float costReachedToNodeBest;
278  std::vector<std::pair<NodeId, float>> kNearestIdsAndDistances;
279  std::map<NodeId, bool> isCollisionFreeCache;
280 
281 
283  nodeNearestId,
284  cfgReached,
285  //out
286  nodeBestId,
287  costReachedToNodeBest,
288  kNearestIdsAndDistances,
289  isCollisionFreeCache
290  );
291 
292  //add node to the tree
293  const auto nodeReachedId = tree.addNode(
294  cfgReached, //reached point
295  nodeBestId, //parent id
296  costReachedToNodeBest//cost parent->node
297  );
298 
299  rewire(nodeReachedId, kNearestIdsAndDistances, isCollisionFreeCache);
300  }
301 
303  const NodeId& nodeId,
304  const std::vector<std::pair<NodeId, float>>& kNearestIdsAndDistances,
305  const std::map<NodeId, bool>& isCollisionFreeCache
306  )
307  {
308  //find root node
309  const auto& node = tree.getNode(nodeId);
310  const ConfigType& cfgNode = node.config;
311  const float nodeFromStartCost = node.fromStartCost;
312 
313  for (const auto& nearIdAndDist : kNearestIdsAndDistances)
314  {
315  const auto& nodeNearId = nearIdAndDist.first;
316  auto& nodeNear = tree.getNode(nodeNearId);
317  const auto& cfgNodeNear = nodeNear.config;
318 
319  const auto costPathToNearOld = nodeNear.fromStartCost;
320  const auto costNodeToNear = nearIdAndDist.second;
321  const auto costPathToNearOverNode = nodeFromStartCost + costNodeToNear;
322 
323  //is this a better path
324  if (costPathToNearOverNode < costPathToNearOld)
325  {
326  //check collision
327  const auto it = isCollisionFreeCache.find(nodeNearId);
328 
329  if ((it != isCollisionFreeCache.end() && (*it).second) || isPathCollisionFree(cfgNode, cfgNodeNear))
330  {
331  //rewire node
332  tree.setNodeParent(nodeNearId, nodeId, costNodeToNear);
333  }
334 
335  }
336  }
337  }
338 
340  const NodeId& nodeNearestId,
341  const ConfigType& cfgReached,
342  NodeId& outNodeBestId,
343  float& outCostReachedToNodeBest,
344  std::vector<std::pair<NodeId, float>>& outKNearestIdsAndDistances,
345  std::map<NodeId, bool>& outIsCollisionFreeCache
346  )
347  {
348  // //find parent
349  outNodeBestId = nodeNearestId;
350  outCostReachedToNodeBest = distance(tree.getNode(outNodeBestId).config, cfgReached);
351  auto costPathToReachedOverNodeBest = tree.getNode(nodeNearestId).fromStartCost + outCostReachedToNodeBest;
352  outKNearestIdsAndDistances = tree.getKNearestNeighboursAndDistances(cfgReached, getK());
353 
354  for (const auto& nearIdAndDist : outKNearestIdsAndDistances)
355  {
356 
357  auto& nodeNearId = nearIdAndDist.first;
358  auto& nodeNear = tree.getNode(nodeNearId);
359  const auto& cfgNodeNear = nodeNear.config;
360 
361  const auto costReachedToNodeNear = nearIdAndDist.second;
362  const auto costPathToReachedOverNodeNear = nodeNear.fromStartCost + costReachedToNodeNear;
363 
364  if (costPathToReachedOverNodeNear < costPathToReachedOverNodeBest)
365  {
366  //check collision
367  if (isPathCollisionFree(cfgNodeNear, cfgReached))
368  {
369  outNodeBestId = nodeNearId;
370  costPathToReachedOverNodeBest = costPathToReachedOverNodeNear;
371  outCostReachedToNodeBest = costReachedToNodeNear;
372  outIsCollisionFreeCache[nodeNearId] = true;
373  }
374  else
375  {
376  outIsCollisionFreeCache[nodeNearId] = false;
377  }
378  }
379  }
380  }
381 
383  {
384  ARMARX_CHECK_EXPRESSION(getDimensionality() == from.size());
386 
387  return dcdSteer(from, to, DCDStepSize, [this](const ConfigType & cfg)
388  {
389  return isCollisionFree(cfg);
390  });
391  }
392 
394  {
395  bool tmp = dcdIsPathCollisionFree(from, to, DCDStepSize, [this](const ConfigType & cfg)
396  {
397  return isCollisionFree(cfg);
398  });//steer(from, to) == to;
399  return tmp;
400  }
401 
403  {
404  bufferCDRange.first = cfg.data();
405  bufferCDRange.second = bufferCDRange.first + cfg.size();
406  return cspace->isCollisionFree(bufferCDRange);
407  }
408 
409  std::size_t WorkerNode::getK()
410  {
411  const auto dim = static_cast<float>(getDimensionality());
412  const auto kRRT = std::pow(2.f, dim + 1.f) * M_E * (1.f + 1.f / dim);
413  const auto k = kRRT * std::log(static_cast<float>(tree.size()));
414  return static_cast<std::size_t>(std::ceil(k));
415  }
416 
417  Update WorkerNode::getUpdate(Ice::Long updateId, const Ice::Current&) const
418  {
419  std::lock_guard<std::mutex> lock {updateMutex};
420  ARMARX_CHECK_EXPRESSION(updateId >= 0);
422  return tree.getLocalUpdates().at(static_cast<std::size_t>(updateId));
423  }
424 
425  Update WorkerNode::getRemoteUpdate(Ice::Long workerNodeId, Ice::Long updateSubId)
426  {
427  return manager->getUpdate(workerNodeId, updateSubId);
428  }
429 }
armarx::addirrtstar::WorkerNode::onInitComponent
void onInitComponent() override
Initializes the worker and starts the worker thread.
Definition: WorkerNode.cpp:37
armarx::addirrtstar::WorkerNode::killRequest
std::atomic_bool killRequest
Wheether the node should shut down.
Definition: WorkerNode.h:285
armarx::addirrtstar::WorkerNode::getUpdate
Update getUpdate(Ice::Long updateId, const Ice::Current &=Ice::emptyCurrent) const override
Definition: WorkerNode.cpp:417
armarx::addirrtstar::WorkerNode::applyPendingUpdates
void applyPendingUpdates()
Applies all pending updates to the tree.
Definition: WorkerNode.cpp:204
armarx::addirrtstar::WorkerNode::rewire
void rewire(const NodeId &nodeId, const std::vector< std::pair< NodeId, float >> &kNearestIdsAndDistances, const std::map< NodeId, bool > &isCollisionFreeCache)
The rewire operation of the rrt* algorithm.
Definition: WorkerNode.cpp:302
armarx::addirrtstar::WorkerNode::updateTopicName
std::string updateTopicName
The update topic's name.
Definition: WorkerNode.h:305
armarx::addirrtstar::WorkerNode::initTree
void initTree()
initializes the tree by getting the current tree from the manager
Definition: WorkerNode.h:233
WorkerNode.h
armarx::addirrtstar::Tree::size
std::size_t size() const
Definition: Tree.h:623
armarx::addirrtstar::Tree::setNodeParent
void setNodeParent(const NodeId &child, const NodeId &newParent, float fromParentCost)
Sets the parent node of child to parent.
Definition: Tree.h:500
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::addirrtstar::WorkerNode::getRemoteUpdate
Update getRemoteUpdate(Ice::Long workerNodeId, Ice::Long updateSubId)
Definition: WorkerNode.cpp:425
armarx::addirrtstar::WorkerNode::ConfigType
Tree::ConfigType ConfigType
Type of a configuration.
Definition: WorkerNode.h:81
util.h
armarx::addirrtstar::Tree::decreaseRadius
void decreaseRadius(const NodeId &id)
Decreases a node's radius.
Definition: Tree.h:509
armarx::addirrtstar::Tree::applyPendingUpdates
void applyPendingUpdates()
Applies all pending updates.
Definition: Tree.h:197
armarx::addirrtstar::Tree::NodeType::config
ConfigType config
The node's configuration.
Definition: Tree.h:65
armarx::Sampler
Stores a distribution and a generator.
Definition: Samplers.h:47
armarx::addirrtstar::WorkerNode::tree
Tree tree
The RRT tree.
Definition: WorkerNode.h:280
armarx::addirrtstar::WorkerNode::updateMutex
std::mutex updateMutex
Mutex protecting the update structures.
Definition: WorkerNode.h:295
armarx::addirrtstar::Tree::NodeType
Represents a node of thr rrt.
Definition: Tree.h:60
armarx::addirrtstar::Tree::addNode
NodeId addNode(ConfigType cfg, const NodeId &parent, float fromParentCost)
Adds a node to the tree.
Definition: Tree.cpp:241
armarx::addirrtstar::WorkerNode::workerPaused
std::atomic_bool workerPaused
workerPaused is true if the worker is currently on hold and does not perform any calculations.
Definition: WorkerNode.h:290
armarx::addirrtstar::Tree::getNearestNeighbourAndDistance
std::pair< NodeId, float > getNearestNeighbourAndDistance(const ConfigType &cfg)
Definition: Tree.h:593
armarx::addirrtstar::Tree::NodeType::radius
float radius
The node's radius.
Definition: Tree.h:73
armarx::addirrtstar::WorkerNode::distance
static float distance(const ConfigType &from, const ConfigType &to)
Calculates the euclidian distance between from and to.
Definition: WorkerNode.h:203
armarx::addirrtstar::WorkerNode::doBatchIteration
void doBatchIteration()
Executes one iteration of a batch.
Definition: WorkerNode.cpp:218
armarx::addirrtstar::WorkerNode::onExitComponent
void onExitComponent() override
Kills the worker thread (if it is still running) and joins it.
Definition: WorkerNode.cpp:59
armarx::addirrtstar::WorkerNode::onConnectComponent
void onConnectComponent() override
Gets a proxy to the topic.
Definition: WorkerNode.cpp:54
armarx::addirrtstar::Tree::sendCurrentUpdate
void sendCurrentUpdate(TreeUpdateInterfacePrx &prx)
Sends the current update to the given proxy.
Definition: Tree.cpp:189
armarx::addirrtstar::WorkerNode::workerTask
void workerTask()
The worker task.
Definition: WorkerNode.cpp:72
armarx::Sampler::DistributionType
Distribution DistributionType
Type of the stored distribution.
Definition: Samplers.h:53
armarx::addirrtstar::WorkerNode::workerThread
std::thread workerThread
Thread executing the worker task.
Definition: WorkerNode.h:300
armarx::dcdSteer
ConfigType dcdSteer(const ConfigType &from, const ConfigType &to, RealType dcdStepSize, CollisionChecker isCollisionFree)
Tries to reach to from from using the given stepsize.
Definition: CollisionCheckUtil.h:46
armarx::addirrtstar::WorkerNode::isCollisionFree
bool isCollisionFree(const ConfigType &cfg)
Definition: WorkerNode.cpp:402
armarx::addirrtstar::WorkerNode::steer
ConfigType steer(const ConfigType &from, const ConfigType &to)
Does a linear interpolation from from to to and checks the path per DCD.
Definition: WorkerNode.cpp:382
armarx::addirrtstar::Tree::getLocalUpdates
const std::deque< Update > & getLocalUpdates() const
Definition: Tree.h:156
armarx::addirrtstar::Tree::getAppliedUpdateIds
const Ice::LongSeq & getAppliedUpdateIds() const
Definition: Tree.h:758
armarx::VariantType::Long
const VariantTypeId Long
Definition: Variant.h:917
armarx::addirrtstar::WorkerNode::isPathCollisionFree
bool isPathCollisionFree(const ConfigType &from, const ConfigType &to)
Definition: WorkerNode.cpp:393
ArmarXObjectScheduler.h
armarx::addirrtstar::WorkerNode::getDimensionality
std::size_t getDimensionality()
Definition: WorkerNode.h:267
armarx::addirrtstar::Tree::getNode
NodeType & getNode(const NodeId &id)
Definition: Tree.cpp:354
armarx::addirrtstar::WorkerNode::getK
std::size_t getK()
Definition: WorkerNode.cpp:409
armarx::addirrtstar::WorkerNode::globalWorkers
TreeUpdateInterfacePrx globalWorkers
Proxy used to write to the update topic.
Definition: WorkerNode.h:309
armarx::addirrtstar::WorkerNode::onePastLastGoalConnect
std::size_t onePastLastGoalConnect
The index one past the last node that was considered to be connected to the goal configuration.
Definition: WorkerNode.h:319
armarx::addirrtstar::WorkerNode::doBatch
void doBatch()
Executes a batch.
Definition: WorkerNode.cpp:131
armarx::addirrtstar::Tree::getPreviousUpdateId
Ice::Long getPreviousUpdateId() const
Definition: Tree.h:270
armarx::addirrtstar::Tree::NodeType::fromStartCost
float fromStartCost
Cost of the path (sttart node, this node)
Definition: Tree.h:81
armarx::addirrtstar::WorkerNode::sampler
std::unique_ptr< InformedSampler > sampler
Stores the sampler.
Definition: WorkerNode.h:328
armarx::addirrtstar::Tree::getKNearestNeighboursAndDistances
std::vector< std::pair< NodeId, float > > getKNearestNeighboursAndDistances(const ConfigType &cfg, std::size_t k)
Definition: Tree.cpp:311
armarx::ManagedIceObject::usingTopic
void usingTopic(const std::string &name, bool orderedPublishing=false)
Registers a proxy for subscription after initialization.
Definition: ManagedIceObject.cpp:248
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::addirrtstar::WorkerNode::findBestParent
void findBestParent(const NodeId &nodeNearestId, const ConfigType &cfgReached, NodeId &outNodeBestId, float &outCostReachedToNodeBest, std::vector< std::pair< NodeId, float >> &outKNearestIdsAndDistances, std::map< NodeId, bool > &outIsCollisionFreeCache)
Searches the optimal parent for a configuration.
Definition: WorkerNode.cpp:339
armarx::ManagedIceObject::offeringTopic
void offeringTopic(const std::string &name)
Registers a topic for retrival after initialization.
Definition: ManagedIceObject.cpp:290
armarx::addirrtstar::Tree::addPendingUpdate
void addPendingUpdate(const Update &u)
Adds a new update to the list of pending updates.
Definition: Tree.cpp:483
armarx::addirrtstar::WorkerNode::updateTree
void updateTree(const Update &u, const Ice::Current &=Ice::emptyCurrent) override
Receives an update from other workers and adds it to the queue of pending updates.
Definition: WorkerNode.cpp:66
armarx::addirrtstar::Tree::getBestCost
float getBestCost() const
Definition: Tree.cpp:399
armarx::addirrtstar::Tree::addGoalReached
void addGoalReached(const NodeId &goal, float costToGoToGoal)
Adds a node to the list of nodes that can reach the goal configuration.
Definition: Tree.h:528
armarx::addirrtstar::Tree::getNodes
const std::deque< std::deque< NodeType > > & getNodes() const
Definition: Tree.h:610
armarx::ManagedIceObject::getObjectScheduler
ArmarXObjectSchedulerPtr getObjectScheduler() const
Definition: ManagedIceObject.cpp:731
armarx::addirrtstar::WorkerNode::addAndRewireConfig
void addAndRewireConfig(const ConfigType &cfgReached, const NodeId &nodeNearestId)
Adds a configuration to the tree.
Definition: WorkerNode.cpp:272
armarx::addirrtstar::WorkerNode::killWorker
void killWorker(const Ice::Current &=Ice::emptyCurrent) override
Signals the worker thread to exit.
Definition: WorkerNode.h:165
armarx::addirrtstar
Definition: ManagerNode.cpp:35