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