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
47
60#include <RobotAPI/libraries/armem_locations/aron/Location.aron.generated.h>
63
65#include <armarx/navigation/algorithms/aron/Room.aron.generated.h>
71#include <armarx/navigation/core/aron/Graph.aron.generated.h>
74#include <armarx/navigation/human/aron/Human.aron.generated.h>
77#include <armarx/navigation/memory/aron/LaserScannerFeatures.aron.generated.h>
80
82{
83
101
103 {
104 }
105
106 void
108 const std::vector<ObjectInfo>& info,
109 viz::Layer& layer)
110 {
111 // --- critical section: convert to DTO while holding the lock ---
112 std::map<armem::MemoryID, location::arondto::Location> raw;
114 [&]()
115 {
116 using namespace armem::server;
118 [&](const wm::Entity& entity)
119 {
120 if (const wm::EntityInstance* instance = entity.findLatestInstance())
121 {
122 location::arondto::Location dto;
123 dto.fromAron(instance->data());
124 raw[entity.id()] = dto;
125 }
126 });
127 });
128
129 // --- outside lock: convert and visualize ---
130 for (const auto& [id, location] : raw)
131 {
132
133 FramedPose framedPose;
134 fromAron(location.framedPose, framedPose);
135
136 const auto res = core::resolveLocation(objects, info, framedPose);
137 if (res.pose.has_value())
138 {
139 visu->vertex->draw(layer, id.str(), res.pose.value());
140 }
141 }
142 }
143
144 void
146 const std::vector<ObjectInfo>& info,
147 std::vector<viz::Layer>& layers)
148 {
149 // --- critical section: convert to DTO while holding the lock ---
150 std::map<armem::MemoryID, navigation::core::arondto::Graph> raw;
151 graphSegment.doLocked(
152 [&]()
153 {
154 using namespace armem::server;
155 graphSegment.forEachEntity(
156 [&](const wm::Entity& entity)
157 {
158 if (const wm::EntityInstance* instance = entity.findLatestInstance())
159 {
160 navigation::core::arondto::Graph dto;
161 dto.fromAron(instance->data());
162 raw[entity.id()] = dto;
163 }
164 });
165 });
166
167 // --- outside lock: convert and visualize ---
168 for (auto& [id, dto] : raw)
169 {
170 viz::Layer& layer = layers.emplace_back(arviz.layer(id.str()));
171 core::Graph graph;
172 fromAron(dto, graph);
174 visu->draw(layer, graph, {.objects=objects, .info=info});
175 }
176 }
177
178 void
179 Visu::drawLocations(std::vector<viz::Layer>& layers)
180 {
181 viz::Layer& layer = layers.emplace_back(arviz.layer(locSegment.id().str()));
183 const std::vector<ObjectInfo> info;
184 drawLocations(objects, info, layer);
185 }
186
187 void
188 Visu::drawLocations(objpose::ObjectPoseClient& objClient, std::vector<viz::Layer>& layers)
189 {
190 viz::Layer& layer = layers.emplace_back(arviz.layer(locSegment.id().str()));
191 if (objClient.isConnected())
192 {
193 try
194 {
195 // this call might fail => "no object with id 'ObjectMemory' registered."
197
198 auto objectFinder = objClient.getObjectFinder();
199 objectFinder.setLogObjectDiscoveryError(false);
200 const std::vector<ObjectInfo> info = objectFinder.findAllObjects();
201
202 drawLocations(objects, info, layer);
203 }
204 catch (const ::Ice::NotRegisteredException& e)
205 {
206 ARMARX_VERBOSE << "Failed to retrieve objects from ObjectMemory: " << e.what();
207 }
208 }
209 }
210
211 void
212 Visu::drawGraphs(std::vector<viz::Layer>& layers)
213 {
215 const std::vector<ObjectInfo> info;
216 drawGraphs(objects, info, layers);
217 }
218
219 void
220 Visu::drawGraphs(objpose::ObjectPoseClient& objClient, std::vector<viz::Layer>& layers)
221 {
222 if (objClient.isConnected())
223 {
224 try
225 {
226 // this call might fail => "no object with id 'ObjectMemory' registered."
228
229 auto objectFinder = objClient.getObjectFinder();
230 objectFinder.setLogObjectDiscoveryError(false);
231 const std::vector<ObjectInfo> info = objectFinder.findAllObjects();
232
233 drawGraphs(objects, info, layers);
234 }
235 catch (const ::Ice::NotRegisteredException& e)
236 {
237 ARMARX_VERBOSE << "Failed to retrieve objects from ObjectMemory: " << e.what();
238 }
239 }
240 }
241
242 void
243 Visu::drawCostmaps(std::vector<viz::Layer>& layers, const float zOffset)
244 {
245 // --- critical section: convert to Costmap while holding the lock ---
246 struct RawCostmap
247 {
249 algorithms::Costmap costmap;
250 };
251
252 std::vector<RawCostmap> raw;
253
254 costmapSegment.doLocked(
255 [&]()
256 {
257 using namespace armem::server;
258 costmapSegment.forEachEntity(
259 [&](const wm::Entity& entity)
260 {
261 if (const wm::EntityInstance* instance = entity.findLatestInstance())
262 {
263 const std::string entityIdStr = instance->id().getEntityID().str();
264 const auto it = lastCostmapVisualization_.find(entityIdStr);
265 if (it == lastCostmapVisualization_.end() ||
266 it->second < instance->id().timestamp)
267 {
268 raw.push_back(
269 {instance->id(),
270 algorithms::costmapFromAron(instance->data())});
271 lastCostmapVisualization_[entityIdStr] = instance->id().timestamp;
272 }
273 }
274 });
275 });
276
277 // --- outside lock: visualize ---
278 for (auto& [id, costmap] : raw)
279 {
280
281 viz::Layer& layer = layers.emplace_back(
282 arviz.layer("costmaps_" + id.providerSegmentName + "_" + id.entityName));
283 algorithms::visualize(costmap, layer, id.entityName, zOffset);
284 }
285 }
286
287 void
288 Visu::drawHumans(std::vector<viz::Layer>& layers,
289 const bool visuTransparent,
290 const Duration maxAge)
291 {
292 struct RawHuman
293 {
294 std::string providerName;
295 DateTime referencedTime;
296 navigation::human::arondto::Human dto;
297 };
298
299 std::set<std::string> providerNames;
300 std::vector<RawHuman> raw;
301
302 const DateTime timestamp = Clock::Now();
303
304 // --- critical section: convert to DTO while holding the lock ---
305 humanSegment.doLocked(
306 [&]()
307 {
308 using namespace armem::server;
309 humanSegment.forEachEntity(
310 [&](const wm::Entity& entity)
311 {
312 providerNames.insert(entity.id().providerSegmentName);
313 entity.getLatestSnapshot().forEachInstance(
314 [&](const armarx::armem::wm::EntityInstance& instance)
315 {
316 raw.push_back(
317 {instance.id().providerSegmentName,
318 instance.metadata().referencedTime,
319 navigation::human::arondto::Human::FromAron(
320 instance.data())});
321 });
322 });
323 });
324
325 // --- outside lock: filter by age, convert, and visualize ---
326 std::map<std::string, navigation::human::Humans> namedProviderHumans;
327 for (const auto& providerName : providerNames)
328 {
329 namedProviderHumans[providerName]; // ensure layer is cleared even if no humans remain
330 }
331
332 for (const auto& entry : raw)
333 {
334 const Duration dtToNow = timestamp - entry.referencedTime;
335 if (dtToNow < maxAge and dtToNow.isPositive())
336 {
338 fromAron(entry.dto, human);
339 namedProviderHumans[entry.providerName].emplace_back(std::move(human));
340 }
341 }
342
343 for (const auto& [providerName, humans] : namedProviderHumans)
344 {
345 viz::Layer& layer = layers.emplace_back(arviz.layer("humans_" + providerName));
346 drawHumansInLayer(humans, layer, visuTransparent);
347 }
348 }
349
350 void
351 Visu::drawRooms(std::vector<viz::Layer>& layers)
352 {
353 // --- critical section: convert to DTO while holding the lock ---
354 std::map<armem::MemoryID, std::optional<navigation::algorithms::arondto::Room>> raw;
355 roomsSegment.doLocked(
356 [&]()
357 {
358 using namespace armem::server;
359 roomsSegment.forEachEntity(
360 [&](const wm::Entity& entity)
361 {
362 std::optional<navigation::algorithms::arondto::Room> dto;
363 if (const wm::EntityInstance* instance = entity.findLatestInstance())
364 {
365 auto& d = dto.emplace();
366 d.fromAron(instance->data());
367 }
368 raw[entity.id()] = dto;
369 });
370 });
371
372 // --- outside lock: convert and visualize ---
373 int i = 0;
374 for (const auto& [id, dto] : raw)
375 {
376 viz::Layer& layer = layers.emplace_back(arviz.layer(id.str()));
377 if (dto.has_value())
378 {
379 algorithms::Room room;
380 fromAron(dto.value(), room);
381 drawRoom(layer, room, simox::color::GlasbeyLUT::at(i));
382 }
383 // else: empty layer clears previous visualization
384 ++i;
385 }
386 }
387
388 void
389 Visu::drawLaserScannerFeatures(std::vector<viz::Layer>& layers)
390 {
391 static const std::string globalEntityName = "global";
392
393 viz::Layer& convexHullLayer =
394 layers.emplace_back(arviz.layer("laser_scanner_features_convex_hulls"));
395 viz::Layer& chainLayer = layers.emplace_back(arviz.layer("laser_scanner_features_chains"));
396
397 struct RawLaserScannerFeatures
398 {
400 memory::arondto::LaserScannerFeatures dto;
401 };
402
403 // --- critical section: convert to aron DTO while holding the lock ---
404 std::vector<RawLaserScannerFeatures> raw;
406 [&]()
407 {
408 using namespace armem::server;
409 laserScannerFeaturesSegment.forEachEntity(
410 [&](const wm::Entity& entity)
411 {
412 if (const wm::EntityInstance* instance = entity.findLatestInstance())
413 {
414 auto& entry = raw.emplace_back();
415 entry.id = instance->id();
416 entry.dto.fromAron(instance->data());
417 }
418 });
419 });
420
421 if (raw.empty())
422 {
423 return;
424 }
425
426
427 // --- outside lock: convert to business objects and visualize ---
428 static constexpr float zOffset = 20.F;
429
430 namespace rv = ranges::views;
431 const auto globalFeatures =
432 raw |
433 rv::transform(
434 [](const RawLaserScannerFeatures& r)
435 -> std::pair<armem::MemoryID, memory::LaserScannerFeatures>
436 {
438 fromAron(r.dto, features);
439 return {r.id, features};
440 }) |
441 rv::filter([](const auto& p) noexcept
442 { return p.second.frame == armarx::GlobalFrame; }) |
443 ranges::to_vector;
444
445 for (const auto& [id, features] : globalFeatures)
446 {
447 const std::string idStr = id.str();
448
449 for (const auto& [index, feature] : ranges::views::enumerate(features.features))
450 {
451
452 if (not feature.convexHull.empty())
453 {
454 viz::Polygon polygon("convex_hull_" + idStr + "_" + std::to_string(index));
455 for (const Eigen::Vector2f& pt : feature.convexHull)
456 {
457 polygon.addPoint(Eigen::Vector3f(pt.x(), pt.y(), zOffset));
458 }
459 polygon.color(simox::Color::red(180, 80));
460 polygon.lineColor(simox::Color::red());
461 polygon.lineWidth(3.F);
462 convexHullLayer.add(polygon);
463 }
464
465 if (feature.chain.size() >= 2)
466 {
467 std::vector<Eigen::Vector3f> pts3d;
468 pts3d.reserve(feature.chain.size());
469 for (const Eigen::Vector2f& pt : feature.chain)
470 {
471 pts3d.emplace_back(pt.x(), pt.y(), zOffset);
472 }
473 chainLayer.add(viz::Path("chain_" + idStr + "_" + std::to_string(index))
474 .points(pts3d)
475 .width(5.F)
476 .color(viz::Color::blue()));
477 }
478 }
479 }
480 }
481
482 void
483 Visu::drawRoom(viz::Layer& layer, const algorithms::Room& room, const simox::Color& color) const
484 {
485 viz::Polygon polygon(room.name + "_polygon");
486 viz::Path path(room.name + "_path");
487
488 for (const auto& point : room.polygon)
489 {
490 polygon.addPoint(conv::to3D(point));
491 path.addPoint(conv::to3D(point));
492 }
493
494 // close polygon
495 path.addPoint(conv::to3D(room.polygon.front()));
496
497 polygon.color(color.with_alpha(50));
498 polygon.lineColor(simox::Color::black());
499 polygon.lineWidth(0);
500
501 layer.add(viz::Text(room.name + "_text")
502 .text(room.name)
503 .position(conv::to3D(room.center()))
504 .scale(5));
505 layer.add(polygon);
506 layer.add(path);
507 }
508
509 void
510 Visu::drawHumansInLayer(const navigation::human::Humans& humans,
511 viz::Layer& layer,
512 const bool visuTransparent)
513 {
514 const Eigen::Translation3f human_T_mmm(Eigen::Vector3f{0, 0, 1000});
515
516 ARMARX_VERBOSE << deactivateSpam(1) << "Visualizing " << humans.size() << " humans";
517 for (const auto& [i, human] : ranges::views::enumerate(humans))
518 {
519 const std::string idx = std::to_string(i);
520 core::Pose human3d = conv::to3D(human.pose) * human_T_mmm;
521
522 viz::Robot mmm("human_" + idx);
523 mmm.file("RobotAPI", "RobotAPI/robots/MMM/mmm.xml");
524 mmm.pose(human3d);
525 mmm.scale(1.7);
526 mmm.overrideColor(viz::Color::orange(255, visuTransparent ? 100 : 255));
527 layer.add(mmm);
528
529 if (human.linearVelocity != Eigen::Vector2f::Zero())
530 {
531 core::Pose vel3d = human3d;
532 vel3d.translation().head<2>() += human.linearVelocity * 2;
533 layer.add(viz::Arrow("human_vel_" + idx)
534 .fromTo(human3d.translation(), vel3d.translation())
535 .color(simox::Color::red()));
536 }
537 }
538 }
539
540} // 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
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:389
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:351
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:179
const armem::server::wm::CoreSegment & laserScannerFeaturesSegment
Definition Visu.h:91
void drawCostmaps(std::vector< viz::Layer > &layers, float zOffset)
Definition Visu.cpp:243
void drawGraphs(std::vector< viz::Layer > &layers)
Definition Visu.cpp:212
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:84
void drawHumans(std::vector< viz::Layer > &layers, bool visuTransparent, Duration maxAge)
Definition Visu.cpp:288
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:187
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