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::human_simulator
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 <cstddef>
27 #include <cstdlib>
28 
29 #include <SimoxUtility/color/Color.h>
30 #include <VirtualRobot/CollisionDetection/CollisionModel.h>
31 #include <VirtualRobot/Random.h>
32 #include <VirtualRobot/SceneObjectSet.h>
33 
36 
40 
46 #include <RVOSimulator.h>
47 #include <Vector2.h>
48 
49 // Include headers you only need in function definitions in the .cpp.
50 
51 // #include <Eigen/Core>
52 
53 // #include <SimoxUtility/color/Color.h>
54 
55 
57 {
59  {
60  addPlugin(virtualRobotReaderPlugin);
61  addPlugin(costmapReaderPlugin);
62  addPlugin(humanWriterPlugin);
63  }
64 
65 
66  const std::string Component::defaultName = "human_simulator";
67 
68 
71  {
74 
75  // Publish to a topic (passing the TopicListenerPrx).
76  // def->topic(myTopicListener);
77 
78  // Subscribe to a topic (passing the topic name).
79  // def->topic<PlatformUnitListener>("MyTopic");
80 
81  // Use (and depend on) another component (passing the ComponentInterfacePrx).
82  // def->component(myComponentProxy)
83 
84 
85  // Add a required property. (The component won't start without a value being set.)
86  // def->required(properties.boxLayerName, "p.box.LayerName", "Name of the box layer in ArViz.");
87 
88  // Add an optionalproperty.
89  def->optional(properties.taskPeriodMs, "p.taskPeriodMs", "");
90  def->optional(properties.nHumans, "p.nHumans", "Number of humans to spawn.");
91 
92  def->optional(
93  properties.humanParams.goalDistanceThreshold, "p.human.goalDistanceThreshold", "");
94  def->optional(properties.humanParams.minLinearVelocity, "p.human.maxLinearVelocity", "");
95  def->optional(properties.humanParams.maxLinearVelocity, "p.human.maxLinearVelocity", "");
96 
97  def->optional(properties.objectPoseProviderName, "p.objectPoseProviderName", "");
98 
99  return def;
100  }
101 
102 
103  void
105  {
106  // Topics and properties defined above are automagically registered.
107 
108  // Keep debug observer data until calling `sendDebugObserverBatch()`.
109  // (Requies the armarx::DebugObserverComponentPluginUser.)
110  // setDebugObserverBatchModeEnabled(true);
111  }
112 
113  void
114  Component::setPreferredVelocities(RVO::RVOSimulator* simulator,
115  const std::vector<RVO::Vector2>& goals)
116  {
117  /* Set the preferred velocity to be a vector of unit magnitude (speed) in the
118  * direction of the goal. */
119  // #ifdef _OPENMP
120  // #pragma omp parallel for
121  // #endif /* _OPENMP */
122 
123  for (int i = 0; i < static_cast<int>(properties.nHumans); ++i)
124  {
125  // RVO::Vector2 goalVector = goals[i] - simulator->getAgentPosition(i);
126 
127  // if (RVO::absSq(goalVector) > 1.0F)
128  // {
129  // goalVector = RVO::normalize(goalVector);
130  // }
131 
132  const auto agentPosition = simulator->getAgentPosition(i);
133  auto& simulatedHuman = simulatedHumans.at(i);
134 
136  pose.translation().x() = agentPosition.x() * 1000; // [m] -> [mm]
137  pose.translation().y() = agentPosition.y() * 1000; // [m] -> [mm]
138 
139  simulatedHuman.reinitialize(pose);
140 
141  const std::vector<core::Position> trajectoryPoints =
142  simulatedHuman.trajectory().positions();
143 
144  const std::size_t lookAheadGoalIdx = std::min(trajectoryPoints.size() - 1, 5ul);
145  const auto& intermediateGoal = trajectoryPoints.at(lookAheadGoalIdx);
146 
147  const Eigen::Vector2f rvoGoalHelper = intermediateGoal.head<2>() / 1000; // [mm] -> [m]
148 
149  const RVO::Vector2 rvoGoal(rvoGoalHelper.x(), rvoGoalHelper.y());
150 
151  RVO::Vector2 goalVector = rvoGoal - agentPosition;
152 
153  if (RVO::absSq(goalVector) > 1.0F)
154  {
155  goalVector = RVO::normalize(goalVector);
156  }
157 
158  simulator->setAgentPrefVelocity(i, goalVector);
159  }
160  }
161 
162 
163  void
165  {
166  //
167  // Costmaps
168  //
169 
170  ARMARX_INFO << "Querying costmap";
171 
172  const algorithms::Costmap costmap = [&]() -> algorithms::Costmap
173  {
174  while (true)
175  {
176  const auto timestamp = Clock::Now();
177 
178  const memory::client::costmap::Reader::Query costmapQuery{
179  .providerName = "distance_to_obstacle_costmap_provider", // TODO check
180  .name = "distance_to_obstacles",
181  .timestamp = timestamp};
182 
183  const memory::client::costmap::Reader::Result costmapResult =
184  costmapReaderPlugin->get().query(costmapQuery);
185 
186  // if costmap is not available yet, wait
188  {
190  continue;
191  }
192 
193  ARMARX_CHECK_EQUAL(costmapResult.status,
195 
196  ARMARX_TRACE;
197  ARMARX_CHECK(costmapResult.costmap.has_value());
198  return costmapResult.costmap.value();
199  };
200  }();
201 
202  /// create simulation environment
203  {
204  ARMARX_INFO << "Creating sim";
205 
206  simulator.setTimeStep(static_cast<float>(properties.taskPeriodMs) / 1000);
207  simulator.setAgentDefaults(15.0F, 10U, 1.5F, .1F, .4F, .5F);
208 
209  const objpose::ObjectPoseSeq obstacles =
211  properties.objectPoseProviderName);
212 
213  const auto objects = armarx::navigation::util::asSceneObjects(obstacles);
214 
215 
216  ARMARX_VERBOSE << obstacles.size() << " obstacles";
217 
218  {
219  auto layer = arviz.layer("obstacles");
220 
221 
222  for (const auto& obst : objects->getCollisionModels())
223  {
224 
225  ARMARX_INFO << "adding obstacle ...";
226  const VirtualRobot::BoundingBox bbox = obst->getGlobalBoundingBox();
227 
228  Eigen::Vector2f bbMin = bbox.getMin().head<2>();
229  Eigen::Vector2f bbMax = bbox.getMax().head<2>();
230 
231  // add some margin to the obstacles
232  const float margin = 100; // [mm]
233 
234  bbMin.x() -= margin;
235  bbMin.y() -= margin;
236  bbMax.x() += margin;
237  bbMax.y() += margin;
238 
239  ARMARX_VERBOSE << "Bounding box: (" << bbMin.transpose() << "), ("
240  << bbMax.transpose() << ")";
241 
242  {
243  constexpr float zHeight = 3; // [mm]
244 
245  viz::Polygon polygon(std::to_string(layer.size()));
246  polygon.addPoint(Eigen::Vector3f{bbMin.x(), bbMin.y(), zHeight});
247  polygon.addPoint(Eigen::Vector3f{bbMax.x(), bbMin.y(), zHeight});
248  polygon.addPoint(Eigen::Vector3f{bbMax.x(), bbMax.y(), zHeight});
249  polygon.addPoint(Eigen::Vector3f{bbMin.x(), bbMax.y(), zHeight});
250 
251  polygon.color(simox::Color::black());
252  polygon.color(simox::Color::black());
253 
254  layer.add(polygon);
255  }
256 
257 
258  const Eigen::Vector2d min = bbMin.cast<double>() / 1000; // [mm] -> [m]
259  const Eigen::Vector2d max = bbMax.cast<double>() / 1000; // [mm] -> [m]
260 
261  std::vector<RVO::Vector2> obstacle;
262  obstacle.emplace_back(min.x(), min.y());
263  obstacle.emplace_back(max.x(), min.y());
264  obstacle.emplace_back(max.x(), max.y());
265  obstacle.emplace_back(min.x(), max.y());
266 
267  simulator.addObstacle(obstacle);
268  }
269 
270  arviz.commit(layer);
271  }
272 
273  simulator.processObstacles();
274  }
275 
276  /// create humans in simulation
277  {
278  simulatedHumans.clear();
279 
280  for (std::size_t i = 0U; i < properties.nHumans; ++i)
281  {
283  simulatedHumans.emplace_back(costmap);
284 
285  const RVO::Vector2 position =
286  RVO::Vector2(human.human().pose.translation().x() / 1000,
287  human.human().pose.translation().y() / 1000);
288  auto goal = human.trajectory().positions().back() / 1000; // [mm] -> [m]
289 
290  ARMARX_VERBOSE << "Adding agent";
291  // simulator.addAgent(position, simulator.addGoal(hrvo::Vector2(goal.x(), goal.y())));
292  simulator.addAgent(
293  position); // , simulator.addGoal(hrvo::Vector2(goal.x(), goal.y())));
294 
295  goals.emplace_back(goal.x(), goal.y());
296  }
297  }
298 
299  /// load robot and add it to the simulator
300  {
301  robot = virtualRobotReaderPlugin->get().getRobotWaiting(properties.robotName);
302  ARMARX_CHECK(
303  virtualRobotReaderPlugin->get().synchronizeRobot(*robot, armarx::Clock::Now()));
304 
305  const auto robotPosH = robot->getGlobalPosition();
306  const RVO::Vector2 robotPos(robotPosH.x() / 1000, robotPosH.y() / 1000);
307 
308  robotAgentId = simulator.addAgent(robotPos);
309  simulator.setAgentMaxSpeed(robotAgentId,
310  0); // we do not allow the robot to be perturbed
311  simulator.setAgentPrefVelocity(robotAgentId, RVO::Vector2(0, 0));
312  simulator.setAgentRadius(robotAgentId, 0.6);
313  }
314 
315  /// start task
316  task = new PeriodicTask<Component>(
317  this, &Component::runPeriodically, properties.taskPeriodMs, false, "runningTask");
318  task->start();
319  }
320 
321 
322  void
324  {
325  task->stop();
326  }
327 
328 
329  void
331  {
332  }
333 
334 
335  std::string
337  {
338  return Component::defaultName;
339  }
340 
341 
342  std::string
344  {
345  return Component::defaultName;
346  }
347 
348  void
349  Component::runPeriodically()
350  {
351  const DateTime timestamp = Clock::Now();
352 
353  setPreferredVelocities(&simulator, goals);
354 
355  // TODO(fabian.reister): check how much the agents interact => this can be an evaluation criterion
356 
357  // update robot
358  {
359  ARMARX_CHECK(
360  virtualRobotReaderPlugin->get().synchronizeRobot(*robot, armarx::Clock::Now()));
361 
362  const auto robotPosH = robot->getGlobalPosition();
363  const RVO::Vector2 robotPos(robotPosH.x() / 1000, robotPosH.y() / 1000);
364 
365  simulator.setAgentPosition(robotAgentId, robotPos);
366  }
367 
368  simulator.doStep();
369 
370  // visualize paths
371  {
372  auto layer = arviz.layer("trajectories");
373  for (const auto& simulatedHuman : simulatedHumans)
374  {
375  viz::Path path(std::to_string(layer.size()));
376 
377  for (const auto& pt : simulatedHuman.trajectory().points())
378  {
379  path.addPoint(pt.waypoint.pose.translation());
380  }
381 
382  // unique and distinct color for this human
383  path.colorGlasbeyLUT(layer.size());
384 
385  layer.add(path);
386  }
387 
388  arviz.commit(layer);
389  }
390 
391 
392  human::Humans humans;
393  for (std::size_t i = 0; i < properties.nHumans; i++)
394  {
395  const auto pos = simulator.getAgentPosition(i);
396 
397  human::Human human;
398  human.pose.setIdentity();
399  human.pose.translation().x() = pos.x() * 1000; // [m] -> [mm]
400  human.pose.translation().y() = pos.y() * 1000; // [m] -> [mm]
401 
402  humans.push_back(human);
403  }
404 
405  ARMARX_VERBOSE << "Updating humans.";
406  humanWriterPlugin->get().store(humans, getName(), timestamp);
407  }
408 
409 
410  /* (Requires the armarx::LightweightRemoteGuiComponentPluginUser.)
411  void
412  Component::createRemoteGuiTab()
413  {
414  using namespace armarx::RemoteGui::Client;
415 
416  // Setup the widgets.
417 
418  tab.boxLayerName.setValue(properties.boxLayerName);
419 
420  tab.numBoxes.setValue(properties.numBoxes);
421  tab.numBoxes.setRange(0, 100);
422 
423  tab.drawBoxes.setLabel("Draw Boxes");
424 
425  // Setup the layout.
426 
427  GridLayout grid;
428  int row = 0;
429  {
430  grid.add(Label("Box Layer"), {row, 0}).add(tab.boxLayerName, {row, 1});
431  ++row;
432 
433  grid.add(Label("Num Boxes"), {row, 0}).add(tab.numBoxes, {row, 1});
434  ++row;
435 
436  grid.add(tab.drawBoxes, {row, 0}, {2, 1});
437  ++row;
438  }
439 
440  VBoxLayout root = {grid, VSpacer()};
441  RemoteGui_createTab(getName(), root, &tab);
442  }
443 
444 
445  void
446  Component::RemoteGui_update()
447  {
448  if (tab.boxLayerName.hasValueChanged() || tab.numBoxes.hasValueChanged())
449  {
450  std::scoped_lock lock(propertiesMutex);
451  properties.boxLayerName = tab.boxLayerName.getValue();
452  properties.numBoxes = tab.numBoxes.getValue();
453 
454  {
455  setDebugObserverDatafield("numBoxes", properties.numBoxes);
456  setDebugObserverDatafield("boxLayerName", properties.boxLayerName);
457  sendDebugObserverBatch();
458  }
459  }
460  if (tab.drawBoxes.wasClicked())
461  {
462  // Lock shared variables in methods running in seperate threads
463  // and pass them to functions. This way, the called functions do
464  // not need to think about locking.
465  std::scoped_lock lock(propertiesMutex, arvizMutex);
466  drawBoxes(properties, arviz);
467  }
468  }
469  */
470 
471 
472  /* (Requires the armarx::ArVizComponentPluginUser.)
473  void
474  Component::drawBoxes(const Component::Properties& p, viz::Client& arviz)
475  {
476  // Draw something in ArViz (requires the armarx::ArVizComponentPluginUser.
477  // See the ArVizExample in RobotAPI for more examples.
478 
479  viz::Layer layer = arviz.layer(p.boxLayerName);
480  for (int i = 0; i < p.numBoxes; ++i)
481  {
482  layer.add(viz::Box("box_" + std::to_string(i))
483  .position(Eigen::Vector3f(i * 100, 0, 0))
484  .size(20).color(simox::Color::blue()));
485  }
486  arviz.commit(layer);
487  }
488  */
489 
490 
492 
493 } // namespace armarx::navigation::components::human_simulator
armarx::navigation::components::human_simulator::ARMARX_REGISTER_COMPONENT_EXECUTABLE
ARMARX_REGISTER_COMPONENT_EXECUTABLE(Component, Component::GetDefaultName())
armarx::viz::Client::commit
CommitResult commit(StagedCommit const &commit)
Definition: Client.cpp:80
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:180
Path.h
armarx::objpose::ObjectPoseSeq
std::vector< ObjectPose > ObjectPoseSeq
Definition: forward_declarations.h:20
basic_types.h
ObjectPoseClientPlugin.h
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::navigation::human::simulation::SimulatedHuman::human
human::Human & human()
Definition: SimulatedHuman.h:51
armarx::max
std::vector< T > max(const std::vector< T > &v1, const std::vector< T > &v2)
Definition: VectorHelpers.h:267
armarx::navigation::human::Human::pose
core::Pose2D pose
Definition: types.h:35
types.h
armarx::navigation::components::human_simulator::Component::onDisconnectComponent
void onDisconnectComponent() override
Definition: Component.cpp:323
armarx::ManagedIceObject::addPlugin
PluginT * addPlugin(const std::string prefix="", ParamsT &&...params)
Definition: ManagedIceObject.h:182
armarx::VariantType::Vector2
const VariantTypeId Vector2
Definition: Pose.h:37
armarx::navigation::components::human_simulator::Component::getDefaultName
std::string getDefaultName() const override
Definition: Component.cpp:336
armarx::navigation::human::simulation::SimulatedHuman
Definition: SimulatedHuman.h:40
armarx::navigation::human::Humans
std::vector< Human > Humans
Definition: types.h:45
Component.h
ARMARX_CHECK
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
Definition: ExpressionException.h:82
Elements.h
armarx::navigation::core::Pose2D
Eigen::Isometry2f Pose2D
Definition: basic_types.h:34
armarx::navigation::memory::client::costmap::Reader::Query::providerName
std::string providerName
Definition: Reader.h:42
Costmap.h
ARMARX_TRACE
#define ARMARX_TRACE
Definition: trace.h:69
GfxTL::Identity
void Identity(MatrixXX< N, N, T > *a)
Definition: MatrixXX.h:523
util.h
armarx::navigation::components::human_simulator::Component::GetDefaultName
static std::string GetDefaultName()
Get the component's default name.
Definition: Component.cpp:343
armarx::navigation::memory::client::costmap::Reader::Result::costmap
std::optional< algorithms::Costmap > costmap
Definition: Reader.h:49
armarx::navigation::human::simulation::SimulatedHuman::trajectory
core::GlobalTrajectory & trajectory()
Definition: SimulatedHuman.h:57
armarx::viz::Polygon::addPoint
Polygon & addPoint(Eigen::Vector3f p)
Definition: Elements.h:294
armarx::navigation::util::asSceneObjects
VirtualRobot::SceneObjectSetPtr asSceneObjects(const objpose::ObjectPoseSeq &objectPoses)
Definition: util.cpp:146
armarx::viz::Polygon
Definition: Elements.h:258
armarx::navigation::memory::client::costmap::Reader::Result::status
enum armarx::navigation::memory::client::costmap::Reader::Result::Status status
armarx::navigation::memory::client::costmap::Reader::Query
Definition: Reader.h:40
armarx::to_string
const std::string & to_string(const std::string &s)
Definition: StringHelpers.h:40
armarx::navigation::components::human_simulator::Component::Component
Component()
Definition: Component.cpp:58
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
armarx::navigation::memory::client::costmap::Reader::Result
Definition: Reader.h:47
armarx::navigation::components::human_simulator::Component::onExitComponent
void onExitComponent() override
Definition: Component.cpp:330
armarx::ComponentPropertyDefinitions
Default component property definition container.
Definition: Component.h:70
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
armarx::navigation::components::human_simulator::Component::onConnectComponent
void onConnectComponent() override
Definition: Component.cpp:164
armarx::ArVizComponentPluginUser::arviz
armarx::viz::Client arviz
Definition: ArVizComponentPlugin.h:43
IceUtil::Handle< class PropertyDefinitionContainer >
armarx::min
std::vector< T > min(const std::vector< T > &v1, const std::vector< T > &v2)
Definition: VectorHelpers.h:294
armarx::viz::ElementOps::color
DerivedT & color(Color color)
Definition: ElementOps.h:195
F
Definition: ExportDialogControllerTest.cpp:16
armarx::navigation::core::GlobalTrajectory::positions
std::vector< Position > positions() const noexcept
Definition: Trajectory.cpp:330
armarx::navigation::components::human_simulator::Component::createPropertyDefinitions
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Definition: Component.cpp:70
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
SimulatedHuman.h
armarx::PeriodicTask
Definition: ArmarXManager.h:70
Logging.h
min
T min(T t1, T t2)
Definition: gdiam.h:42
armarx::viz::Path
Definition: Path.h:31
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
armarx::viz::Client::layer
Layer layer(std::string const &name) const
Definition: Client.cpp:73
armarx::navigation::components::human_simulator
Definition: Component.cpp:56
armarx::navigation::components::human_simulator::Component::onInitComponent
void onInitComponent() override
Definition: Component.cpp:104
armarx::navigation::algorithms::Costmap
Definition: Costmap.h:13
armarx::core::time::Duration::MilliSeconds
static Duration MilliSeconds(std::int64_t milliSeconds)
Constructs a duration in milliseconds.
Definition: Duration.cpp:55
armarx::navigation::memory::client::costmap::Reader::Result::Success
@ Success
Definition: Reader.h:53
armarx::ObjectPoseClientPluginUser::getObjectPosesByProvider
objpose::ObjectPoseSeq getObjectPosesByProvider(const std::string &providerName)
Definition: ObjectPoseClientPlugin.cpp:75