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