Visu.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package Navigation::ArmarXObjects::NavigationMemory
17 * @author Rainer Kartmann ( rainer dot kartmann at kit dot edu )
18 * @date 2021
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23#include "Visu.h"
24
25#include <map>
26#include <memory>
27#include <optional>
28#include <set>
29#include <string>
30#include <utility>
31#include <vector>
32
33#include <Ice/LocalException.h>
34
35#include <range/v3/range/conversion.hpp>
36#include <range/v3/view/enumerate.hpp>
37#include <range/v3/view/filter.hpp>
38#include <range/v3/view/transform.hpp>
39
40#include <SimoxUtility/color/Color.h>
41#include <SimoxUtility/color/GlasbeyLUT.h>
42
48
61#include <RobotAPI/libraries/armem_locations/aron/Location.aron.generated.h>
64
66#include <armarx/navigation/algorithms/aron/Room.aron.generated.h>
72#include <armarx/navigation/core/aron/Graph.aron.generated.h>
75#include <armarx/navigation/human/aron/Human.aron.generated.h>
78#include <armarx/navigation/memory/aron/LaserScannerFeatures.aron.generated.h>
81
83{
84
102
104 {
105 }
106
107 void
109 const std::vector<ObjectInfo>& info,
110 viz::Layer& layer)
111 {
112 // --- critical section: convert to DTO while holding the lock ---
113 std::map<armem::MemoryID, location::arondto::Location> raw;
115 [&]()
116 {
117 using namespace armem::server;
119 [&](const wm::Entity& entity)
120 {
121 if (const wm::EntityInstance* instance = entity.findLatestInstance())
122 {
123 location::arondto::Location dto;
124 dto.fromAron(instance->data());
125 raw[entity.id()] = dto;
126 }
127 });
128 });
129
130 // --- outside lock: convert and visualize ---
131 for (const auto& [id, location] : raw)
132 {
133
134 FramedPose framedPose;
135 fromAron(location.framedPose, framedPose);
136
137 const auto res = core::resolveLocation(objects, info, framedPose);
138 if (res.pose.has_value())
139 {
140 visu->vertex->draw(layer, id.str(), res.pose.value());
141 }
142 }
143 }
144
145 void
147 const std::vector<ObjectInfo>& info,
148 std::vector<viz::Layer>& layers)
149 {
150 // --- critical section: convert to DTO while holding the lock ---
151 std::map<armem::MemoryID, navigation::core::arondto::Graph> raw;
152 graphSegment.doLocked(
153 [&]()
154 {
155 using namespace armem::server;
156 graphSegment.forEachEntity(
157 [&](const wm::Entity& entity)
158 {
159 if (const wm::EntityInstance* instance = entity.findLatestInstance())
160 {
161 navigation::core::arondto::Graph dto;
162 dto.fromAron(instance->data());
163 raw[entity.id()] = dto;
164 }
165 });
166 });
167
168 // --- outside lock: convert and visualize ---
169 for (auto& [id, dto] : raw)
170 {
171 viz::Layer& layer = layers.emplace_back(arviz.layer(id.str()));
172 core::Graph graph;
173 fromAron(dto, graph);
175 visu->draw(layer, graph, {.objects=objects, .info=info});
176 }
177 }
178
179 void
180 Visu::drawLocations(std::vector<viz::Layer>& layers)
181 {
182 viz::Layer& layer = layers.emplace_back(arviz.layer(locSegment.id().str()));
184 const std::vector<ObjectInfo> info;
185 drawLocations(objects, info, layer);
186 }
187
188 void
189 Visu::drawLocations(objpose::ObjectPoseClient& objClient, std::vector<viz::Layer>& layers)
190 {
191 viz::Layer& layer = layers.emplace_back(arviz.layer(locSegment.id().str()));
192 if (objClient.isConnected())
193 {
194 try
195 {
196 // this call might fail => "no object with id 'ObjectMemory' registered."
198
199 auto objectFinder = objClient.getObjectFinder();
200 objectFinder.setLogObjectDiscoveryError(false);
201 const std::vector<ObjectInfo> info = objectFinder.findAllObjects();
202
203 drawLocations(objects, info, layer);
204 }
205 catch (const ::Ice::NotRegisteredException& e)
206 {
207 ARMARX_VERBOSE << "Failed to retrieve objects from ObjectMemory: " << e.what();
208 }
209 }
210 }
211
212 void
213 Visu::drawGraphs(std::vector<viz::Layer>& layers)
214 {
216 const std::vector<ObjectInfo> info;
217 drawGraphs(objects, info, layers);
218 }
219
220 void
221 Visu::drawGraphs(objpose::ObjectPoseClient& objClient, std::vector<viz::Layer>& layers)
222 {
223 if (objClient.isConnected())
224 {
225 try
226 {
227 // this call might fail => "no object with id 'ObjectMemory' registered."
229
230 auto objectFinder = objClient.getObjectFinder();
231 objectFinder.setLogObjectDiscoveryError(false);
232 const std::vector<ObjectInfo> info = objectFinder.findAllObjects();
233
234 drawGraphs(objects, info, layers);
235 }
236 catch (const ::Ice::NotRegisteredException& e)
237 {
238 ARMARX_VERBOSE << "Failed to retrieve objects from ObjectMemory: " << e.what();
239 }
240 }
241 }
242
243 void
244 Visu::drawCostmaps(std::vector<viz::Layer>& layers, const float zOffset)
245 {
246 // --- critical section: convert to Costmap while holding the lock ---
247 struct RawCostmap
248 {
250 algorithms::Costmap costmap;
251 };
252
253 std::vector<RawCostmap> raw;
254
255 costmapSegment.doLocked(
256 [&]()
257 {
258 using namespace armem::server;
259 costmapSegment.forEachEntity(
260 [&](const wm::Entity& entity)
261 {
262 if (const wm::EntityInstance* instance = entity.findLatestInstance())
263 {
264 const std::string entityIdStr = instance->id().getEntityID().str();
265 const auto it = lastCostmapVisualization_.find(entityIdStr);
266 if (it == lastCostmapVisualization_.end() ||
267 it->second < instance->id().timestamp)
268 {
269 raw.push_back(
270 {instance->id(),
271 algorithms::costmapFromAron(instance->data())});
272 lastCostmapVisualization_[entityIdStr] = instance->id().timestamp;
273 }
274 }
275 });
276 });
277
278 // --- outside lock: visualize ---
279 for (auto& [id, costmap] : raw)
280 {
281
282 viz::Layer& layer = layers.emplace_back(
283 arviz.layer("costmaps_" + id.providerSegmentName + "_" + id.entityName));
284 algorithms::visualize(costmap, layer, id.entityName, zOffset);
285 }
286 }
287
288 void
289 Visu::drawHumans(std::vector<viz::Layer>& layers,
290 const bool visuTransparent,
291 const Duration maxAge)
292 {
293 struct RawHuman
294 {
295 std::string providerName;
296 DateTime referencedTime;
297 navigation::human::arondto::Human dto;
298 };
299
300 std::set<std::string> providerNames;
301 std::vector<RawHuman> raw;
302
303 const DateTime timestamp = Clock::Now();
304
305 // --- critical section: convert to DTO while holding the lock ---
306 humanSegment.doLocked(
307 [&]()
308 {
309 using namespace armem::server;
310 humanSegment.forEachEntity(
311 [&](const wm::Entity& entity)
312 {
313 providerNames.insert(entity.id().providerSegmentName);
314 entity.getLatestSnapshot().forEachInstance(
315 [&](const armarx::armem::wm::EntityInstance& instance)
316 {
317 raw.push_back(
318 {instance.id().providerSegmentName,
319 instance.metadata().referencedTime,
320 navigation::human::arondto::Human::FromAron(
321 instance.data())});
322 });
323 });
324 });
325
326 // --- outside lock: filter by age, convert, and visualize ---
327 std::map<std::string, navigation::human::Humans> namedProviderHumans;
328 for (const auto& providerName : providerNames)
329 {
330 namedProviderHumans[providerName]; // ensure layer is cleared even if no humans remain
331 }
332
333 for (const auto& entry : raw)
334 {
335 const Duration dtToNow = timestamp - entry.referencedTime;
336 if (dtToNow < maxAge and dtToNow.isPositive())
337 {
339 fromAron(entry.dto, human);
340 namedProviderHumans[entry.providerName].emplace_back(std::move(human));
341 }
342 }
343
344 for (const auto& [providerName, humans] : namedProviderHumans)
345 {
346 viz::Layer& layer = layers.emplace_back(arviz.layer("humans_" + providerName));
347 drawHumansInLayer(humans, layer, visuTransparent);
348 }
349 }
350
351 void
352 Visu::drawRooms(std::vector<viz::Layer>& layers)
353 {
354 // --- critical section: convert to DTO while holding the lock ---
355 std::map<armem::MemoryID, std::optional<navigation::algorithms::arondto::Room>> raw;
356 roomsSegment.doLocked(
357 [&]()
358 {
359 using namespace armem::server;
360 roomsSegment.forEachEntity(
361 [&](const wm::Entity& entity)
362 {
363 std::optional<navigation::algorithms::arondto::Room> dto;
364 if (const wm::EntityInstance* instance = entity.findLatestInstance())
365 {
366 auto& d = dto.emplace();
367 d.fromAron(instance->data());
368 }
369 raw[entity.id()] = dto;
370 });
371 });
372
373 // --- outside lock: convert and visualize ---
374 int i = 0;
375 for (const auto& [id, dto] : raw)
376 {
377 viz::Layer& layer = layers.emplace_back(arviz.layer(id.str()));
378 if (dto.has_value())
379 {
380 algorithms::Room room;
381 fromAron(dto.value(), room);
382 drawRoom(layer, room, simox::color::GlasbeyLUT::at(i));
383 }
384 // else: empty layer clears previous visualization
385 ++i;
386 }
387 }
388
389 void
390 Visu::drawLaserScannerFeatures(std::vector<viz::Layer>& layers)
391 {
392 static const std::string globalEntityName = "global";
393
394 viz::Layer& convexHullLayer =
395 layers.emplace_back(arviz.layer("laser_scanner_features_convex_hulls"));
396 viz::Layer& chainLayer = layers.emplace_back(arviz.layer("laser_scanner_features_chains"));
397
398 struct RawLaserScannerFeatures
399 {
401 memory::arondto::LaserScannerFeatures dto;
402 };
403
404 // --- critical section: convert to aron DTO while holding the lock ---
405 std::vector<RawLaserScannerFeatures> raw;
407 [&]()
408 {
409 using namespace armem::server;
410 laserScannerFeaturesSegment.forEachEntity(
411 [&](const wm::Entity& entity)
412 {
413 if (const wm::EntityInstance* instance = entity.findLatestInstance())
414 {
415 auto& entry = raw.emplace_back();
416 entry.id = instance->id();
417 entry.dto.fromAron(instance->data());
418 }
419 });
420 });
421
422 if (raw.empty())
423 {
424 return;
425 }
426
427
428 // --- outside lock: convert to business objects and visualize ---
429 static constexpr float zOffset = 20.F;
430
431 // The segment holds the features of several providers (e.g. laser_scanner_feature_extraction
432 // and dynamic_scene_provider). Draw each provider in its own color and 1 cm above the
433 // previous one, so that it is visible which component contributed which feature.
434 static constexpr float providerZSpacing = 10.F;
435
436 namespace rv = ranges::views;
437 const auto globalFeatures =
438 raw |
439 rv::transform(
440 [](const RawLaserScannerFeatures& r)
441 -> std::pair<armem::MemoryID, memory::LaserScannerFeatures>
442 {
444 fromAron(r.dto, features);
445 return {r.id, features};
446 }) |
447 rv::filter([](const auto& p) noexcept
448 { return p.second.frame == armarx::GlobalFrame; }) |
449 ranges::to_vector;
450
451 // Assign the color/height per provider in alphabetical order, so that a provider keeps its
452 // color across runs regardless of the order the segment is iterated in.
453 std::map<std::string, std::size_t> providerIndices;
454 for (const auto& [id, features] : globalFeatures)
455 {
456 providerIndices.emplace(id.providerSegmentName, 0);
457 }
458 {
459 std::size_t nextProviderIndex = 0;
460 for (auto& [providerSegmentName, index] : providerIndices)
461 {
462 index = nextProviderIndex++;
463 }
464 }
465
466 for (const auto& [id, features] : globalFeatures)
467 {
468 const std::string idStr = id.str();
469
470 const std::size_t provider = providerIndices.at(id.providerSegmentName);
471 const float z = zOffset + static_cast<float>(provider) * providerZSpacing;
472 const simox::Color providerColor = simox::color::GlasbeyLUT::at(provider);
473
474 ARMARX_VERBOSE << deactivateSpam(id.providerSegmentName, 5)
475 << "Drawing laser scanner features of provider "
476 << QUOTED(id.providerSegmentName) << " at z " << z << " mm";
477
478 for (const auto& [index, feature] : ranges::views::enumerate(features.features))
479 {
480
481 if (not feature.convexHull.empty())
482 {
483 viz::Polygon polygon("convex_hull_" + idStr + "_" + std::to_string(index));
484 for (const Eigen::Vector2f& pt : feature.convexHull)
485 {
486 polygon.addPoint(Eigen::Vector3f(pt.x(), pt.y(), z));
487 }
488 polygon.color(providerColor.with_alpha(80));
489 polygon.lineColor(providerColor);
490 polygon.lineWidth(3.F);
491 convexHullLayer.add(polygon);
492 }
493
494 if (feature.chain.size() >= 2)
495 {
496 std::vector<Eigen::Vector3f> pts3d;
497 pts3d.reserve(feature.chain.size());
498 for (const Eigen::Vector2f& pt : feature.chain)
499 {
500 pts3d.emplace_back(pt.x(), pt.y(), z);
501 }
502 chainLayer.add(viz::Path("chain_" + idStr + "_" + std::to_string(index))
503 .points(pts3d)
504 .width(5.F)
505 .color(providerColor));
506 }
507 }
508 }
509 }
510
511 void
512 Visu::drawRoom(viz::Layer& layer, const algorithms::Room& room, const simox::Color& color) const
513 {
514 viz::Polygon polygon(room.name + "_polygon");
515 viz::Path path(room.name + "_path");
516
517 for (const auto& point : room.polygon)
518 {
519 polygon.addPoint(conv::to3D(point));
520 path.addPoint(conv::to3D(point));
521 }
522
523 // close polygon
524 path.addPoint(conv::to3D(room.polygon.front()));
525
526 polygon.color(color.with_alpha(50));
527 polygon.lineColor(simox::Color::black());
528 polygon.lineWidth(0);
529
530 layer.add(viz::Text(room.name + "_text")
531 .text(room.name)
532 .position(conv::to3D(room.center()))
533 .scale(5));
534 layer.add(polygon);
535 layer.add(path);
536 }
537
538 void
539 Visu::drawHumansInLayer(const navigation::human::Humans& humans,
540 viz::Layer& layer,
541 const bool visuTransparent)
542 {
543 const Eigen::Translation3f human_T_mmm(Eigen::Vector3f{0, 0, 1000});
544
545 ARMARX_VERBOSE << deactivateSpam(1) << "Visualizing " << humans.size() << " humans";
546 for (const auto& [i, human] : ranges::views::enumerate(humans))
547 {
548 const std::string idx = std::to_string(i);
549 core::Pose human3d = conv::to3D(human.pose) * human_T_mmm;
550
551 viz::Robot mmm("human_" + idx);
552 mmm.file("RobotAPI", "RobotAPI/robots/MMM/mmm.xml");
553 mmm.pose(human3d);
554 mmm.scale(1.7);
555 mmm.overrideColor(viz::Color::orange(255, visuTransparent ? 100 : 255));
556 layer.add(mmm);
557
558 if (human.linearVelocity != Eigen::Vector2f::Zero())
559 {
560 core::Pose vel3d = human3d;
561 vel3d.translation().head<2>() += human.linearVelocity * 2;
562 layer.add(viz::Arrow("human_vel_" + idx)
563 .fromTo(human3d.translation(), vel3d.translation())
564 .color(simox::Color::red()));
565 }
566 }
567 }
568
569} // namespace armarx::navigation::memory
std::string timestamp()
uint8_t index
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
#define QUOTED(x)
std::string str(const T &t)
static DateTime Now()
Current time on the virtual clock.
Definition Clock.cpp:93
void setLogObjectDiscoveryError(bool logEnabled)
std::string providerSegmentName
Definition MemoryID.h:52
auto * findLatestInstance(int instanceIndex=0)
Definition EntityBase.h:356
auto doLocked(FunctionT &&function) const
Execute function under shared (read) lock.
Client-side working entity instance.
Represents a point in time.
Definition DateTime.h:25
Represents a duration.
Definition Duration.h:17
bool isPositive() const
Tests whether the duration is positive (value in µs > 0).
Definition Duration.cpp:168
void drawLaserScannerFeatures(std::vector< viz::Layer > &layers)
Definition Visu.cpp:390
std::unique_ptr< navigation::graph::GraphVisu > visu
Definition Visu.h:93
const armem::server::wm::CoreSegment & costmapSegment
Definition Visu.h:88
const armem::server::wm::CoreSegment & graphSegment
Definition Visu.h:87
const armem::server::wm::CoreSegment & roomsSegment
Definition Visu.h:90
void drawRooms(std::vector< viz::Layer > &layers)
Definition Visu.cpp:352
const armem::server::wm::CoreSegment & humanSegment
Definition Visu.h:89
viz::ScopedClient arviz
Definition Visu.h:84
const armem::server::wm::CoreSegment & locSegment
Definition Visu.h:86
void drawLocations(std::vector< viz::Layer > &layers)
Definition Visu.cpp:180
const armem::server::wm::CoreSegment & laserScannerFeaturesSegment
Definition Visu.h:91
void drawCostmaps(std::vector< viz::Layer > &layers, float zOffset)
Definition Visu.cpp:244
void drawGraphs(std::vector< viz::Layer > &layers)
Definition Visu.cpp:213
Visu(viz::Client &arviz, const armem::server::wm::CoreSegment &locSegment, const armem::server::wm::CoreSegment &graphSegment, const armem::server::wm::CoreSegment &costmapSegment, const armem::server::wm::CoreSegment &humanSegment, const armem::server::wm::CoreSegment &roomsSegment, const armem::server::wm::CoreSegment &laserScannerFeaturesSegment)
Definition Visu.cpp:85
void drawHumans(std::vector< viz::Layer > &layers, bool visuTransparent, Duration maxAge)
Definition Visu.cpp:289
Provides access to the armarx::objpose::ObjectPoseStorageInterface (aka the object memory).
const ObjectFinder & getObjectFinder() const
Get the internal object finder.
bool isConnected() const
Indicate whether this client is connected to an object pose storage.
ObjectPoseMap fetchObjectPosesAsMap() const
Fetch all known object poses.
DerivedT & color(Color color)
Definition ElementOps.h:218
DerivedT & position(float x, float y, float z)
Definition ElementOps.h:136
DerivedT & scale(Eigen::Vector3f scale)
Definition ElementOps.h:254
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:185
std::string const GlobalFrame
Variable of the global coordinate system.
Definition FramedPose.h:65
armem::wm::EntityInstance EntityInstance
Costmap costmapFromAron(const aron::data::DictPtr &dto)
void visualize(const algorithms::Costmap &costmap, viz::Layer &layer, const std::string &name, const float zOffset)
std::vector< Eigen::Vector3f > to3D(const std::vector< Eigen::Vector2f > &v)
Definition eigen.cpp:14
void resolveLocation(Graph::Vertex &vertex, const aron::data::DictPtr &locationData)
Definition Graph.cpp:267
Eigen::Isometry3f Pose
Definition basic_types.h:31
void resolveLocations(Graph &graph, const MemoryContainerT &locationContainer)
Definition Graph.h:145
This file is part of ArmarX.
Definition Visu.h:48
This file is part of ArmarX.
std::vector< Human > Humans
Definition types.h:48
void fromAron(const arondto::Circle &dto, Circle &bo)
std::map< ObjectID, ObjectPose > ObjectPoseMap
Time referencedTime
Time this instance refers to.
auto & getLatestSnapshot(int snapshotIndex=0)
Retrieve the latest entity snapshot.
std::vector< Eigen::Vector2f > polygon
Definition Room.h:37
Eigen::Vector2f center() const
Definition Room.cpp:34
void add(ElementT const &element)
Definition Layer.h:31
Polygon & lineWidth(float w)
Definition Elements.h:292
Polygon & addPoint(Eigen::Vector3f p)
Definition Elements.h:302
Polygon & lineColor(Color color)
Definition Elements.h:272
Text & text(std::string const &t)
Definition Elements.h:188