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::ControlMemory
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// STD / STL
26#include <filesystem>
27#include <fstream>
28#include <iostream>
29
35
42
45// #include <armarx/navigation/algorithms/Costmap.h>
46// #include <armarx/navigation/memory/constants.h>
47// #include <armarx/navigation/algorithms/aron/Costmap.aron.generated.h>
48// #include <armarx/navigation/core/Graph.h>
49// #include <armarx/navigation/core/aron/Graph.aron.generated.h>
50// #include <armarx/navigation/core/aron/Location.aron.generated.h>
51// #include <armarx/navigation/core/aron/Trajectory.aron.generated.h>
52// #include <armarx/navigation/core/aron/Twist.aron.generated.h>
53// #include <armarx/navigation/graph/constants.h>
54// #include <armarx/navigation/location/constants.h>
55
56#include <armarx/control/common/control_law/aron/TaskspaceImpedanceControllerConfig.aron.generated.h>
57
58// #include <armarx/control/njoint_qp_controller/aron_conversions.h>
59
60// #include <simox/control/method/examples/json_conversions.h>
61
63{
64
65
68 {
71
72 // Publish to a topic (passing the TopicListenerPrx).
73 // def->topic(myTopicListener);
74
75 // Subscribe to a topic (passing the topic name).
76 // def->topic<PlatformUnitListener>("MyTopic");
77
78 // Use (and depend on) another component (passing the ComponentInterfacePrx).
79 // def->component(myComponentProxy)
80
81
83 def->optional(properties.snapshotToLoad,
84 "p.snapshotToLoad",
85 "Memory snapshot to load at start up \n"
86 "(e.g. 'PriorKnowledgeData/navigation-graphs/snapshot').");
87
88 def->optional(properties.locationGraph.visuFrequency,
89 "p.locationGraph.visuFrequency",
90 "Visualization frequeny of locations and graph edges [Hz].");
91
92 // core segment max history sizes
93 {
94 def->optional(properties.coreSeg.defaultParameterization.maxHistorySize,
95 "p.coreSeg.defaultParameterization.maxHistorySize",
96 "Max history size of the " +
98 DefaultParameterizationCoreSegmentName) +
99 " core segment.");
100
101 def->optional(
102 properties.coreSeg.parameterization.maxHistorySize,
103 "p.coreSeg.defaultParameterization.maxHistorySize",
104 "Max history size of the " +
106 " core segment.");
107 }
108
109 return def;
110 }
111
112 void
114 {
115 // Topics and properties defined above are automagically registered.
116
117 // Keep debug observer data until calling `sendDebugObserverBatch()`.
118 // (Requies the armarx::DebugObserverComponentPluginUser.)
119 // setDebugObserverBatchModeEnabled(true);
120
124 .setMaxHistorySize(properties.coreSeg.defaultParameterization.maxHistorySize);
125
128 .setMaxHistorySize(properties.coreSeg.parameterization.maxHistorySize);
129
130
131 /*
132 if (not properties.snapshotToLoad.empty())
133 {
134 std::filesystem::path snapshotToLoadPath(
135 ArmarXDataPath::resolvePath(properties.snapshotToLoad));
136 if (std::filesystem::is_directory(snapshotToLoadPath))
137 {
138 // This section loads the snapshot specified by the scenario parameters
139 // resolve the paths for the locations and graphs
140 const std::filesystem::path graph = snapshotToLoadPath / "Graph";
141 const std::filesystem::path location = snapshotToLoadPath / "Location";
142
143 // remove date from folder name (if present)
144 // Sometimes, we use the date before the snapshotname and use a symlink to the real data (e.g. R003 and 2022-03-01_R003)
145 auto splitted = simox::alg::split(snapshotToLoadPath.filename().string(), "_");
146 ARMARX_CHECK_GREATER(splitted.size(), 0);
147 const std::string providerName = splitted[splitted.size() - 1];
148
149 // This if statement loads the location. Each location is a single file (without extension). The filename is the name of the location.
150 // The file contains json with the globalRobotPose (4x4 matrix) and relativeToObject information
151 if (std::filesystem::is_directory(location))
152 {
153 armem::Commit c;
154 armem::MemoryID providerID = workingMemory().id();
155 providerID.coreSegmentName = "Location";
156 providerID.providerSegmentName = providerName;
157 for (const auto& subdir : std::filesystem::directory_iterator(
158 location)) // iterate over all files in folder (the locations)
159 {
160 const std::filesystem::path location = subdir.path();
161 if (std::filesystem::is_regular_file(
162 location)) // check if its a file (otherwise skip)
163 {
164 std::ifstream ifs(location);
165 const std::string content((std::istreambuf_iterator<char>(ifs)),
166 (std::istreambuf_iterator<char>()));
167
168 // parse location as json. All files in Location folder must be valid json objects!
169 nlohmann::json j = nlohmann::json::parse(content);
170
171 if (j.find("globalRobotPose") == j.end())
172 {
173 ARMARX_WARNING
174 << "The file '" << location.string()
175 << "' has no 'globalRobotPose' member. Skipping this file.";
176 continue;
177 }
178
179 if (j.find("relativeToObject") == j.end())
180 {
181 ARMARX_WARNING
182 << "The file '" << location.string()
183 << "' has no 'relativeToObject' member. Skipping this file.";
184 continue;
185 }
186
187 std::vector<float> p = j.at("globalRobotPose");
188 ARMARX_CHECK_EQUAL(p.size(), 16);
189
190 navigation::location::arondto::Location loc;
191 loc.globalRobotPose << p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
192 p[8], p[9], p[10], p[11], p[12], p[13], p[14],
193 p[15]; // load the 4x4 matrix
194
195 // TODO: All location I have seen were null.
196 // I don't know how this member should look like (von @Fabian Peller to @Fabian Reister)
197 loc.relativeToObject = std::nullopt;
198
199 // send commit to memory
200 auto& up = c.add();
201 up.confidence = 1.0;
202 up.referencedTime = armem::Time::Now();
203 up.timeSent = armem::Time::Now();
204 up.timeArrived = armem::Time::Now();
205 up.entityID = providerID.withEntityName(location.filename().string());
206 up.instancesData = {loc.toAron()};
207 }
208 }
209
210 // commit all locations at once
211 iceAdapter().commit(c);
212 }
213
214 // Next we load the graphs. The graph folder may contain several graphs, represented by different folders.
215 // Each of those graphs contains a list of files representing the vertices. The filename is the vertice id (ideally starting at 0).
216 // The file contains a json with the corresponding location name (as path) and the adjacent vertives (representing the directed outgoing edges).
217 if (std::filesystem::is_directory(graph))
218 {
219 armem::Commit c;
220 armem::MemoryID providerID = workingMemory().id();
221 providerID.coreSegmentName = memory::constants::GraphCoreSegmentName;
222 providerID.providerSegmentName = providerName;
223
224 for (const auto& graphdir : std::filesystem::directory_iterator(
225 graph)) // iterate over the different graphs (subfolders)
226 {
227 const std::filesystem::path singleGraph = graphdir.path();
228 if (std::filesystem::is_directory(
229 singleGraph)) // assure that its a folder. otherwise skip
230 {
231 navigation::core::arondto::Graph g;
232
233 for (const auto& subdir : std::filesystem::directory_iterator(
234 singleGraph)) // iterate over all files in the graph
235 {
236 const std::filesystem::path vertice = subdir.path();
237 if (std::filesystem::is_regular_file(
238 vertice)) // assure its a file. otherwise skip
239 {
240 std::ifstream ifs(vertice);
241 const std::string content((std::istreambuf_iterator<char>(ifs)),
242 (std::istreambuf_iterator<char>()));
243
244 // parse vertice. Each vertice must be a valid json object
245 nlohmann::json j = nlohmann::json::parse(content);
246 if (j.find("location") == j.end())
247 {
248 ARMARX_WARNING
249 << "The file '" << vertice.string()
250 << "' has no 'location' member. Skipping this file.";
251 continue;
252 }
253
254 if (j.find("outgoingEdges") == j.end())
255 {
256 ARMARX_WARNING << "The file '" << vertice.string()
257 << "' has no 'outgoingEdges' member. "
258 "Skipping this file.";
259 continue;
260 }
261
262 std::string location = j.at("location");
263 int id = std::stoi(vertice.filename());
264
265 auto split = simox::alg::split(location, "/");
266 ARMARX_CHECK_EQUAL(
267 split.size(),
268 4); // the location is always a path like Navigation/Location/XXX/YYY
269
270 armarx::control::core::arondto::Vertex v;
271 v.vertexID = id;
272 v.locationID.memoryName = split[0];
273 v.locationID.coreSegmentName = split[1];
274 v.locationID.providerSegmentName = split[2];
275 v.locationID.entityName = split[3];
276 toAron(v.locationID.timestamp, armem::Time::Invalid());
277 v.locationID.instanceIndex = 0;
278
279 g.vertices.push_back(v);
280
281 // add edges of this vertice to graph
282 std::vector<float> edges = j.at("outgoingEdges");
283
284 for (const auto edge_id : edges)
285 {
286 armarx::control::core::arondto::Edge e;
287 e.sourceVertexID = id;
288 e.targetVertexID = edge_id;
289 g.edges.push_back(e);
290 }
291 }
292 }
293
294 auto& up = c.add();
295 up.confidence = 1.0;
296 up.referencedTime = armem::Time::Now();
297 up.timeSent = armem::Time::Now();
298 up.timeArrived = armem::Time::Now();
299 up.entityID =
300 providerID.withEntityName(singleGraph.filename().string());
301 up.instancesData = {g.toAron()};
302 }
303 }
304
305 // send graph to memory
306 iceAdapter().commit(c);
307 }
308
309 // LEGACY CODE (Not working anymore since the wm json export changed due to ltm updates and aron updates)
310 // armem::wm::Memory memory = armem::server::ltm::disk::load(path.value());
311 //armem::server::ltm::disk::Memory ltm(path.value());
312 //armem::wm::Memory memory;
313 //ltm.loadAll(memory);
314 //workingMemory().update(armem::toCommit(memory));
315 ARMARX_INFO << "Loaded ControlMemory '" << properties.snapshotToLoad << "'";
316 }
317 else
318 {
319 ARMARX_WARNING << "Could not load ControlMemory '" << properties.snapshotToLoad
320 << "'. Continue with empty memory.";
321 }
322 }
323 */
324
325 loadDefaultConfigs();
326 }
327
328 void
329 Component::loadDefaultConfigs()
330 {
331 // std::string filename;
332 // ArmarXDataPath::getAbsolutePath("armarx_control/controller_config/default/"
333 // "njoint_controller/impedance_controller_config.json",
334 // filename);
335
336 // std::ifstream ifs{filename};
337
338 // nlohmann::json defaultConfig;
339 // ifs >> defaultConfig;
340
341 // {
342
343 // common::control_law::arondto::TaskSpaceImpedanceControllerConfig cfg;
344 // // TODO load
345
346 // armem::Commit c;
347 // armem::MemoryID providerID = workingMemory().id();
348 // providerID.coreSegmentName = memory::constants::DefaultParameterizationCoreSegmentName;
349 // providerID.providerSegmentName =
350 // common::ControllerTypeNames.to_name(common::ControllerType::TSImp);
351
352
353 // auto& up = c.add();
354 // up.confidence = 1.0;
355 // up.referencedTime = up.timeSent = up.timeArrived = armem::Time::Now();
356
357 // up.entityID = providerID.withEntityName("default");
358 // up.instancesData = {cfg.toAron()};
359
360 // iceAdapter().commit(c);
361 // }
362
363 // {
364 // njoint_qp_controller::impedance::arondto::WholeBodyImpedanceControllerConfig cfg;
365
366 // // "kpImpedance": [300, 300, 300, 3, 3, 3],
367 // // "kdImpedance": [ 50, 50, 50, 1, 1, 1],
368 // // "kpNullspaceTorque": [25.0, 15.0, 15.0, 5.0, 5.0, 5.0, 5.0, 5.0],
369 // // "kdNullspaceTorque": [4.0, 2.0, 2.0, 1.0, 1.0, 1.0, 1.0, 2.0],
370 // // "desiredNullspaceJointAngles": [],
371 // // "torqueLimit": 20,
372 // // "qvelFilter": 0.9
373
374
375 // cfg.leftArmImpedanceConfig.kpImpedance << 300, 300, 300, 3, 3, 3;
376 // cfg.leftArmImpedanceConfig.kdImpedance << 50, 50, 50, 1, 1, 1;
377 // cfg.leftArmImpedanceConfig.kpNullspaceTorque << 25.0, 15.0, 15.0, 5.0, 5.0, 5.0, 5.0, 5.0;
378 // cfg.leftArmImpedanceConfig.kdNullspaceTorque << 4.0, 2.0, 2.0, 1.0, 1.0, 1.0, 1.0, 2.0;
379 // // cfg.leftArmImpedanceConfig.desiredNullspaceJointAngles;
380 // cfg.leftArmImpedanceConfig.torqueLimit = 20;
381 // cfg.leftArmImpedanceConfig.qvelFilter = 0.9;
382
383 // cfg.rightArmImpedanceConfig = cfg.leftArmImpedanceConfig;
384
385
386 // // TODO load
387
388 // armem::Commit c;
389 // armem::MemoryID providerID = workingMemory().id();
390 // providerID.coreSegmentName = memory::constants::DefaultParameterizationCoreSegmentName;
391 // providerID.providerSegmentName =
392 // common::ControllerTypeNames.to_name(common::ControllerType::QPWholeBodyImpedance);
393
394
395 // auto& up = c.add();
396 // up.confidence = 1.0;
397 // up.referencedTime = up.timeSent = up.timeArrived = armem::Time::Now();
398
399 // up.entityID = providerID.withEntityName("default");
400 // up.instancesData = {cfg.toAron()};
401
402 // iceAdapter().commit(c);
403 // }
404
405 // CMakePackageFinder pkg_finder("armarx_control");
406 // const std::filesystem::path pkgDataDir{pkg_finder.getDataDir()};
407
408 // const std::filesystem::path configBasePath =
409 // pkgDataDir / "armarx_control/controller_config";
410
411 // for (auto const& dir_entry : std::filesystem::directory_iterator{
412 // configBasePath /
413 // common::ControllerTypeNames.to_name(common::ControllerType::QPWholeBodyVelocity)})
414 // {
415 // if (not dir_entry.is_regular_file())
416 // {
417 // continue;
418 // }
419
420 // const std::string filename = dir_entry.path();
421
422 // ARMARX_INFO << "Loading config from file `" << filename << "`";
423 // std::ifstream ifs{filename};
424
425 // nlohmann::json jsonConfig;
426 // ifs >> jsonConfig;
427
428 // njoint_qp_controller::velocity::WholeBodyVelocityControllerConfig cfg;
429 // cfg.params.weights = jsonConfig.at("weights");
430 // cfg.params.kinematicConstraints = jsonConfig.at("kinematicConstraints");
431
432 // njoint_qp_controller::velocity::arondto::WholeBodyVelocityControllerConfig aronCfg;
433 // toAron(aronCfg, cfg);
434
435
436 // armem::Commit c;
437 // armem::MemoryID providerID = workingMemory().id();
438 // providerID.coreSegmentName = memory::constants::DefaultParameterizationCoreSegmentName;
439 // providerID.providerSegmentName =
440 // common::ControllerTypeNames.to_name(common::ControllerType::QPWholeBodyVelocity);
441
442
443 // auto& up = c.add();
444 // up.confidence = 1.0;
445 // up.referencedTime = up.timeSent = up.timeArrived = armem::Time::Now();
446
447 // up.entityID = providerID.withEntityName("default");
448 // up.instancesData = {aronCfg.toAron()};
449
450 // iceAdapter().commit(c);
451 // }
452 }
453
454 void
456 {
457 // Do things after connecting to topics and components.
458
459
460 /* (Requies the armarx::DebugObserverComponentPluginUser.)
461 // Use the debug observer to log data over time.
462 // The data can be viewed in the ObserverView and the LivePlotter.
463 // (Before starting any threads, we don't need to lock mutexes.)
464 {
465 setDebugObserverDatafield("numBoxes", properties.numBoxes);
466 setDebugObserverDatafield("boxLayerName", properties.boxLayerName);
467 sendDebugObserverBatch();
468 }
469 */
470
471 // Setup the remote GUI.
472 {
475 }
476
477 tasks.visuTask = new SimpleRunningTask<>([this]() { this->visuRun(); }, "Visualization");
478 tasks.visuTask->start();
479 }
480
481 void
485
486 void
490
491 std::string
493 {
494 return GetDefaultName();
495 }
496
497 std::string
499 {
500 return "ControlMemory";
501 }
502
503 void
505 {
506 using namespace armarx::RemoteGui::Client;
507
508 // Setup the widgets.
509 tab.locationGraph.setup(*this);
510
511
512 // Setup the layout.
513 VBoxLayout root = {tab.locationGraph.group, VSpacer()};
514 RemoteGui_createTab(getName(), root, &tab);
515 }
516
517 void
519 {
520 tab.locationGraph.update(*this);
521 }
522
523 void
525 {
526 using namespace armarx::RemoteGui::Client;
527 GridLayout grid;
528 int row = 0;
529 {
530 visuLocations.setValue(owner.properties.locationGraph.visuLocations);
531 visuGraphEdges.setValue(owner.properties.locationGraph.visuLocations);
532
533 grid.add(Label("Visualize Locations"), {row, 0}).add(visuLocations, {row, 1});
534 ++row;
535
536 grid.add(Label("Visualize Graph Edges"), {row, 0}).add(visuGraphEdges, {row, 1});
537 ++row;
538 }
539
540 group = GroupBox({grid});
541 }
542
543 void
545 {
546 if (visuLocations.hasValueChanged() or visuGraphEdges.hasValueChanged())
547 {
548 std::scoped_lock lock(owner.propertiesMutex);
549 owner.properties.locationGraph.visuLocations = visuLocations.getValue();
550 owner.properties.locationGraph.visuGraphEdges = visuGraphEdges.getValue();
551 }
552 }
553
554 void
555 Component::visuRun()
556 {
557 // memory::Visu visu{arviz,
558 // workingMemory().getCoreSegment(navigation::location::coreSegmentID),
559 // workingMemory().getCoreSegment(navigation::graph::coreSegmentID)};
560
561 // Properties::LocationGraph p;
562 // {
563 // std::scoped_lock lock(propertiesMutex);
564 // p = properties.locationGraph;
565 // }
566
567 // CycleUtil cycle(static_cast<int>(1000 / p.visuFrequency));
568 // while (tasks.visuTask and not tasks.visuTask->isStopped())
569 // {
570 // {
571 // std::scoped_lock lock(propertiesMutex);
572 // p = properties.locationGraph;
573 // }
574
575 // std::vector<viz::Layer> layers;
576
577 // // Locations
578 // visu.drawLocations(layers, p.visuLocations);
579
580 // // Graph Edges
581 // visu.drawGraphs(layers, p.visuGraphEdges);
582
583 // arviz.commit(layers);
584
585 // cycle.waitForCycleDuration();
586 // }
587 }
588
590
591
592} // namespace armarx::control::components::control_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)
Default component property definition container.
Definition Component.h:70
Component()
Protected default constructor. Used for virtual inheritance. Use createManagedIceObject() instead.
Definition Component.cpp:66
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
std::string getName() const
Retrieve name of object.
CoreSegment & addCoreSegment(const std::string &name, Args... args)
void setMaxHistorySize(long maxSize)
Sets the maximum history size of entities in this container.
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Definition Component.cpp:67
This file is part of ArmarX.
Definition constants.h:27
constexpr const char * MemoryName
Definition constants.h:29
constexpr const char * DefaultParameterizationCoreSegmentName
Definition constants.h:31
constexpr const char * ParameterizationCoreSegmentName
Definition constants.h:32
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