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