CalcPath.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2014-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 RobotSkillTemplates::MovePlatformToLandmarkGroup
19 * @author Valerij Wittenbeck ( valerij dot wittenbeck at student dot kit dot edu )
20 * @date 2014
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24
25#include "CalcPath.h"
26
27#include <VirtualRobot/MathTools.h>
28
30
32
33#include "../PlatformContext.h"
37
38
39using namespace armarx;
40using namespace PlatformGroup;
41
42// DO NOT EDIT NEXT LINE
44
46 XMLStateTemplate<CalcPath>(stateData), CalcPathGeneratedBase<CalcPath>(stateData)
47{
48 ARMARX_IMPORTANT << "CalcPath";
49}
50
51bool
52getNearestPositionOnEdge(const ::memoryx::GraphNodeBaseList& nodes,
53 float currentX,
54 float currentY,
55 Eigen::Vector2f& closest,
56 memoryx::GraphNodePtr& edgeNode1,
57 memoryx::GraphNodePtr& edgeNode2)
58{
59 Eigen::Vector2f robotLocation(currentX, currentY);
60 closest.setZero();
61
62 for (auto& basenode : nodes)
63 {
64 memoryx::GraphNodePtr node = memoryx::GraphNodePtr::dynamicCast(basenode);
65
66 auto size = node->getOutdegree();
67 for (int i = 0; i < size; ++i)
68 {
69 memoryx::GraphNodePtr nextNode =
70 memoryx::GraphNodePtr::dynamicCast(node->getAdjacentNode(i)->getEntity());
71
72 Eigen::Vector2f node1 =
73 FramedPosePtr::dynamicCast(node->getPose())->toEigen().block<2, 1>(0, 3);
74 Eigen::Vector2f node2 =
75 FramedPosePtr::dynamicCast(nextNode->getPose())->toEigen().block<2, 1>(0, 3);
76
77 if ((node1 - node2).norm() > 1e-9)
78 {
79 Eigen::Vector2f result =
80 VirtualRobot::MathTools::nearestPointOnSegment<Eigen::Vector2f>(
81 node1, node2, robotLocation);
82
83 if ((closest.x() == 0 && closest.y() == 0) ||
84 (result - robotLocation).norm() < (closest - robotLocation).norm())
85 {
86 closest = result;
87 edgeNode1 = node;
88 edgeNode2 = nextNode;
89 }
90 }
91 }
92 }
93
94 return (closest.x() != 0 || closest.y() != 0);
95}
96
97/**
98 * @brief Projection of point on an edge of the graph.
99 * (node)-------(position)-------->(targetNode)
100 */
106
107std::vector<EdgeProjection>
108getNearestPositionOnEdges(const ::memoryx::GraphNodeBaseList& nodes, Eigen::Vector2f robotPos)
109{
110 std::vector<EdgeProjection> projections;
111
112 for (auto& basenode : nodes)
113 {
114 memoryx::GraphNodePtr node = memoryx::GraphNodePtr::dynamicCast(basenode);
115
116 auto size = node->getOutdegree();
117 for (int i = 0; i < size; ++i)
118 {
119 memoryx::GraphNodePtr nextNode =
120 memoryx::GraphNodePtr::dynamicCast(node->getAdjacentNode(i)->getEntity());
121
122 Eigen::Vector2f node1 =
123 FramedPosePtr::dynamicCast(node->getPose())->toEigen().block<2, 1>(0, 3);
124 Eigen::Vector2f node2 =
125 FramedPosePtr::dynamicCast(nextNode->getPose())->toEigen().block<2, 1>(0, 3);
126
127 if ((node1 - node2).norm() > 1e-9)
128 {
129 Eigen::Vector2f result =
130 VirtualRobot::MathTools::nearestPointOnSegment<Eigen::Vector2f>(
131 node1, node2, robotPos);
132
133 projections.push_back(EdgeProjection{.targetNode = nextNode, .position = result});
134 }
135 }
136 }
137
138 return projections;
139}
140
141void
143{
144 ARMARX_VERBOSE << "CalcPath::onEnter()";
145 PlatformContext* context = getContext<PlatformContext>();
146
147 auto graphSegment = context->priorKnowledgePrx->getGraphSegment();
148 viz::ScopedClient arviz(
149 viz::Client::createFromTopic("MovePlatformToLandmark/CalcPath", context->arvizTopic));
150
151 std::vector<armarx::Vector3Ptr> targetPositions;
152
153 // Does the scene exist
154 const std::string sceneName = in.getSceneName();
155 if (!graphSegment->hasScene(sceneName))
156 {
157 ARMARX_WARNING << "Target scene '" << sceneName << "' does not exist in menory";
158 out.settargetPositions(targetPositions);
159 emitEvNoPathFound();
160 return;
161 }
162
163 // Does the target landmark exist
164 const std::string landmark = in.gettargetLandmark();
165 if (!graphSegment->hasNodeWithName(sceneName, landmark))
166 {
167 ARMARX_WARNING << "Target landmark '" << landmark << "' doesn't exist in graph (scene) '"
168 << sceneName << "'";
169 auto nodes = graphSegment->getNodesByScene(sceneName);
170 for (auto& node : nodes)
171 {
172 ARMARX_INFO << "node: " << node->getName();
173 }
174 out.settargetPositions(targetPositions);
175 sendEvent<EvNoPathFound>();
176 return;
177 }
178
179 memoryx::GraphNodeBasePtr goalNode;
180 {
181 memoryx::GraphNodeBasePtr landmarkNode =
182 graphSegment->getNodeFromSceneByName(sceneName, landmark);
183 ARMARX_CHECK_NOT_NULL(landmarkNode)
184 << "Could not find node with name '" << landmark << "' in scene '" << sceneName << "'";
185
186 goalNode = context->graphNodePoseResolverPrx->getNearestRobotLocationNode(landmarkNode);
187 ARMARX_CHECK_NOT_NULL(goalNode) << "No nearest node found for landmark '" << landmark
188 << "' in scene '" << sceneName << "'";
189 }
190 ARMARX_INFO << "Resolved target landmark to '" << goalNode->getName() << "'";
191
192
193 viz::Layer layerRobotPose = arviz.layer("Robot Pose");
194 viz::Layer layerClosestPoint = arviz.layer("Closest Point");
195
196 // Get current position
197 ChannelRefPtr poseRef =
198 context->getChannelRef(context->getPlatformUnitObserverName(), "platformPose");
199 const float platformPositionX = poseRef->getDataField("positionX")->getFloat();
200 const float platformPositionY = poseRef->getDataField("positionY")->getFloat();
201 const float platformYaw = poseRef->getDataField("rotation")->getFloat();
202
203 Eigen::Vector3f currentPos(platformPositionX, platformPositionY, 0);
204 const Eigen::Vector2f currentPos2d = currentPos.head<2>();
205
206 {
207 layerRobotPose.add(
208 viz::Pose("RobotPose")
209 .position(currentPos)
210 .orientation(VirtualRobot::MathTools::rpy2eigen3f(0, 0, platformYaw)));
211 }
212
213 std::vector<EdgeProjection> projections =
214 getNearestPositionOnEdges(graphSegment->getNodesByScene(sceneName), currentPos2d);
215 if (projections.empty())
216 {
217 ARMARX_ERROR << "Could not find nearest point on graph";
218 emitEvNoPathFound();
219 return;
220 }
221
222 std::sort(projections.begin(),
223 projections.end(),
224 [&currentPos2d](const auto& p1, const auto& p2)
225 {
226 const float c1 = (p1.position - currentPos2d).norm();
227 const float c2 = (p2.position - currentPos2d).norm();
228
229 return c1 < c2;
230 });
231
232 // Projections are sorted by distance to current position.
233 std::optional<EdgeProjection> bestProjection;
234 ::memoryx::GraphNodeBaseList bestPath;
235 float bestPathCosts = std::numeric_limits<float>::max();
236
237 for (const EdgeProjection& projection : projections)
238 {
239 const Eigen::Vector3f nodePosition = fromIce(projection.targetNode->getPose()->position);
240 const float distToNode = (nodePosition - currentPos).norm();
241 ARMARX_INFO << "Current node: '" << projection.targetNode->getName() << "' | "
242 << VAROUT(distToNode);
243
244 // Find a path from the closest node to the target
245 ::memoryx::GraphNodeBaseList path =
246 graphSegment->aStar(projection.targetNode->getId(), goalNode->getId());
247 // ToDo: Cost = (current pos -> proj.position) + (proj.position -> proj.targetNode)
248 float pathCost = distToNode + getPathLength(path);
249
250 if (path.empty())
251 {
252 continue;
253 }
254
255 // Projections can have two entries with the same length right after another (undirected edge)
256 // We only stop after we visited both directions.
257 if (bestProjection.has_value() and
258 (bestProjection->position - projection.position).norm() > 1e-4)
259 {
260 break;
261 }
262
263 if (pathCost < bestPathCosts)
264 {
265 bestProjection = projection;
266 bestPath = path;
267 bestPathCosts = pathCost;
268 }
269 }
270
271 if (not bestProjection.has_value())
272 {
273 ARMARX_WARNING << "No path found";
274 out.settargetPositions(targetPositions);
275 emitEvNoPathFound();
276 return;
277 }
278
279 const Eigen::Vector2f nearestPoint = bestProjection->position;
280 ARMARX_INFO << "Nearest Point on graph: " << nearestPoint.transpose();
281 {
282 Eigen::Vector3f closestPointPos;
283 closestPointPos << nearestPoint, 0;
284
285 layerClosestPoint.add(viz::Sphere("Closest Point")
286 .position(closestPointPos)
287 .radius(100)
288 .color(simox::Color::green()));
289 layerClosestPoint.add(viz::Cylinder("To Closest Point")
290 .fromTo(currentPos, closestPointPos)
291 .radius(30)
292 .color(simox::Color::green(96)));
293 }
294
295 // arviz.commit({layerRobotPose, layerClosestPoint});
296
297 bool fixedOrentation = true;
298 float length = getPathLength(bestPath);
299
300 bool useWaypointOrientations = in.getUseWaypointOrientations();
301 if (!useWaypointOrientations && length > in.getdriveMode_lookAhead_thresholdMM())
302 {
303 ARMARX_INFO << "long path (" << length
304 << " mm) -> rotating platform towards moving direction";
305 fixedOrentation = false;
306 }
307
308 Eigen::Vector3f rpy;
309 PosePtr pose = PosePtr::dynamicCast(bestPath.back()->getPose());
310 VirtualRobot::MathTools::eigen4f2rpy(pose->toEigen(), rpy);
311 const float goalYaw = rpy[2];
313 float lastAlpha = 0;
314 int count = 0;
315
316 for (auto& node : bestPath)
317 {
318 ::armarx::FramedPosePtr currentPose = FramedPosePtr::dynamicCast(node->getPose());
319 Vector3Ptr v = new Vector3();
320 v->x = currentPose->position->x;
321 v->y = currentPose->position->y;
322
323 if (useWaypointOrientations)
324 {
325 VirtualRobot::MathTools::eigen4f2rpy(currentPose->toEigen(), rpy);
326 v->z = rpy[2];
327 ARMARX_INFO << "Using waypoint orientation: " << v->z;
328 }
329 else if (fixedOrentation)
330 {
331 v->z = goalYaw;
332 }
333 else
334 {
335 if (count <= 0 || count >= int(bestPath.size() - 1))
336 {
337 // first /last pos
338 VirtualRobot::MathTools::eigen4f2rpy(currentPose->toEigen(), rpy);
339 ARMARX_DEBUG << "start or last pose of path, using orientation of waypoint: "
340 << rpy[2];
341 v->z = rpy[2];
342 lastAlpha = rpy[2];
343 }
344 else
345 {
346 double x = currentPose->position->x - lastPose->position->x;
347 double y = currentPose->position->y - lastPose->position->y;
348 double dist = std::sqrt(x * x + y * y);
349
350 if (dist < 500)
351 {
352 ARMARX_DEBUG << "points close together, using last orientation: " << lastAlpha;
353 v->z = lastAlpha;
354 }
355 else
356 {
357 // compute alpha
358 Eigen::Vector2d a(x, y);
359 a.normalize();
360 Eigen::Vector2d b(0, 1); // vector in y dir == 0 degrees
361 v->z = -(std::atan2(b.y(), b.x()) - std::atan2(a.y(), a.x()));
362
363 if (v->z < 0)
364 {
365 v->z += 2.0 * M_PI;
366 }
367
368 //v->z = acos(a.dot(b));
369 lastAlpha = v->z;
370 ARMARX_DEBUG << "waypoint angle between " << a.transpose() << " and "
371 << b.transpose() << ": " << v->z;
372 }
373 }
374
375 lastPose = currentPose;
376 count++;
377 }
378
379 targetPositions.push_back(v);
380 ARMARX_INFO << "waypoint #" << targetPositions.size() << ": " << node->getName()
381 << " Coordinates:\n " << v->output();
382 }
383
384 // Replace initial node with closest point on edge
385 Vector3Ptr v(new Vector3(nearestPoint.x(),
386 nearestPoint.y(),
387 Vector3Ptr::dynamicCast(targetPositions[0])->toEigen().z()));
388 targetPositions.insert(targetPositions.begin(), v);
389
390 out.settargetPositions(targetPositions);
391 emitEvPathFound();
392}
393
394void
396{
397 PlatformContext* context = getContext<PlatformContext>();
398 context->debugDrawer->removeSphereDebugLayerVisu("closestPoint");
399}
400
401float
402CalcPath::getPathLength(::memoryx::GraphNodeBaseList& path)
403{
404 float l = 0.0f;
405 ::armarx::FramedPoseBasePtr lastPose;
406
407 for (auto& node : path)
408 {
409 if (!lastPose)
410 {
411 lastPose = node->getPose();
412 continue;
413 }
414
415 ::armarx::FramedPoseBasePtr currentPose = node->getPose();
416 float x = currentPose->position->x - lastPose->position->x;
417 float y = currentPose->position->y - lastPose->position->y;
418 l += std::sqrt(x * x + y * y);
419 lastPose = currentPose;
420 }
421
422 return l;
423}
424
425// DO NOT EDIT NEXT FUNCTION
426std::string
428{
429 return "CalcPath";
430}
431
432// DO NOT EDIT NEXT FUNCTION
bool getNearestPositionOnEdge(const ::memoryx::GraphNodeBaseList &nodes, float currentX, float currentY, Eigen::Vector2f &closest, memoryx::GraphNodePtr &edgeNode1, memoryx::GraphNodePtr &edgeNode2)
Definition CalcPath.cpp:52
std::vector< EdgeProjection > getNearestPositionOnEdges(const ::memoryx::GraphNodeBaseList &nodes, Eigen::Vector2f robotPos)
Definition CalcPath.cpp:108
#define M_PI
Definition MathTools.h:17
#define VAROUT(x)
memoryx::GraphNodePoseResolverInterfacePrx graphNodePoseResolverPrx
memoryx::PriorKnowledgeInterfacePrx priorKnowledgePrx
std::string getPlatformUnitObserverName()
armarx::viz::TopicPrx arvizTopic
DebugDrawerInterfacePrx debugDrawer
CalcPath(XMLStateConstructorParams stateData)
Definition CalcPath.cpp:45
float getPathLength(::memoryx::GraphNodeBaseList &path)
Definition CalcPath.cpp:402
static SubClassRegistry Registry
Definition CalcPath.h:52
static std::string GetName()
Definition CalcPath.cpp:427
static XMLStateFactoryBasePtr CreateInstance(XMLStateConstructorParams stateData)
Definition CalcPath.cpp:434
ChannelRefPtr getChannelRef(const std::string &observerName, const std::string &channelName) override
The Vector3 class.
Definition Pose.h:113
Class for legacy to stay compatible with old statecharts.
Definition XMLState.h:147
static Client createFromTopic(std::string const &componentName, armarx::viz::Topic::ProxyType const &topic)
Definition Client.cpp:40
viz::Client that will delete (clear) committed layers when destroyed.
Layer layer(std::string const &name) const override
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceInternal::Handle< Vector3 > Vector3Ptr
Definition Pose.h:165
IceInternal::Handle< Pose > PosePtr
Definition Pose.h:306
IceInternal::Handle< ChannelRef > ChannelRefPtr
Definition ChannelRef.h:40
IceInternal::Handle< XMLStateFactoryBase > XMLStateFactoryBasePtr
Definition XMLState.h:64
void fromIce(const std::map< IceKeyT, IceValueT > &iceMap, boost::container::flat_map< CppKeyT, CppValueT > &cppMap)
IceInternal::Handle< FramedPose > FramedPosePtr
Definition FramedPose.h:272
IceInternal::Handle< GraphNode > GraphNodePtr
Definition GraphNode.h:47
double norm(const Point &a)
Definition point.hpp:102
Projection of point on an edge of the graph.
Definition CalcPath.cpp:102
Eigen::Vector2f position
Definition CalcPath.cpp:104
memoryx::GraphNodePtr targetNode
Definition CalcPath.cpp:103
void add(ElementT const &element)
Definition Layer.h:31