ArvizIntrospector.cpp
Go to the documentation of this file.
1#include "ArvizIntrospector.h"
2
3#include <algorithm>
4#include <cstddef>
5#include <iterator>
6#include <optional>
7#include <string>
8#include <utility>
9#include <vector>
10
11#include <Eigen/Geometry>
12
13#include <range/v3/algorithm/max_element.hpp>
14#include <range/v3/view/enumerate.hpp>
15
16#include <SimoxUtility/algorithm/apply.hpp>
17#include <SimoxUtility/algorithm/get_map_keys_values.h>
18#include <SimoxUtility/algorithm/string/string_tools.h>
19#include <SimoxUtility/color/Color.h>
20#include <SimoxUtility/color/ColorMap.h>
21#include <SimoxUtility/color/cmaps/colormaps.h>
22#include <VirtualRobot/Robot.h> // IWYU pragma: keep
23#include <VirtualRobot/VirtualRobot.h>
24
30
38
45
47{
48
49 inline armarx::PackagePath
50 asPackagePath(const std::string& absfilepath)
51 {
52 const std::vector<std::string> packages =
54 const std::string package = armarx::ArmarXDataPath::getProject(packages, absfilepath);
55
56 // make sure that the relative path is without the 'package/' prefix
57 const std::string relPath = [&absfilepath, &package]() -> std::string
58 {
59 if (simox::alg::starts_with(absfilepath, package))
60 {
61 // remove "package" + "/"
62 return absfilepath.substr(package.size() + 1, -1);
63 }
64
65 return absfilepath;
66 }();
67
68 return {package, relPath};
69 }
70
72 const VirtualRobot::RobotConstPtr& robot,
73 const objpose::ObjectPoseClient& objClient) :
74 arviz(arviz), robot(robot), objClient(objClient)
75 // visualization(arviz,
76 // util::Visualization::Params{.robotModelFileName = asPackagePath(robot->getFilename())})
77 {
78 robotPosesLayer = arviz.layer("robot_poses");
79 }
80
81 // TODO maybe run with predefined frequency instead of
82
83 void
85 {
86 ARMARX_DEBUG << "ArvizIntrospector::onGlobalPlannerResult";
87
88 drawGlobalTrajectory(result.trajectory);
89 if (result.helperTrajectory)
90 {
91 drawGlobalHelperTrajectory(result.helperTrajectory.value());
92 }
93 else
94 {
95 // clear layer
96 auto layer = arviz.layer("global_helper_trajectory");
97 layers[layer.data_.name] = std::move(layer);
98 }
99 arviz.commit(simox::alg::get_values(layers));
100
101
102 // visualization.visualize(result.trajectory);
103 }
104
105 void
107 {
108 ARMARX_DEBUG << "ArvizIntrospector::onGlobalPlannerSubdivision";
109
110 drawGlobalPathSubdivision(subdivision);
111
112 if (subdivision.plan.helperTrajectory)
113 {
114 drawGlobalHelperTrajectory(subdivision.plan.helperTrajectory.value());
115 }
116 else
117 {
118 // clear layer
119 auto layer = arviz.layer("global_helper_trajectory");
120 layers[layer.data_.name] = std::move(layer);
121 }
122
123 arviz.commit(simox::alg::get_values(layers));
124 }
125
126 void
128 const std::optional<local_planning::LocalPlannerResult>& result)
129 {
130 if (result)
131 {
132 drawLocalTrajectory(result.value().trajectory);
133 }
134 else
135 {
136 drawLocalTrajectory(std::nullopt);
137 }
138
139 arviz.commit(simox::alg::get_values(layers));
140 }
141
142 // void
143 // ArvizIntrospector::onTrajectoryControllerResult(
144 // const traj_ctrl::TrajectoryControllerResult& result)
145 // {
146 // std::lock_guard g{mtx};
147
148 // drawRawVelocity(result.twist);
149 // }
150
151 // void
152 // ArvizIntrospector::onSafetyGuardResult(const safety_guard::SafetyGuardResult& result)
153 // {
154 // std::lock_guard g{mtx};
155
156 // drawSafeVelocity(result.twist);
157 // }
158
159 void
161 {
162 auto layer = arviz.layer("goal");
163 layer.add(viz::Pose("goal").pose(goal).scale(3));
164
165 robotPosesLayer.clear();
166
167 // arviz.commit({layer, robotPosesLayer});
168 arviz.commit({layer});
169
170 // visualization.setTarget(goal);
171 }
172
173 void
175 {
176
177 constexpr float eps = 50; // [mm]
178
179 if (not lastPose)
180 {
181 lastPose = pose;
182 }
183 else
184 {
185 // only draw poses every `eps` mm
186 if ((lastPose->translation() - pose.translation()).norm() < eps)
187 {
188 return;
189 }
190
191 lastPose = pose;
192 }
193
194 robotPosesLayer.add(viz::Pose("pose" + std::to_string(robotPosesLayer.size())).pose(pose));
195 arviz.commit(robotPosesLayer);
196 }
197
198 void
199 ArvizIntrospector::onGlobalShortestPath(const std::vector<core::Pose>& path)
200 {
201 auto layer = arviz.layer("graph_shortest_path");
202
203 const auto toPosition = [](const core::Pose& pose) -> core::Position
204 { return pose.translation(); };
205
206
207 std::vector<core::Position> pts;
208 pts.reserve(path.size());
209 std::transform(path.begin(), path.end(), std::back_inserter(pts), toPosition);
210
211
212 // layer.add(viz::Path("shortest_path").points(pts).color(viz::Color::purple()));
213
214 for (size_t i = 0; i < (pts.size() - 1); i++)
215 {
216 layer.add(viz::Arrow("segment_" + std::to_string(i))
217 .fromTo(pts.at(i), pts.at(i + 1))
218 .color(viz::Color::purple()));
219 }
220
221
222 arviz.commit(layer);
223 }
224
225 // private methods
226
227
228 void
229 ArvizIntrospector::drawGlobalTrajectory(const core::GlobalTrajectory& trajectory)
230 {
231 drawGlobalTrajectory(trajectory, "global_planner", simox::Color::blue());
232 }
233
234 void
235 ArvizIntrospector::drawGlobalHelperTrajectory(const core::GlobalTrajectory& trajectory)
236 {
237 drawGlobalTrajectory(trajectory, "global_helper_trajectory", simox::Color::gray());
238 }
239
240 void
241 ArvizIntrospector::drawGlobalTrajectory(const core::GlobalTrajectory& trajectory,
242 const std::string layerName,
243 simox::color::Color color)
244 {
245 auto layer = arviz.layer(layerName);
246
247 layer.add(viz::Path("path").points(trajectory.positions()).color(color));
248
249 const auto cmap = simox::color::cmaps::viridis();
250
251 const float maxVelocity = ranges::max_element(trajectory.points(),
252 std::less{},
254 ->velocity;
255
256
257 for (const auto& [idx, tp] : trajectory.points() | ranges::views::enumerate)
258 {
259 const float scale = tp.velocity;
260
261 const Eigen::Vector3f target =
262 scale * tp.waypoint.pose.linear() * Eigen::Vector3f::UnitY();
263
264 layer.add(
265 viz::Arrow("velocity_" + std::to_string(idx))
266 .fromTo(tp.waypoint.pose.translation(), tp.waypoint.pose.translation() + target)
267 .color(cmap.at(tp.velocity / maxVelocity)));
268 }
269
270 layers[layer.data_.name] = std::move(layer);
271 }
272
273 void
274 ArvizIntrospector::drawGlobalPathSubdivision(const GlobalPathSubdivision& subdivision)
275 {
276 if (subdivision.subdivision.empty())
277 {
278 // no actual subdivision
279 drawGlobalTrajectory(subdivision.plan.trajectory);
280 return;
281 }
282
283 auto layer = arviz.layer("global_path_subdivision");
284
285 for (const auto& segment : subdivision.subdivision)
286 {
287 viz::Path path("segment_" + std::to_string(segment.s));
288 // subTrajectory from [s,t] to overlap with next segment
289 const auto& subTrajectory = subdivision.plan.trajectory.getSubTrajectory(
290 segment.s, std::min(segment.t + 1, subdivision.plan.trajectory.points().size()));
291 path.points(subTrajectory.positions());
292 layer.add(
293 path.color(segment.useLocalPlanner ? simox::Color::blue() : simox::Color::red()));
294 }
295
296 const auto cmap = simox::color::cmaps::viridis();
297 const float maxVelocity = ranges::max_element(subdivision.plan.trajectory.points(),
298 std::less{},
300 ->velocity;
301
302 for (const auto& [idx, tp] :
303 subdivision.plan.trajectory.points() | ranges::views::enumerate)
304 {
305 const float scale = tp.velocity;
306
307 const Eigen::Vector3f target =
308 scale * tp.waypoint.pose.linear() * Eigen::Vector3f::UnitY();
309
310 layer.add(
311 viz::Arrow("velocity_" + std::to_string(idx))
312 .fromTo(tp.waypoint.pose.translation(), tp.waypoint.pose.translation() + target)
313 .color(cmap.at(tp.velocity / maxVelocity)));
314 }
315
316 layers[layer.data_.name] = std::move(layer);
317 }
318
319 void
320 ArvizIntrospector::drawLocalTrajectory(const std::optional<core::LocalTrajectory>& input)
321 {
322 if (!input)
323 {
324 // no local trajectory found, remove old one
325 auto layer = arviz.layer("local_planner");
326 auto velLayer = arviz.layer("local_planner_velocity");
327
328 layers[layer.data_.name] = std::move(layer);
329 layers[velLayer.data_.name] = std::move(velLayer);
330 return;
331 }
332
333 const auto trajectory = input.value();
334
335 auto layer = arviz.layer("local_planner");
336
337 const std::vector<Eigen::Vector3f> points =
338 simox::alg::apply(trajectory.points(),
339 [](const core::LocalTrajectoryPoint& pt) -> Eigen::Vector3f
340 { return pt.pose.translation(); });
341
342 layer.add(viz::Path("path").points(points).color(simox::Color::green()));
343
344
345 // Visualize trajectory speed
346 auto velLayer = arviz.layer("local_planner_velocity");
347
348 simox::ColorMap cm = simox::color::cmaps::inferno();
349 cm.set_vmin(0);
350 cm.set_vmax(0.6);
351
352 for (size_t i = 0; i < trajectory.points().size() - 1; i++)
353 {
354 const core::LocalTrajectoryPoint start = trajectory.points().at(i);
355 const core::LocalTrajectoryPoint end = trajectory.points().at(i + 1);
356
357 const Duration dT = end.timestamp - start.timestamp;
358 const Eigen::Vector3f distance = end.pose.translation() - start.pose.translation();
359 const float speed = distance.norm() / 1000 / dT.toSecondsDouble();
360
361 const Eigen::Vector3f pos = start.pose.translation() + distance / 2;
362 const simox::Color color = cm.at(speed);
363
364 velLayer.add(
365 viz::Sphere("velocity_" + std::to_string(i)).position(pos).radius(50).color(color));
366 }
367
368 layers[layer.data_.name] = std::move(layer);
369 layers[velLayer.data_.name] = std::move(velLayer);
370 }
371
372 void
373 ArvizIntrospector::drawRawVelocity(const core::Twist& twist)
374 {
375 auto layer = arviz.layer("trajectory_controller");
376
377 layer.add(viz::Arrow("linear_velocity")
378 .fromTo(robot->getGlobalPosition(),
379 core::Pose(robot->getGlobalPose()) * twist.linear)
380 .color(simox::Color::orange()));
381
382 layers[layer.data_.name] = std::move(layer);
383 }
384
385 void
386 ArvizIntrospector::drawSafeVelocity(const core::Twist& twist)
387 {
388 auto layer = arviz.layer("safety_guard");
389
390 layer.add(viz::Arrow("linear_velocity")
391 .fromTo(robot->getGlobalPosition(),
392 core::Pose(robot->getGlobalPose()) * twist.linear)
393 .color(simox::Color::green()));
394
395 layers[layer.data_.name] = std::move(layer);
396 }
397
399 arviz{other.arviz}, robot{other.robot}, layers(std::move(other.layers)) //,
400 // visualization(std::move(other.visualization))
401 {
402 }
403
404 void
406 {
407 // clear all layers
408 for (auto& [name, layer] : layers)
409 {
410 layer.markForDeletion();
411 }
412 arviz.commit(simox::alg::get_values(layers));
413 layers.clear();
414
415 // some special internal layers of TEB
416 arviz.commitDeleteLayer("local_planner_obstacles");
417 arviz.commitDeleteLayer("local_planner_velocity");
418 arviz.commitDeleteLayer("local_planner_path_alternatives");
419 }
420
423 {
424 return *this;
425 }
426
427 void
429 {
430 auto layer = arviz.layer("global_planning_graph");
431
432 const objpose::ObjectPoseMap objects = objClient.fetchObjectPosesAsMap();
433 const std::vector<ObjectInfo> info = objClient.getObjectFinder().findAllObjects();
434
435 for (const auto& edge : graph.edges())
436 {
437
438 const auto sourcePose = graph.vertex(edge.sourceDescriptor()).attrib().getPose();
439 const auto targetPose = graph.vertex(edge.targetDescriptor()).attrib().getPose();
440
441
442 const viz::Color color = [&]()
443 {
444 if (edge.attrib().cost() > 100'000) // "NaN check"
445 {
446 return viz::Color::red();
447 }
448
449 return viz::Color::green();
450 }();
451
452 const auto from = core::resolveLocation(objects, info, sourcePose);
453 const auto to = core::resolveLocation(objects, info, sourcePose);
454
455 if (from.pose.has_value() && to.pose.has_value())
456 {
457 layer.add(
458 viz::Arrow(std::to_string(edge.sourceObjectID().t) + " -> " +
459 std::to_string(edge.targetObjectID().t))
460 .fromTo(from.pose.value().translation(), to.pose.value().translation())
461 .color(color));
462 }
463 }
464
465 arviz.commit(layer);
466 }
467
468 void
470 {
471 fn(arviz);
472 }
473
474 void
476 {
477 clear();
478
479 // visualization.success();
480 }
481
482 void
484 {
485 clear();
486
487 // visualization.failed();
488 }
489
490
491} // namespace armarx::navigation::server
static std::vector< std::string > FindAllArmarXSourcePackages()
double toSecondsDouble() const
Returns the amount of seconds.
Definition Duration.cpp:90
void onRobotPose(const core::Pose &pose) override
ArvizIntrospector(armarx::viz::Client arviz, const VirtualRobot::RobotConstPtr &robot, const objpose::ObjectPoseClient &objClient)
void onGlobalShortestPath(const std::vector< core::Pose > &path) override
void onGlobalGraph(const core::Graph &graph) override
void onGlobalPlannerResult(const global_planning::GlobalPlannerResult &result) override
ArvizIntrospector & operator=(ArvizIntrospector &&) noexcept
void callGenericDrawFunction(std::function< void(viz::Client &)>) override
void onGlobalPlannerSubdivision(const GlobalPathSubdivision &subdivision) override
void onGoal(const core::Pose &goal) override
void onLocalPlannerResult(const std::optional< local_planning::LocalPlannerResult > &result) override
Provides access to the armarx::objpose::ObjectPoseStorageInterface (aka the object memory).
DerivedT & pose(Eigen::Matrix4f const &pose)
Definition ElementOps.h:176
DerivedT & color(Color color)
Definition ElementOps.h:218
Layer layer(std::string const &name) const override
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
armarx::core::time::Duration Duration
void resolveLocation(Graph::Vertex &vertex, const aron::data::DictPtr &locationData)
Definition Graph.cpp:267
Eigen::Isometry3f Pose
Definition basic_types.h:31
Eigen::Vector3f Position
Definition basic_types.h:36
This file is part of ArmarX.
Definition Visu.h:48
This file is part of ArmarX.
armarx::PackagePath asPackagePath(const std::string &absfilepath)
std::map< ObjectID, ObjectPose > ObjectPoseMap
Vertex target(const detail::edge_base< Directed, Vertex > &e, const PCG &)
double distance(const Point &a, const Point &b)
Definition point.hpp:95
std::optional< core::GlobalTrajectory > helperTrajectory
Optional helper trajectory that can be used for visualization or debugging purposes.
global_planning::GlobalPlannerResult plan
Definition Navigator.h:77
Arrow & fromTo(const Eigen::Vector3f &from, const Eigen::Vector3f &to)
Definition Elements.h:219
void add(ElementT const &element)
Definition Layer.h:31