Component.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 Fabian Reister ( fabian dot reister 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 "Component.h"
24
25#include <algorithm>
26#include <cstddef>
27#include <cstdint>
28#include <filesystem>
29#include <fstream>
30#include <iomanip>
31#include <iterator>
32#include <map>
33#include <memory>
34#include <mutex>
35#include <string>
36#include <utility>
37#include <vector>
38
39#include <Ice/Current.h>
40
41#include <nlohmann/json_fwd.hpp>
42
43#include <SimoxUtility/algorithm/string/string_tools.h>
44#include <VirtualRobot/Primitive.h>
45#include <VirtualRobot/Robot.h> // IWYU pragma: keep
46#include <VirtualRobot/VirtualRobot.h>
47#include <VirtualRobot/XML/RobotIO.h>
48
59#include <ArmarXCore/interface/core/PackagePath.h>
62
64
75#include <RobotAPI/libraries/armem_locations/aron/Location.aron.generated.h>
76#include <RobotAPI/libraries/aron/common/aron_conversions.h> // IWYU pragma: keep
79
80#include <armarx/navigation/algorithms/aron/Costmap.aron.generated.h>
81#include <armarx/navigation/algorithms/aron/Costmap3D.aron.generated.h>
82#include <armarx/navigation/algorithms/aron/Room.aron.generated.h>
85#include <armarx/navigation/core/aron/Graph.aron.generated.h>
86#include <armarx/navigation/core/aron/Trajectory.aron.generated.h>
88#include <armarx/navigation/human/aron/Human.aron.generated.h>
90#include <armarx/navigation/memory/aron/LaserScannerFeatures.aron.generated.h>
93
94#include "Visu.h"
95
97{
98
100
103 {
106
107 // Publish to a topic (passing the TopicListenerPrx).
108 // def->topic(myTopicListener);
109
110 // Subscribe to a topic (passing the topic name).
111 // def->topic<PlatformUnitListener>("MyTopic");
112
113 // Use (and depend on) another component (passing the ComponentInterfacePrx).
114 // def->component(myComponentProxy)
115
116
117 def->optional(properties.snapshotToLoad,
118 "p.snapshotToLoad",
119 "Snapshot to load at start up \n"
120 "(e.g. 'R003' to load 'PriorKnowledgeData/navigation-graphs/R003').");
121
122 def->optional(properties.packageToLoad,
123 "p.packageToLoad",
124 "Package to load snapshot at start up \n"
125 "(e.g. 'PriorKnowledgeData').");
126
127 def->optional(properties.locationGraph.visuLocations,
128 "p.locationGraph.visuLocation",
129 "Enable visualization of locations.");
130
131 def->optional(
132 properties.locationGraph.visuRelativeLocations,
133 "p.locationGraph.visuRelativeLocation",
134 "Enable visualization of relative locations (currently supported: robots, objects).");
135
136 def->optional(properties.locationGraph.visuGraphEdges,
137 "p.locationGraph.visuGraphEdges",
138 "Enable visualization of navigation graph edges.");
139
140 def->optional(properties.visuCostmaps,
141 "p.visuCostmaps",
142 "Enable visualization of costmaps.");
143 def->optional(properties.zOffsetCostmap,
144 "p.zOffsetCostmap",
145 "Adjust the height of the visualized costmap.");
146
147 def->optional(properties.visuHumans, "p.visuHumans", "Enable visualization of humans.");
148
149 def->optional(properties.visuTransparent,
150 "p.visuTransparent",
151 "Enable visualization of humans a bit transparent.");
152
153 def->optional(properties.visuHumanMaxAgeMs,
154 "p.visuHumanMaxAgeMs",
155 "The maximum age of humans to be drawn in ms.");
156
157 def->optional(properties.visuRooms, "p.visuRooms", "Enable visualization of rooms.");
158
159 def->optional(properties.visuLaserScannerFeatures,
160 "p.visuLaserScannerFeatures",
161 "Enable visualization of laser scanner features (global frame only).");
162
163 def->optional(properties.visuFrequency,
164 "p.visuFrequency",
165 "Visualization frequeny of locations and graph edges [Hz].");
166
167 // Core segments (max history size)
168 {
169 def->optional(
170 properties.coreSeg.parameterization.maxHistorySize,
171 "p.coreSeg.parameterization.maxHistorySize",
172 "Max history size of the " +
174 " core segment.");
175
176 def->optional(properties.coreSeg.costmap.maxHistorySize,
177 "p.coreSeg.costmap.maxHistorySize",
178 "Max history size of the " +
180 " core segment.");
181
182 def->optional(properties.coreSeg.costmap3d.maxHistorySize,
183 "p.coreSeg.costmap3d.maxHistorySize",
184 "Max history size of the " +
186 " core segment.");
187
188 def->optional(
189 properties.coreSeg.resultsGlobalPlanner.maxHistorySize,
190 "p.coreSeg.resultsGlobalPlanner.maxHistorySize",
191 "Max history size of the " +
193 " core segment.");
194
195 def->optional(properties.coreSeg.resultsLocalPlanner.maxHistorySize,
196 "p.coreSeg.resultsLocalPlanner.maxHistorySize",
197 "Max history size of the " +
199 " core segment.");
200
201 def->optional(properties.coreSeg.events.maxHistorySize,
202 "p.coreSeg.events.maxHistorySize",
203 "Max history size of the " +
205 " core segment.");
206
207 def->optional(properties.coreSeg.exceptions.maxHistorySize,
208 "p.coreSeg.exceptions.maxHistorySize",
209 "Max history size of the " +
211 " core segment.");
212
213 def->optional(properties.coreSeg.location.maxHistorySize,
214 "p.coreSeg.location.maxHistorySize",
215 "Max history size of the " +
217 " core segment.");
218
219 def->optional(properties.coreSeg.graph.maxHistorySize,
220 "p.coreSeg.graph.maxHistorySize",
221 "Max history size of the " +
223 " core segment.");
224
225 def->optional(properties.coreSeg.human.maxHistorySize,
226 "p.coreSeg.human.maxHistorySize",
227 "Max history size of the " +
229 " core segment.");
230
231 def->optional(
232 properties.coreSeg.laserScannerFeatures.maxHistorySize,
233 "p.coreSeg.laserScannerFeatures.maxHistorySize",
234 "Max history size of the " +
236 " core segment.");
237
238 def->optional(properties.coreSeg.rooms.maxHistorySize,
239 "p.coreSeg.rooms.maxHistorySize",
240 "Max history size of the " +
242 " core segment.");
243 }
244
245 return def;
246 }
247
249 {
250 workingMemory().setName("Navigation");
251 }
252
253 void
255 {
256 usingProxy("ObjectMemory");
257
258 // Topics and properties defined above are automagically registered.
259
260 // Keep debug observer data until calling `sendDebugObserverBatch()`.
261 // (Requies the armarx::DebugObserverComponentPluginUser.)
262 // setDebugObserverBatchModeEnabled(true);
263
266 .setMaxHistorySize(properties.coreSeg.parameterization.maxHistorySize);
267
270 algorithms::arondto::Costmap::ToAronType())
271 .setMaxHistorySize(properties.coreSeg.costmap.maxHistorySize);
272
275 algorithms::orientation_aware::arondto::Costmap3D::ToAronType())
276 .setMaxHistorySize(properties.coreSeg.costmap3d.maxHistorySize);
277
280 navigation::core::arondto::GlobalTrajectory::ToAronType())
281 .setMaxHistorySize(properties.coreSeg.resultsGlobalPlanner.maxHistorySize);
282
285 navigation::core::arondto::LocalTrajectory::ToAronType())
286 .setMaxHistorySize(properties.coreSeg.resultsLocalPlanner.maxHistorySize);
287 // workingMemory().addCoreSegment("Results_TrajectoryController",
288 // navigation::core::arondto::Twist::ToAronType());
289 // workingMemory().addCoreSegment("Results_SafetyGuard",
290 // navigation::core::arondto::Twist::ToAronType());
291
294 .setMaxHistorySize(properties.coreSeg.events
295 .maxHistorySize); //, armem::example::ExampleData::ToAronType());
296
299 .setMaxHistorySize(properties.coreSeg.exceptions
300 .maxHistorySize); //, armem::example::ExampleData::ToAronType());
301
304 navigation::location::arondto::Location::ToAronType())
305 .setMaxHistorySize(properties.coreSeg.location.maxHistorySize);
308 navigation::core::arondto::Graph::ToAronType())
309 .setMaxHistorySize(properties.coreSeg.graph.maxHistorySize);
310
313 navigation::human::arondto::Human::ToAronType())
314 .setMaxHistorySize(properties.coreSeg.human.maxHistorySize);
315
318 navigation::memory::arondto::LaserScannerFeatures::ToAronType())
319 .setMaxHistorySize(properties.coreSeg.laserScannerFeatures.maxHistorySize);
320
321 // workingMemory().addCoreSegment(memory::constants::HumanGroupCoreSegmentName,
322 // navigation::human::arondto::Human::ToAronType());
325 navigation::algorithms::arondto::Room::ToAronType())
326 .setMaxHistorySize(properties.coreSeg.rooms.maxHistorySize);
327
328 loadSnapshot();
329 }
330
331 void
333 {
334 // connect readers if enabled
335 // if (properties.locationGraph.visuRelativeLocations)
336 {
338 loadSpecialRooms();
339 }
340
341 // Setup the remote GUI.
342 {
345 }
346
347 tasks.visuTask = new SimpleRunningTask<>([this]() { this->visuRun(); }, "Visualization");
348 tasks.visuTask->start();
349 }
350
351 void
353 {
354 if (tasks.visuTask)
355 {
356 ARMARX_INFO << "Stopping visu task";
357 tasks.visuTask->stop();
358 tasks.visuTask = nullptr;
359 }
360 }
361
362 void
366
367 std::string
369 {
370 return GetDefaultName();
371 }
372
373 std::string
375 {
376 return "navigation_memory";
377 }
378
379 void
381 {
384
385 // Setup the widgets.
386 tab.locationGraph.setup(*this);
387
388 // Setup the layout.
389 VBoxLayout root = {tab.locationGraph.group, VSpacer()};
390 RemoteGui_createTab(getName(), root, &tab);
391 }
392
393 void
395 {
396 tab.locationGraph.update(*this);
397 }
398
399 void
401 {
406
407 GridLayout grid;
408 int row = 0;
409 {
411 reloadSnapshot.setLabel("Reload from Snapshot '" + owner.properties.snapshotToLoad +
412 "'");
413
414 visuLocations.setValue(owner.properties.locationGraph.visuLocations);
415 visuGraphEdges.setValue(owner.properties.locationGraph.visuLocations);
416
417 grid.add(reloadSnapshot, {.row = row, .column = 0}, {.rows = 1, .columns = 2});
418 ++row;
419
420 grid.add(Label("Visualize Locations"), {.row = row, .column = 0})
421 .add(visuLocations, {.row = row, .column = 1});
422 ++row;
423
424 grid.add(Label("Visualize Graph Edges"), {.row = row, .column = 0})
425 .add(visuGraphEdges, {.row = row, .column = 1});
426 ++row;
427 }
428
429 group = GroupBox({grid});
430 group.setLabel("Locations");
431 }
432
433 void
435 {
436 if (reloadSnapshot.wasClicked())
437 {
438 owner.loadSnapshot();
439 }
440 if (visuLocations.hasValueChanged() or visuGraphEdges.hasValueChanged())
441 {
442 std::scoped_lock lock(owner.propertiesMutex);
443 owner.properties.locationGraph.visuLocations = visuLocations.getValue();
444 owner.properties.locationGraph.visuGraphEdges = visuGraphEdges.getValue();
445 }
446 }
447
448 void
449 Component::visuRun()
450 {
451 memory::Visu visu{
452 arviz,
460
462 Properties p;
463 {
464 std::scoped_lock lock(propertiesMutex);
465 pp = properties.locationGraph;
466 p = properties;
467 }
468
469 Metronome metronome(Frequency::HertzDouble(p.visuFrequency));
470 while (tasks.visuTask and not tasks.visuTask->isStopped())
471 {
473
474 {
475 std::scoped_lock lock(propertiesMutex);
476 pp = properties.locationGraph;
477 }
478
479 std::vector<viz::Layer> layers;
480
481 // Locations
482 if (pp.visuLocations)
483 {
485 if (properties.locationGraph.visuRelativeLocations)
486 {
487 visu.drawLocations(objClient, layers);
488 }
489 else
490 {
491 visu.drawLocations(layers);
492 }
493 }
494
495 // Graph Edges
496 if (pp.visuGraphEdges)
497 {
499 if (properties.locationGraph.visuRelativeLocations)
500 {
501 visu.drawGraphs(objClient, layers);
502 }
503 else
504 {
505 visu.drawGraphs(layers);
506 }
507 }
508
509 // Costmaps
510 if (p.visuCostmaps)
511 {
513 visu.drawCostmaps(layers, p.zOffsetCostmap);
514 }
515
516 // Humans
517 if (p.visuHumans)
518 {
520 visu.drawHumans(layers, p.visuTransparent, Duration::MilliSeconds(p.visuHumanMaxAgeMs));
521 }
522
523 // Rooms
524 if (p.visuRooms)
525 {
527 visu.drawRooms(layers);
528 }
529
530 // Laser scanner features (global frame)
531 if (p.visuLaserScannerFeatures)
532 {
534 visu.drawLaserScannerFeatures(layers);
535 }
536
537 arviz.commit(layers);
538
539 metronome.waitForNextTick();
540 }
541 }
542
543 void
544 Component::loadSnapshot()
545 {
546 if (properties.snapshotToLoad.empty())
547 {
549 << "Not loading any predefined locations or graphs as the params was empty ...";
550 return;
551 }
552
553 const std::vector<std::string> snapshotsToLoad =
554 simox::alg::split(properties.snapshotToLoad, ";", true);
555
556 for (const auto& snapshotToLoad : snapshotsToLoad)
557 {
558
559 ARMARX_INFO << "Loading snapshots from snapshot " << QUOTED(snapshotToLoad)
560 << " in package '" << properties.packageToLoad << "' ...";
561
562 armarx::priorknowledge::navigation_graphs::NavigationGraphFinder f(
563 properties.packageToLoad,
564 armarx::priorknowledge::navigation_graphs::NavigationGraphFinder::
565 DEFAULT_DIR_TO_IDS);
566
567 if (auto graph = f.find(snapshotToLoad); graph.has_value())
568 {
569 const std::string graphId = graph->getID();
570 ARMARX_DEBUG << "Found graph " << graphId << " with path: " << graph->getFullPath();
571
572 // Sometimes, we use the date before the snapshotname.
573 // In this case a symlink to the real data should be used (e.g. R003 and 2022-03-01_R003)
574
575 // This loads the location. All locations are stored in a single locations.json file
576 // The file contains json with the framed poses (frame, agent, 4x4 matrix)
577 {
578 std::filesystem::path locationsFilePath =
579 graph->getFullPath() / "locations.json";
580 if (not std::filesystem::is_regular_file(locationsFilePath))
581 {
582 ARMARX_WARNING << "Found no locations file for graph " << QUOTED(graphId)
583 << ".";
584 }
585 else
586 {
587
588 ARMARX_DEBUG << "Found the locations file";
589 armem::Commit c;
590 armem::MemoryID providerID = workingMemory().id();
591 providerID.coreSegmentName = "Location";
592 providerID.providerSegmentName = graphId;
593
594 const auto now = armem::Time::Now();
595 ARMARX_DEBUG << "Loading " << locationsFilePath;
596
597 std::ifstream ifs(locationsFilePath);
598 const std::string content((std::istreambuf_iterator<char>(ifs)),
599 (std::istreambuf_iterator<char>()));
600
601 // parse location as json. All files in Location folder must be valid json objects!
602 nlohmann::json js = nlohmann::json::parse(content);
603
604 auto locations =
606 js);
607
608 for (const auto& location : locations)
609 {
610 // we assert that only framedLocations are allowed. Everything else will be rejected!
611 if (location->type !=
613 {
615 << "Navigation memory received location '" + location->id.name +
616 "' which is not of type "
617 "'armarx::priorknowledge::util::LocationType:"
618 ":FRAMED_LOCATION' (e.g. you specified a "
619 "FRAMED_BOXED_LOCATION?). Only FramedLocations (i.e. "
620 "poses in 3D space) are navigatable. Ignoring this "
621 "location "
622 "but please check the location.json file.";
623 continue;
624 }
625
626 auto& framedLocation =
627 dynamic_cast<armarx::priorknowledge::util::FramedLocation&>(
628 *location);
629
630 navigation::location::arondto::Location loc;
631
632 // set members of loc
633 loc.framedPose.header.frame = framedLocation.frame;
634 loc.framedPose.header.agent = framedLocation.agent;
635 loc.framedPose.pose = framedLocation.pose;
636 toAron(loc.names, framedLocation.names);
637
638 // send commit to memory
639 auto& up = c.add();
640 up.confidence = 1.0;
641 up.referencedTime = now;
642 up.sentTime = now;
643 up.arrivedTime = now;
644 up.entityID = providerID.withEntityName(framedLocation.id.name);
645 up.instancesData = {loc.toAron()};
646 }
647
648 ARMARX_DEBUG << "Sending locations to memory";
650 }
651 }
652
653 {
654 // Next we load the graphs. The graph folder may contain several graphs, represented by different json files.
655 // Each of those graphs contains a list of files representing the vertices. The filename is the vertice id (ideally starting at 0).
656 // The file contains a json with the corresponding location name (as path) and the adjacent vertives (representing the directed outgoing edges).
657 std::filesystem::path graphFilesPath = graph->getFullPath() / "Graph";
658
659 if (not std::filesystem::is_directory(graphFilesPath))
660 {
661 ARMARX_INFO << "Found no `Graph/` folder for graph " << QUOTED(graphId)
662 << ".";
663 ARMARX_INFO << "Creating empty graph.";
664
665 armem::Commit c;
666 armem::MemoryID providerID = workingMemory().id();
668 providerID.providerSegmentName = graphId;
669
670 const auto now = armem::Time::Now();
671
672 navigation::core::arondto::Graph g;
673
674 auto& up = c.add();
675 up.confidence = 1.0;
676 up.referencedTime = now;
677 up.arrivedTime = now;
678 up.sentTime = now;
679 up.entityID = providerID.withEntityName(snapshotToLoad);
680 up.instancesData = {g.toAron()};
681
682 ARMARX_DEBUG << "Sending empty graph " << QUOTED(snapshotToLoad)
683 << " to memory.";
685 }
686 else
687 {
688 ARMARX_DEBUG << "Loading graphs from " << graphFilesPath;
689
690 armem::Commit c;
691 armem::MemoryID providerID = workingMemory().id();
693 providerID.providerSegmentName = graphId;
694
695 const auto now = armem::Time::Now();
696
697 for (const auto& graphFile : std::filesystem::directory_iterator(
698 graphFilesPath)) // iterate over the different graphs (json files)
699 {
700 if (std::filesystem::is_regular_file(graphFile.path()) and
701 simox::alg::ends_with(graphFile.path().filename(),
702 ".json")) // assure that its a json file
703 {
704 const std::string graphName = graphFile.path().stem();
705
706
707 ARMARX_DEBUG << "Loading graph `" << graphName << " `from file `"
708 << graphFile.path() << "`.";
709
710 navigation::core::arondto::Graph g;
711
712 std::ifstream ifs(graphFile.path());
713 const std::string content((std::istreambuf_iterator<char>(ifs)),
714 (std::istreambuf_iterator<char>()));
715
716 const nlohmann::json j = nlohmann::json::parse(content);
717
718 const auto& jEdges = j.at("edges");
719 const auto& jVertices = j.at("vertices");
720
721 for (const auto& jVertex : jVertices)
722 {
723 const armarx::armem::MemoryID vertexLocationMemoryId(
724 jVertex.at("locationID"));
725
726 armarx::navigation::core::arondto::Vertex v;
727 v.vertexID = jVertex.at("vertexID");
728 toAron(v.locationID, vertexLocationMemoryId);
729 toAron(v.locationID.timestamp, armem::Time::Invalid());
730 v.locationID.instanceIndex = 0;
731
732 g.vertices.push_back(v);
733 }
734
735 for (const auto& jEdge : jEdges)
736 {
737 std::pair<std::int64_t, std::int64_t> edgeSourceTargetPair;
738 jEdge.get_to(edgeSourceTargetPair);
739
740 armarx::navigation::core::arondto::Edge e;
741 e.sourceVertexID = edgeSourceTargetPair.first;
742 e.targetVertexID = edgeSourceTargetPair.second;
743 g.edges.push_back(e);
744 }
745
746 auto& up = c.add();
747 up.confidence = 1.0;
748 up.referencedTime = now;
749 up.arrivedTime = now;
750 up.sentTime = now;
751 up.entityID = providerID.withEntityName(graphName);
752 up.instancesData = {g.toAron()};
753 }
754 }
755
756 ARMARX_DEBUG << "Sending graphs to memory";
758 }
759 }
760
761 {
762 // Next we load the rooms.
763 std::filesystem::path roomsFilePath = graph->getFullPath() / "rooms.json";
764 if (not std::filesystem::is_regular_file(roomsFilePath))
765 {
766 ARMARX_INFO << "Found no rooms file for graph " << QUOTED(graphId) << ".";
767 }
768 else
769 {
770 ARMARX_DEBUG << "Found the rooms file";
771
772 armem::Commit c;
773 armem::MemoryID providerID = workingMemory().id();
774 providerID.coreSegmentName =
776 providerID.providerSegmentName = graphId;
777
778 const auto now = armem::Time::Now();
779 ARMARX_VERBOSE << "Loading " << roomsFilePath;
780
781 std::ifstream ifs(roomsFilePath);
782 const std::string content((std::istreambuf_iterator<char>(ifs)),
783 (std::istreambuf_iterator<char>()));
784
785 // parse room. TODO: Move into PriorKnowledge similar to locations
786 const nlohmann::json js = nlohmann::json::parse(content);
787
788 const auto rooms =
789 js.at("rooms").get<std::map<std::string, nlohmann::json>>();
790 ARMARX_INFO << "Found " << rooms.size() << " rooms.";
791 for (const auto& [roomId, j] : rooms)
792 {
793 auto roomMemoryId = providerID;
794 roomMemoryId.entityName = roomId;
795
796 const std::string roomMemoryIdStr = roomMemoryId.str();
797
798 if (j.find("polygon") == j.end())
799 {
800 ARMARX_WARNING << "The element '" << roomMemoryIdStr
801 << "' has no 'polygon' member. Skipping "
802 "this entity.";
803 continue;
804 }
805
806 if (j.find("height") == j.end())
807 {
808 ARMARX_WARNING << "The element '" << roomMemoryIdStr
809 << "' has no 'height' member. "
810 "Skipping this entity.";
811 continue;
812 }
813
814 navigation::algorithms::arondto::Room room;
815 j.at("polygon").get_to(room.polygon);
816 j.at("height").get_to(room.height);
817 room.name = roomId;
818
819 // set the polygon to z=0
820 for (auto& point : room.polygon)
821 {
822 point.z() = 0;
823 }
824
825 // send commit to memory
826 auto& up = c.add();
827 up.confidence = 1.0;
828 up.referencedTime = now;
829 up.sentTime = now;
830 up.arrivedTime = now;
831 up.entityID = armarx::armem::MemoryID(roomMemoryIdStr);
832 up.instancesData = {room.toAron()};
833 }
834
835 ARMARX_DEBUG << "Sending rooms to memory";
837 }
838 }
839 ARMARX_INFO << "Loaded NavigationMemory " << QUOTED(snapshotToLoad)
840 << " --> graph: '" << graphId << "'";
841 }
842 else
843 {
844 ARMARX_WARNING << "Could not load NavigationMemory '" << snapshotToLoad
845 << "'. Continue with empty memory.";
846 }
847 }
848 }
849
850 namespace
851 {
852
853 [[maybe_unused]]
854 bool
855 store(const std::map<armem::MemoryID, core::Graph>& graphs,
856 const std::filesystem::path& baseDirectory)
857 {
858 ARMARX_INFO << "Creating export directory " << QUOTED(baseDirectory) << ".";
859 std::filesystem::create_directories(baseDirectory);
860
861 for (const auto& [memoryId, graph] : graphs)
862 {
863 const std::filesystem::path nestedSubDir =
864 std::filesystem::path(memoryId.providerSegmentName) / memoryId.coreSegmentName;
865 const std::filesystem::path dir = baseDirectory / nestedSubDir;
866
867 std::filesystem::create_directories(dir);
868
869 nlohmann::json j;
870
871 // source -> target
872 std::vector<std::pair<std::size_t, std::size_t>> outgoingEdges;
873 std::transform(graph.m_edges.begin(),
874 graph.m_edges.end(),
875 std::back_inserter(outgoingEdges),
876 [](const auto& edge)
877 { return std::make_pair(edge.m_source, edge.m_target); });
878
879 j["edges"] = outgoingEdges;
880 j["vertices"] = {};
881
882 for (const auto& vertex : graph.m_vertices)
883 {
884
885 armarx::armem::MemoryID locationId;
886 fromAron(vertex.m_property.aron.locationID, locationId);
887
888 nlohmann::json jVertex;
889 jVertex["locationID"] = locationId.getEntityID().str();
890 jVertex["vertexID"] = vertex.m_property.aron.vertexID;
891
892 j["vertices"].push_back(jVertex);
893 }
894
895 // save to disk
896 const std::filesystem::path filename = dir / (memoryId.entityName + ".json");
897 ARMARX_VERBOSE << "Saving file " << QUOTED(filename) << ".";
898 std::ofstream ofs(filename);
899 ofs << std::setw(4) << j;
900 }
901
902 return true;
903 }
904
905 bool
906 store(const std::map<armem::MemoryID, location::arondto::Location>& locations,
907 const std::filesystem::path& baseDirectory)
908 {
909 ARMARX_INFO << "Creating export directory " << QUOTED(baseDirectory) << ".";
910 std::filesystem::create_directories(baseDirectory);
911
912 // key: provider id
913 std::map<std::string, nlohmann::json> js;
914
915 for (const auto& [memoryId, location] : locations)
916 {
917 auto& j = js[memoryId.providerSegmentName];
918
919 if (j.count("locations") == 0)
920 {
921 // we explicitly use a map here to ensure that the locations are
922 // stored sorted by name
923 j["locations"] = std::map<std::string, nlohmann::json>{};
924 }
925
926 nlohmann::json jLoc;
927 nlohmann::json framedPose;
928
929 framedPose["agent"] = location.framedPose.header.agent;
930 framedPose["frame"] = location.framedPose.header.frame;
931
932 // utilize ice conversion of eigen
933 std::vector<std::vector<float>> poseAsVec;
934 armarx::core::eigen::toIce(location.framedPose.pose, poseAsVec);
935 framedPose["pose"] = poseAsVec;
936
937 jLoc["framedPose"] = framedPose;
938
939 auto entityId = memoryId.getEntityID();
940 j["locations"][entityId.entityName] = jLoc;
941 }
942
943 // save to disk
944 for (const auto& [providerId, j] : js)
945 {
946 const std::filesystem::path subDir = std::filesystem::path(providerId);
947 const std::filesystem::path dir = baseDirectory / subDir;
948
949 if (not std::filesystem::exists(dir))
950 {
951 std::filesystem::create_directories(dir);
952 }
953
954 const std::filesystem::path filename = dir / "locations.json";
955 ARMARX_VERBOSE << "Saving file " << QUOTED(filename) << ".";
956 std::ofstream ofs(filename);
957 ofs << std::setw(4) << j;
958 }
959
960
961 return true;
962 }
963
964 } // namespace
965
966 bool
967 Component::storeLocationGraph(const armarx::data::PackagePath& packagePath,
968 const Ice::Current& /*current*/)
969 {
970 armem::server::wm::CoreSegment& locationCoreSegment =
972 armem::server::wm::CoreSegment& graphCoreSegment =
974
975 // obtain locations and graphs
976
977 const std::map<armem::MemoryID, location::arondto::Location> locations =
978 [&locationCoreSegment]()
979 {
980 std::map<armem::MemoryID, location::arondto::Location> locations;
981 locationCoreSegment.doLocked(
982 [&]()
983 {
984 locationCoreSegment.forEachEntity(
985 [&](const armarx::armem::server::wm::Entity& entity) -> bool
986 {
988 entity.findLatestInstance())
989 {
990 locations[entity.id()].fromAron(instance->data());
991 }
992
993 return true;
994 });
995
996 return true;
997 });
998
999 return locations;
1000 }();
1001
1002 const auto graphs = [&graphCoreSegment]()
1003 {
1004 std::map<armem::MemoryID, core::Graph> graphs;
1005 graphCoreSegment.doLocked(
1006 [&]()
1007 {
1008 graphCoreSegment.forEachEntity(
1009 [&](const armarx::armem::server::wm::Entity& entity) -> bool
1010 {
1011 core::Graph& graph = graphs[entity.id()];
1012 if (const armarx::armem::server::wm::EntityInstance* instance =
1013 entity.findLatestInstance())
1014 {
1015 navigation::core::arondto::Graph aron;
1016 aron.fromAron(instance->data());
1018 }
1019
1020 return true;
1021 });
1022 });
1023
1024 return graphs;
1025 }();
1026
1027
1028 // store on disk
1029
1030 const std::filesystem::path baseDirectory = armarx::PackagePath(packagePath).toSystemPath();
1031
1032 store(locations, baseDirectory);
1033
1034 // The graph is no longer relevant.
1035 // store(graphs, baseDirectory);
1036
1037 return true;
1038 }
1039
1040 void
1041 Component::loadSpecialRooms()
1042 {
1043 armem::Commit c; // will be populated and sent to the memory at the end
1044
1045 // special handling of object affordances that are actually rooms
1046 {
1047 const auto objectFinder = objClient.getObjectFinder();
1048 const auto objectPoseMap = objClient.fetchObjectPosesAsMap();
1049
1050 for (const auto& [providerName, objectPose] : objectPoseMap)
1051 {
1052 const auto objectInfoOpt = objectFinder.findObject(objectPose.objectID);
1053 if (not objectInfoOpt)
1054 {
1055 ARMARX_WARNING << "Could not find object with id '" << objectPose.objectID
1056 << "'.";
1057 continue;
1058 }
1059
1060 const auto& objectInfo = *objectInfoOpt;
1061
1062 // TODO: handle non-articulated model similar to articulated model below
1063
1064 // load the articulated model and extract the room affordances (type: containable, name: room)
1065
1066 const auto articulatedModelPathOpt = objectInfo.getArticulatedModel();
1067 if (not articulatedModelPathOpt)
1068 {
1069 ARMARX_VERBOSE << "Object '" << objectPose.objectID
1070 << "' is not an articulated model.";
1071 continue;
1072 }
1073
1074 const auto& articulatedModelPath = *articulatedModelPathOpt;
1075 if (not std::filesystem::is_regular_file(articulatedModelPath.absolutePath))
1076 {
1077 ARMARX_VERBOSE << "Object '" << objectPose.objectID
1078 << "' has no articulated model.";
1079 continue;
1080 }
1081
1082 // load the model
1083 VirtualRobot::RobotPtr articulatedObject =
1084 VirtualRobot::RobotIO::loadRobot(articulatedModelPath.absolutePath.string(),
1085 VirtualRobot::RobotIO::eStructure);
1086 ARMARX_CHECK_NOT_NULL(articulatedObject)
1087 << QUOTED(articulatedModelPath.absolutePath.string());
1088
1089 // synchronize state
1090 articulatedObject->setJointValues(objectPose.objectJointValues);
1091 articulatedObject->setGlobalPose(objectPose.objectPoseGlobal);
1092
1093 // extract the room affordances
1094 const auto affordances = articulatedObject->getAffordances();
1095 for (const auto& affordanceLocation : affordances)
1096 {
1097 for (const auto& affordance : affordanceLocation.affordances)
1098 {
1099 if (simox::alg::to_lower(affordance.type) == "containable")
1100 {
1101 // check if the affordance is a room
1102 // if (simox::alg::to_lower(affordanceLocation.name) == "room")
1103 {
1104 for (const std::shared_ptr<VirtualRobot::Primitive::Primitive>&
1105 primitive : affordance.primitiveRepresentation)
1106 {
1107 if (primitive->type == VirtualRobot::Primitive::Box::TYPE)
1108 {
1109 VirtualRobot::Primitive::Box* box =
1110 std::dynamic_pointer_cast<VirtualRobot::Primitive::Box>(
1111 primitive)
1112 .get();
1113
1115
1116 const Eigen::Isometry3f& root_T_prim =
1117 affordanceLocation.pose.pose;
1118 const Eigen::Isometry3f global_T_prim =
1119 Eigen::Isometry3f{articulatedObject->getGlobalPose()} *
1120 root_T_prim;
1121
1122 const Eigen::Isometry3f prim_T_affordance{
1123 primitive->transform};
1124 const Eigen::Isometry3f global_T_affordance =
1125 global_T_prim * prim_T_affordance;
1126
1127 const Eigen::Vector3f boxSize{box->width,
1128 box->height,
1129 box->depth};
1130
1131 // we want to extract the polygon in the xy-plane
1132
1133 // Local corner points (clock-wise)
1134 const Eigen::Vector3f corner1 =
1135 Eigen::Vector3f{boxSize.x() / 2,
1136 boxSize.y() / 2,
1137 -boxSize.z() / 2};
1138 const Eigen::Vector3f corner2 =
1139 Eigen::Vector3f{-boxSize.x() / 2,
1140 boxSize.y() / 2,
1141 -boxSize.z() / 2};
1142 const Eigen::Vector3f corner3 =
1143 Eigen::Vector3f{-boxSize.x() / 2,
1144 -boxSize.y() / 2,
1145 -boxSize.z() / 2};
1146 const Eigen::Vector3f corner4 =
1147 Eigen::Vector3f{boxSize.x() / 2,
1148 -boxSize.y() / 2,
1149 -boxSize.z() / 2};
1150
1151 // FIXME: strong assumption: the box is axis aligned. relax
1152 // obtain the corner points of the box in the xy-plane
1153 std::vector<Eigen::Vector3f> polygon;
1154 polygon.reserve(4);
1155 polygon.emplace_back(global_T_affordance * corner1);
1156 polygon.emplace_back(global_T_affordance * corner2);
1157 polygon.emplace_back(global_T_affordance * corner3);
1158 polygon.emplace_back(global_T_affordance * corner4);
1159
1160 // create a room object
1161 navigation::algorithms::arondto::Room room;
1162 room.name = "inside";
1163 room.polygon = polygon;
1164 room.height = boxSize.z();
1165
1166 // send commit to memory
1167 armem::MemoryID providerID = workingMemory().id();
1168 providerID.coreSegmentName =
1170 providerID.providerSegmentName =
1171 objectPose.objectID.dataset() +
1172 "::" + objectPose.objectID.className();
1173
1174 const auto now = armem::Time::Now();
1175
1176 auto& up = c.add();
1177 up.confidence = 1.0;
1178 up.referencedTime = now;
1179 up.sentTime = now;
1180 up.arrivedTime = now;
1181 up.entityID = providerID.withEntityName(room.name);
1182 up.instancesData = {room.toAron()};
1183 }
1184 else
1185 {
1186 ARMARX_INFO << "Not handling affordance of type "
1187 << primitive->type;
1188 continue;
1189 }
1190 }
1191 }
1192 }
1193 }
1194 }
1195
1196 // check whether a specific areas.json exists
1197 const auto areas = objectInfo.file(".json", "_areas", true);
1198 ARMARX_VERBOSE << "Checking for areas.json file at " << areas.absolutePath;
1199 if (std::filesystem::exists(areas.absolutePath))
1200 {
1201 ARMARX_INFO << "Found areas.json file for object '" << objectPose.objectID
1202 << "'.";
1203
1204 // load json
1205 std::ifstream ifs(areas.absolutePath);
1206 const auto json = nlohmann::json::parse(ifs);
1207
1208 const std::map<std::string, nlohmann::json> jAreas = json.at("areas");
1209 ARMARX_INFO << "Found " << jAreas.size() << " areas.";
1210
1211 for (const auto& [areaName, jArea] : jAreas)
1212 {
1213 const std::vector<Eigen::Vector2f> polygon =
1214 jArea.at("polygon").get<std::vector<Eigen::Vector2f>>();
1215 const float height = jArea.at("height").get<float>();
1216
1217 // send commit to memory
1218 armem::MemoryID providerID = workingMemory().id();
1219 providerID.coreSegmentName =
1221 providerID.providerSegmentName =
1222 objectPose.objectID.dataset() + "::" + objectPose.objectID.className();
1223
1224 const auto now = armem::Time::Now();
1225
1226 // create a room object
1227 navigation::algorithms::arondto::Room room;
1228 room.name =
1229 objectPose.objectID.getClassID().withInstanceName(areaName).str();
1230 room.polygon = conv::to3D(polygon); // at this point relative to the object
1231 room.height = height;
1232
1233 // change the room to the global coordinate system
1234 const Eigen::Isometry3f global_T_object{objectPose.objectPoseGlobal};
1235 for (auto& point : room.polygon)
1236 {
1237 // see comment below: point = global_T_object * point;
1238 point = global_T_object * point;
1239 }
1240
1241 // If you want to convert globalliy-defined rooms to local coordinates,
1242 // change the for loop above (comment it out) and uncomment the line below:
1243 // point = global_T_object.inverse() * point;
1244 // Then, comment in the lines below and read out the local cooredinates in the
1245 // ArmarX log.
1246
1247 // nlohmann::json jj;
1248 // jj["polygon"] = conv::to2D(room.polygon);
1249 // ARMARX_INFO << areaName << " " << jj.dump(4);
1250
1251 auto& up = c.add();
1252 up.confidence = 1.0;
1253 up.referencedTime = now;
1254 up.sentTime = now;
1255 up.arrivedTime = now;
1256 up.entityID = providerID.withEntityName(areaName);
1257 up.instancesData = {room.toAron()};
1258 }
1259 }
1260 }
1261 }
1262
1263 if (c.updates.empty())
1264 {
1265 ARMARX_INFO << "No special rooms found.";
1266 return;
1267 }
1268
1269 ARMARX_INFO << "Sending " << c.updates.size() << " special rooms to memory";
1270 iceAdapter().commit(c);
1271 }
1272} // namespace armarx::navigation::components::navigation_memory
int Label(int n[], int size, int *curLabel, MiscLib::Vector< std::pair< int, size_t > > *labels)
Definition Bitmap.cpp:801
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
#define QUOTED(x)
constexpr T c
Default component property definition container.
Definition Component.h:70
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
static Duration MilliSeconds(std::int64_t milliSeconds)
Constructs a duration in milliseconds.
Definition Duration.cpp:48
static Frequency HertzDouble(double hertz)
Definition Frequency.cpp:30
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
std::string getName() const
Retrieve name of object.
objpose::ObjectPoseClient getClient() const
static std::filesystem::path toSystemPath(const data::PackagePath &pp)
std::string coreSegmentName
Definition MemoryID.h:51
std::string str(bool escapeDelimiters=true) const
Get a string representation of this memory ID.
Definition MemoryID.cpp:102
MemoryID withEntityName(const std::string &name) const
Definition MemoryID.cpp:425
std::string entityName
Definition MemoryID.h:53
std::string providerSegmentName
Definition MemoryID.h:52
MemoryID getEntityID() const
Definition MemoryID.cpp:310
auto * findLatestInstance(int instanceIndex=0)
Definition EntityBase.h:356
void setName(const std::string &name)
Definition MemoryBase.h:248
CoreSegmentT & getCoreSegment(const std::string &name)
Definition MemoryBase.h:134
data::CommitResult commitLocking(const data::Commit &commitIce, Time timeArrived)
data::CommitResult commit(const data::Commit &commitIce, Time timeArrived)
auto doLocked(FunctionT &&function) const
Execute function under shared (read) lock.
CoreSegment & addCoreSegment(const std::string &name, Args... args)
void setMaxHistorySize(long maxSize)
Sets the maximum history size of entities in this container.
static DateTime Now()
Definition DateTime.cpp:51
static DateTime Invalid()
Definition DateTime.cpp:57
Simple rate limiter for use in loops to maintain a certain frequency given a clock.
Definition Metronome.h:57
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
bool storeLocationGraph(const armarx::data::PackagePath &packagePath, const Ice::Current &current) override
static std::vector< LocationPtr > LoadLocations(const std::string &dataset, const nlohmann::json &)
CommitResult commit(StagedCommit const &commit)
Definition Client.cpp:89
#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_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
std::shared_ptr< class Robot > RobotPtr
Definition Bus.h:19
void store(const mongocxx::database &db, const armem::wm::Memory &m)
armem::wm::EntityInstance EntityInstance
void toIce(const Eigen::Vector2f &e, Ice::FloatSeq &ice)
double v(double t, double v0, double a0, double j)
Definition CtrlUtil.h:39
std::vector< Eigen::Vector3f > to3D(const std::vector< Eigen::Vector2f > &v)
Definition eigen.cpp:14
This file is part of ArmarX.
Definition Visu.h:48
const armem::MemoryID coreSegmentID
Definition constants.h:30
const armem::MemoryID coreSegmentID
Definition constants.h:30
constexpr const char * GlobalPlannerResultCoreSegment
Definition constants.h:42
constexpr const char * EventsCoreSegmentName
Definition constants.h:33
constexpr const char * GraphCoreSegmentName
Definition constants.h:34
constexpr const char * LaserScannerFeaturesCoreSegment
Definition constants.h:39
constexpr const char * Costmap3DCoreSegmentName
Definition constants.h:37
constexpr const char * ParameterizationCoreSegmentName
Definition constants.h:32
constexpr const char * HumanCoreSegmentName
Definition constants.h:38
constexpr const char * LocalPlannerResultCoreSegment
Definition constants.h:43
constexpr const char * ExceptionsCoreSegmentName
Definition constants.h:40
constexpr const char * CostmapCoreSegmentName
Definition constants.h:36
const armem::MemoryID coreSegmentID
Definition constants.h:10
void toAron(arondto::PackagePath &dto, const PackageFileLocation &bo)
void fromAron(const arondto::PackagePath &dto, PackageFileLocation &bo)
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
SimpleRunningTask(Ts...) -> SimpleRunningTask< std::function< void(void)> >
void RemoteGui_createTab(std::string const &name, RemoteGui::Client::Widget const &rootWidget, RemoteGui::Client::Tab *tab)
GridLayout & add(Widget const &child, Pos pos, Span span=Span{1, 1})
Definition Widgets.cpp:438
A bundle of updates to be sent to the memory.
Definition Commit.h:90
#define ARMARX_TRACE
Definition trace.h:77