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::dynamic_scene_provider
17 * @author Fabian Reister ( fabian dot reister at kit dot edu )
18 * @date 2022
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23
24#include "Component.h"
25
26#include <string>
27#include <vector>
28
40
47
50
52{
53
54 const std::string Component::defaultName = "dynamic_scene_provider";
55
57 {
58 addPlugin(humanPoseReaderPlugin);
59 addPlugin(laserScannerFeaturesReaderPlugin);
60 addPlugin(virtualRobotReaderPlugin);
61 addPlugin(costmapReaderPlugin);
62 addPlugin(occupancyGridReaderPlugin);
63 addPlugin(humanWriterPlugin);
64 addPlugin(laserScannerFeaturesWriterPlugin);
65 }
66
69 {
72
73 // Publish to a topic (passing the TopicListenerPrx).
74 // def->topic(myTopicListener);
75
76 // Subscribe to a topic (passing the topic name).
77 // def->topic<PlatformUnitListener>("MyTopic");
78
79 // Use (and depend on) another component (passing the ComponentInterfacePrx).
80 // def->component(myComponentProxy)
81
82
83 // Add a required property. (The component won't start without a value being set.)
84 // def->required(properties.boxLayerName, "p.box.LayerName", "Name of the box layer in ArViz.");
85
86 // Add an optionalproperty.
87 def->optional(
88 properties.taskPeriodMs, "p.taskPeriodMs", "Update rate of the running task.");
89
90 def->optional(properties.laserScannerFeatures.enabled,
91 "p.laserScannerFeatures.enabled",
92 "Whether laser scanner features are used.");
93 def->optional(properties.laserScannerFeatures.providerName,
94 "p.laserScannerFeatures.providerName",
95 "");
96 def->optional(properties.laserScannerFeatures.name, "p.laserScannerFeatures.name", "");
97
98 def->required(properties.robot.name, "p.robot.name", "");
99
100 def->optional(properties.occupancyGrid.providerName, "p.occupancyGrid.providerName", "");
101 def->optional(properties.occupancyGrid.name, "p.occupancyGrid.name", "");
102 def->optional(
103 properties.occupancyGrid.freespaceThreshold, "p.occupancyGrid.freespaceThreshold", "");
104 def->optional(
105 properties.occupancyGrid.occupiedThreshold, "p.occupancyGrid.occupiedThreshold", "");
106
107 def->optional(
108 properties.humanPoses.enabled, "p.humanPoses.enabled", "Whether human poses are used.");
109 def->optional(properties.humanPoses.providerName, "p.humanPoses.providerName", "");
110
111 return def;
112 }
113
114 void
116 {
117 // Topics and properties defined above are automagically registered.
118
119 // Keep debug observer data until calling `sendDebugObserverBatch()`.
120 // (Requies the armarx::DebugObserverComponentPluginUser.)
122
123 usingProxy("RobotStateMemory");
124 }
125
126 void
128 {
129 arvizDrawer.emplace(ArVizDrawer(getArvizClient()));
130
131 // Do things after connecting to topics and components.
132
133 /* (Requies the armarx::DebugObserverComponentPluginUser.)
134 // Use the debug observer to log data over time.
135 // The data can be viewed in the ObserverView and the LivePlotter.
136 // (Before starting any threads, we don't need to lock mutexes.)
137 {
138 setDebugObserverDatafield("numBoxes", properties.numBoxes);
139 setDebugObserverDatafield("boxLayerName", properties.boxLayerName);
140 sendDebugObserverBatch();
141 }
142 */
143
144 /* (Requires the armarx::ArVizComponentPluginUser.)
145 // Draw boxes in ArViz.
146 // (Before starting any threads, we don't need to lock mutexes.)
147 drawBoxes(properties, arviz);
148 */
149
150 /* (Requires the armarx::LightweightRemoteGuiComponentPluginUser.)
151 // Setup the remote GUI.
152 {
153 createRemoteGuiTab();
154 RemoteGui_startRunningTask();
155 }
156 */
157
158 while (true)
159 {
160 robot = virtualRobotReaderPlugin->get().getRobotWaiting(properties.robot.name);
161 if (robot != nullptr)
162 {
163 break;
164 }
165
167 }
169
170 humanTracker.reset();
171
172 task = new PeriodicTask<Component>(
173 this, &Component::runPeriodically, properties.taskPeriodMs, false, "runningTask");
174 task->start();
175 }
176
177 void
179 {
180 task->stop();
181 }
182
183 void
187
188 std::string
190 {
191 return Component::defaultName;
192 }
193
194 std::string
196 {
197 return Component::defaultName;
198 }
199
200 void
201 Component::runPeriodically()
202 {
203 const auto logDuration = [this](const std::string& name, const Duration& duration)
204 { setDebugObserverDatafield("timing." + name + " [ms]", duration.toMilliSeconds()); };
205
206 const auto makeSWlog = [&](const std::string& name)
207 { return [=](const Duration& duration) { logDuration(name, duration); }; };
208
209 armarx::core::time::ScopedStopWatch sw(makeSWlog("dynamic_scene.full"));
210
211 // obtain data from perception
212 const DateTime timestamp = Clock::Now();
213
214 //
215 // Robot
216 //
218 {
219 armarx::core::time::ScopedStopWatch sw(makeSWlog("dynamic_scene.read_robot"));
220
221 if (not virtualRobotReaderPlugin->get().synchronizeRobotPose(*robot, timestamp))
222 {
223 ARMARX_INFO << "Failed to synchronize robot to timestamp " << QUOTED(timestamp) << ".";
224 return;
225 }
226
227 const core::Pose global_T_robot(robot->getGlobalPose());
228
229 ARMARX_VERBOSE << "Robot position: " << global_T_robot.translation().head<2>();
230 }
231
232 //
233 // Human
234 //
236 const std::vector<armem::human::HumanPose> humanPoses = [&]
237 {
238 if (!properties.humanPoses.enabled)
239 {
240 return std::vector<armem::human::HumanPose>{};
241 }
242
243 armarx::core::time::ScopedStopWatch sw(makeSWlog("dynamic_scene.read_human"));
244
245 ARMARX_VERBOSE << "Querying humans";
246 const armem::human::client::Reader::Query humanPoseQuery{
247 .providerName = properties.humanPoses.providerName,
248 .timestamp = timestamp,
249 .maxAge = Duration::MilliSeconds(500)};
250
251 const armem::human::client::Reader::Result humanPoseResult =
252 humanPoseReaderPlugin->get().query(humanPoseQuery);
253 ARMARX_CHECK(humanPoseResult.status !=
255 << humanPoseResult.errorMessage;
256
257 ARMARX_VERBOSE << humanPoseResult.humanPoses.size() << " humans in the scene.";
258
259 return humanPoseResult.humanPoses;
260 }();
261
262 //
263 // Laser scanner features
264 //
266 const memory::LaserScannerFeatures laserFeatures = [&]() -> memory::LaserScannerFeatures
267 {
268 if (!properties.laserScannerFeatures.enabled)
269 {
270 return memory::LaserScannerFeatures{};
271 }
272
273 armarx::core::time::ScopedStopWatch sw(makeSWlog("dynamic_scene.read_laserscanner"));
274
275 ARMARX_VERBOSE << "Querying laser scanner features";
276 const memory::client::laser_scanner_features::Reader::Query laserFeaturesQuery{
277 .providerName = properties.laserScannerFeatures.providerName,
278 .name = properties.laserScannerFeatures.name,
279 .timestamp = timestamp};
280
281 const memory::client::laser_scanner_features::Reader::Result laserFeaturesResult =
282 laserScannerFeaturesReaderPlugin->get().queryData(laserFeaturesQuery);
283
284 if (laserFeaturesResult.status !=
286 {
287 ARMARX_VERBOSE << "Querying laser scanner features failed. Reason: "
288 << laserFeaturesResult.errorMessage;
289 return {};
290 }
291
292 // "Happy" case: data is available
293 ARMARX_CHECK_EQUAL(laserFeaturesResult.status,
295 << laserFeaturesResult.errorMessage;
296
297 ARMARX_VERBOSE << laserFeaturesResult.features.size() << " clusters/features";
298
299 if (laserFeaturesResult.features.empty())
300 {
301 ARMARX_VERBOSE << "No laser scanner features available";
302 return {};
303 }
304
305 ARMARX_CHECK_EQUAL(laserFeaturesResult.features.size(), 1)
306 << "We request the merged instance with only one result";
307
308 return laserFeaturesResult.features.front();
309 }();
310
311
312 /* we don't need this at the moment
313
314 //
315 // Objects in the scene (both static and dynamic)
316 //
317 ARMARX_INFO << "Querying object poses";
318
319 const objpose::ObjectPoseSeq objectPoses = ObjectPoseClientPluginUser::getObjectPoses();
320
321 // remove those objects that belong to an object dataset. the manipulation object / distance computation is broken
322 const auto objectPosesStatic =
323 armarx::navigation::util::filterObjects(objectPoses, {"KIT", "HOPE", "MDB", "YCB"});
324
325 const auto objects = armarx::navigation::util::asSceneObjects(objectPosesStatic);
326 ARMARX_INFO << objects->getSize() << " objects in the scene";
327
328 // ARMARX_INFO << "Creating costmap";
329 // ARMARX_CHECK_NOT_NULL(scene.robot);
330
331 // algorithms::CostmapBuilder costmapBuilder(
332 // scene.robot,
333 // objects,
334 // algorithms::Costmap::Parameters{.binaryGrid = false, .cellSize = 100},
335 // "Platform-navigation-colmodel");
336
337 // // const auto costmap = costmapBuilder.create();
338
339 //
340 // Costmaps
341 //
342
343 ARMARX_INFO << "Querying costmap";
344
345 const memory::client::costmap::Reader::Query costmapQuery{
346 .providerName = "distance_to_obstacle_costmap_provider", // TODO check
347 .name = "distance_to_obstacles",
348 .timestamp = timestamp};
349
350 const memory::client::costmap::Reader::Result costmapResult =
351 costmapReaderPlugin->get().query(costmapQuery);
352
353 ARMARX_CHECK_EQUAL(costmapResult.status, memory::client::costmap::Reader::Result::Success);
354
355 ARMARX_TRACE;
356 ARMARX_CHECK(costmapResult.costmap.has_value());
357 const auto grid = costmapResult.costmap->getGrid();
358
359
360 //
361 // Occupancy grid: from SLAM component
362 //
363
364 const armem::vision::occupancy_grid::client::Reader::Result result =
365 occupancyGridReaderPlugin->get().query(
366 armem::vision::occupancy_grid::client::Reader::Query{
367 .providerName = properties.occupancyGrid.providerName,
368 .timestamp = armem::Time::Now()});
369
370 if (result and result.occupancyGrid.has_value())
371 {
372 ARMARX_INFO << "Occupancy grid available!";
373
374 const auto occupancyGridSceneElements = util::asSceneObjects(
375 result.occupancyGrid.value(),
376 OccupancyGridHelper::Params{
377 .freespaceThreshold = properties.occupancyGrid.freespaceThreshold,
378 .occupiedThreshold = properties.occupancyGrid.occupiedThreshold});
379 ARMARX_INFO << occupancyGridSceneElements->getSize()
380 << " scene elements from occupancy grid";
381
382 auto occupancyGridObstacles =
383 std::make_shared<VirtualRobot::SceneObjectSet>("OccupancyGridObstacles");
384 occupancyGridObstacles->addSceneObjects(occupancyGridSceneElements);
385
386 // draw
387 // auto layer = arviz.layer("occupancy_grid");
388
389 // for (const auto& sceneObject : occupancyGridSceneElements->getSceneObjects())
390 // {
391 // const Eigen::Isometry3f world_T_obj(sceneObject->getGlobalPose());
392 // ARMARX_INFO << world_T_obj.translation();
393 // ARMARX_INFO << layer.size();
394 // layer.add(viz::Box("box_" + std::to_string(layer.size()))
395 // .pose(world_T_obj)
396 // .size(result.occupancyGrid->resolution)
397 // .color(viz::Color::orange()));
398 // }
399
400 // ARMARX_INFO << "Creating costmap";
401
402 // algorithms::CostmapBuilder costmapBuilder(
403 // getRobot(),
404 // scene.objects,
405 // algorithms::Costmap::Parameters{.binaryGrid = false, .cellSize = 100},
406 // "Platform-navigation-colmodel");
407
408 // const auto costmap = costmapBuilder.create();
409
410 // ARMARX_INFO << "Done";
411
412 // ARMARX_TRACE;
413 // ARMARX_INFO << "Saving costmap.";
414 // algorithms::save(costmap, "/tmp/navigation-costmap");
415
416 // arviz.commit({layer});
417 }
418 */
419
420 // here ends: data fetching
421
422
423 // process data from perception and write information back to memory
424
425 //
426 // Human tracking
427 //
429 if (properties.humanPoses.enabled)
430 {
431 armarx::core::time::ScopedStopWatch sw(makeSWlog("dynamic_scene.human_tracker.camera"));
432
433 ARMARX_VERBOSE << "Running human tracker with camera measurements";
434 humanTracker.update(human::HumanTracker::CameraMeasurement{.detectionTime = timestamp,
435 .humanPoses = humanPoses});
436 }
437
438
440 const std::vector<memory::LaserScannerFeature> unusedFeatures = [&]
441 {
442 if (!properties.laserScannerFeatures.enabled)
443 {
444 return std::vector<memory::LaserScannerFeature>{};
445 }
446
447 armarx::core::time::ScopedStopWatch sw(
448 makeSWlog("dynamic_scene.human_tracker.laserscanner"));
449
450 ARMARX_VERBOSE << "Running human tracker with lasersensor measurements";
451
452 return humanTracker.update(human::HumanTracker::LaserMeasurement{
453 .detectionTime = timestamp, .clusters = laserFeatures.features});
454 }();
455
456
457 ARMARX_VERBOSE << "Human tracking done";
458
459
460 //
461 // Write dynamic scene back to memory
462 //
464 {
465 armarx::core::time::ScopedStopWatch sw(makeSWlog("dynamic_scene.write_back_human"));
466
467 //TODO use clusters for obstacle creation
468 std::vector<human::Human> humans = humanTracker.getTrackedHumans();
469
470 if (not humans.empty())
471 {
472 ARMARX_VERBOSE << "Detected " << humans.size() << " humans";
473 humanWriterPlugin->get().store(humans, getName(), timestamp);
474 }
475
476 if (not unusedFeatures.empty())
477 {
478 ARMARX_VERBOSE << "Detected " << unusedFeatures.size()
479 << " laser scanner features not associated with humans";
480 //TODO(groeger): check frame, do we need it?
481 laserScannerFeaturesWriterPlugin->get().store(
482 memory::LaserScannerFeatures{.frame = "global",
483 .frameGlobalPose = Eigen::Isometry3f::Identity(),
484 .features = unusedFeatures},
485 getName(),
486 timestamp);
487 }
488 }
489
491 }
492
493 /* (Requires the armarx::LightweightRemoteGuiComponentPluginUser.)
494 void
495 Component::createRemoteGuiTab()
496 {
497 using namespace armarx::RemoteGui::Client;
498
499 // Setup the widgets.
500
501 tab.boxLayerName.setValue(properties.boxLayerName);
502
503 tab.numBoxes.setValue(properties.numBoxes);
504 tab.numBoxes.setRange(0, 100);
505
506 tab.drawBoxes.setLabel("Draw Boxes");
507
508 // Setup the layout.
509
510 GridLayout grid;
511 int row = 0;
512 {
513 grid.add(Label("Box Layer"), {row, 0}).add(tab.boxLayerName, {row, 1});
514 ++row;
515
516 grid.add(Label("Num Boxes"), {row, 0}).add(tab.numBoxes, {row, 1});
517 ++row;
518
519 grid.add(tab.drawBoxes, {row, 0}, {2, 1});
520 ++row;
521 }
522
523 VBoxLayout root = {grid, VSpacer()};
524 RemoteGui_createTab(getName(), root, &tab);
525 }
526
527
528 void
529 Component::RemoteGui_update()
530 {
531 if (tab.boxLayerName.hasValueChanged() || tab.numBoxes.hasValueChanged())
532 {
533 std::scoped_lock lock(propertiesMutex);
534 properties.boxLayerName = tab.boxLayerName.getValue();
535 properties.numBoxes = tab.numBoxes.getValue();
536
537 {
538 setDebugObserverDatafield("numBoxes", properties.numBoxes);
539 setDebugObserverDatafield("boxLayerName", properties.boxLayerName);
540 sendDebugObserverBatch();
541 }
542 }
543 if (tab.drawBoxes.wasClicked())
544 {
545 // Lock shared variables in methods running in seperate threads
546 // and pass them to functions. This way, the called functions do
547 // not need to think about locking.
548 std::scoped_lock lock(propertiesMutex, arvizMutex);
549 drawBoxes(properties, arviz);
550 }
551 }
552 */
553
554
555 /* (Requires the armarx::ArVizComponentPluginUser.)
556 void
557 Component::drawBoxes(const Component::Properties& p, viz::Client& arviz)
558 {
559 // Draw something in ArViz (requires the armarx::ArVizComponentPluginUser.
560 // See the ArVizExample in RobotAPI for more examples.
561
562 viz::Layer layer = arviz.layer(p.boxLayerName);
563 for (int i = 0; i < p.numBoxes; ++i)
564 {
565 layer.add(viz::Box("box_" + std::to_string(i))
566 .position(Eigen::Vector3f(i * 100, 0, 0))
567 .size(20).color(simox::Color::blue()));
568 }
569 arviz.commit(layer);
570 }
571 */
572
573
575
576} // namespace armarx::navigation::components::dynamic_scene_provider
std::string timestamp()
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
#define QUOTED(x)
armarx::viz::Client & getArvizClient()
static DateTime Now()
Current time on the virtual clock.
Definition Clock.cpp:93
static void WaitFor(const Duration &duration)
Wait for a certain duration on the virtual clock.
Definition Clock.cpp:99
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
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
PluginT * addPlugin(const std::string prefix="", ParamsT &&... params)
std::string getName() const
Retrieve name of object.
The periodic task executes one thread method repeatedly using the time period specified in the constr...
Represents a point in time.
Definition DateTime.h:25
Represents a duration.
Definition Duration.h:17
Measures the time this stop watch was inside the current scope.
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Definition Component.cpp:68
static std::string GetDefaultName()
Get the component's default name.
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#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_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
Eigen::Isometry3f Pose
Definition basic_types.h:31
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
enum armarx::armem::human::client::Reader::Result::Status status
#define ARMARX_TRACE
Definition trace.h:77