30 #include <Eigen/Geometry>
32 #include <SimoxUtility/shapes/AxisAlignedBoundingBox.h>
33 #include <SimoxUtility/shapes/OrientedBox.h>
34 #include <VirtualRobot/BoundingBox.h>
35 #include <VirtualRobot/Robot.h>
36 #include <VirtualRobot/SceneObjectSet.h>
37 #include <VirtualRobot/VirtualRobot.h>
38 #include <VirtualRobot/Visualization/CoinVisualization/CoinVisualizationFactory.h>
39 #include <VirtualRobot/Visualization/VisualizationFactory.h>
40 #include <VirtualRobot/CollisionDetection/CollisionModel.h>
41 #include <VirtualRobot/CollisionDetection/CollisionChecker.h>
56 static std::size_t nextMultipleOf(std::size_t
value, std::size_t multiple)
63 std::size_t remainder =
value % multiple;
69 return value + multiple - remainder;
72 static std::vector<std::string> splitProperty(Property<std::string> prop)
74 std::string propString(prop.getValue());
75 std::vector<std::string> result =
Split(propString,
",");
81 GridDimension LaserScannerSimulation::calculateGridDimension(
82 const std::vector<VirtualRobot::BoundingBox>& boundingBoxes)
const
85 for (
const auto& boundingBox : boundingBoxes)
87 box.expand_to(boundingBox.getMin());
88 box.expand_to(boundingBox.getMax());
91 const auto extents = box.extents();
93 std::size_t gridSizeX =
static_cast<std::size_t
>(std::ceil(extents.x() / gridCellSize));
94 gridSizeX = nextMultipleOf(gridSizeX,
sizeof(std::size_t) * CHAR_BIT);
96 std::size_t gridSizeY =
static_cast<std::size_t
>(std::ceil(extents.y() / gridCellSize));
97 gridSizeY = nextMultipleOf(gridSizeY,
sizeof(std::size_t) * CHAR_BIT);
99 return {.originX = box.min_x(),
100 .originY = box.min_y(),
101 .sizeX = std::max<std::size_t>(gridSizeX, 1),
102 .sizeY = std::max<std::size_t>(gridSizeY, 1)};
105 VirtualRobot::SceneObjectSetPtr LaserScannerSimulation::getCollisionObjects(
106 const std::vector<VirtualRobot::SceneObjectPtr>& sceneObjects)
const
108 VirtualRobot::SceneObjectSetPtr objects(
new VirtualRobot::SceneObjectSet());
110 for (
auto&
object : sceneObjects)
113 if (robot ==
nullptr)
115 objects->addSceneObject(
object);
119 for (
const auto& rns : robot->getRobotNodeSets())
121 objects->addSceneObjects(rns);
129 GridDimension LaserScannerSimulation::calculateGridDimension(
130 const std::vector<VirtualRobot::SceneObjectPtr>& sceneObjects)
const
132 VirtualRobot::SceneObjectSetPtr objectSet(
new VirtualRobot::SceneObjectSet());
134 for (
auto&
object : sceneObjects)
137 if (robot ==
nullptr)
139 objectSet->addSceneObject(
object);
147 objectSet->addSceneObject(
object);
152 return calculateGridDimension(objectSet);
155 std::vector<VirtualRobot::BoundingBox>
156 LaserScannerSimulation::boundingBoxes(VirtualRobot::SceneObjectSetPtr
const& objects)
const
158 std::vector<VirtualRobot::BoundingBox> objectBoudingBoxes;
160 for (
unsigned int objIndex = 0; objIndex < objects->getSize(); ++objIndex)
165 if (robot ==
nullptr)
167 objectBoudingBoxes.push_back(object->getCollisionModel()->getBoundingBox());
171 for (
const auto& collisionModel : robot->getCollisionModels())
173 if (collisionModel->getCollisionModelImplementation()->getPQPModel())
175 objectBoudingBoxes.push_back(collisionModel->getBoundingBox());
181 return objectBoudingBoxes;
185 LaserScannerSimulation::calculateGridDimension(VirtualRobot::SceneObjectSetPtr
const& objects)
const
187 if (objects->getSize() == 0)
189 return {.originX = 0.0F, .originY = 0.0F, .sizeX = 1, .sizeY = 1};
192 const auto objectBoundingBoxes = boundingBoxes(objects);
193 ARMARX_INFO << objectBoundingBoxes.size() <<
" bounding boxes";
197 return calculateGridDimension(objectBoundingBoxes);
200 void LaserScannerSimulation::fillOccupancyGrid(std::vector<VirtualRobot::SceneObjectPtr>
const& sceneObjects)
203 const auto gridDim = calculateGridDimension(sceneObjects);
204 grid.init(gridDim.sizeX, gridDim.sizeY, gridDim.originX, gridDim.originY, gridCellSize);
206 ARMARX_INFO_S <<
"Creating grid with size (" << gridDim.sizeX <<
", " << gridDim.sizeY
209 VirtualRobot::CollisionCheckerPtr collisionChecker =
210 VirtualRobot::CollisionChecker::getGlobalCollisionChecker();
211 VirtualRobot::VisualizationFactoryPtr factory(
new VirtualRobot::CoinVisualizationFactory());
214 float boxSize = 1.1f * gridCellSize;
215 VirtualRobot::VisualizationNodePtr boxVisu =
216 factory->createBox(boxSize, boxSize, boxSize, VirtualRobot::VisualizationFactory::Color::Green());
217 VirtualRobot::CollisionModelPtr boxCollisionModel(
218 new VirtualRobot::CollisionModel(boxVisu,
"", collisionChecker));
220 new VirtualRobot::SceneObject(
"my_box", boxVisu, boxCollisionModel));
222 if (not collisionChecker)
229 const auto collisionObjects = getCollisionObjects(sceneObjects);
231 for (std::size_t indexY = 0; indexY < grid.sizeY; ++indexY)
233 for (std::size_t indexX = 0; indexX < grid.sizeX; ++indexX)
235 const Eigen::Vector2f pos = grid.getCentralPosition(indexX, indexY);
236 const Eigen::Vector3f boxPos(pos.x(), pos.y(), boxPosZ);
238 Eigen::Affine3f(Eigen::Translation3f(boxPos)).matrix();
239 box->setGlobalPose(boxPose);
241 const bool collision = collisionChecker->checkCollision(box, collisionObjects);
243 grid.setOccupied(indexX, indexY, collision);
253 VirtualRobot::init(
"SimulatorViewerApp");
254 worldVisu = Component::create<ArmarXPhysicsWorldVisualization>(
258 enableVisualization = getProperty<bool>(
"visualization.enable").getValue();
259 ARMARX_INFO <<
"Visualization will be " << (enableVisualization ?
"enabled" :
"disabled");
261 topicName = getProperty<std::string>(
"LaserScannerTopicName").getValue();
262 ARMARX_INFO <<
"Reporting on topic \"" << topicName <<
"\"";
265 robotStateName = getProperty<std::string>(
"RobotStateComponentName").getValue();
266 ARMARX_INFO <<
"Using RobotStateComponent \"" << robotStateName <<
"\"";
269 debugDrawerName = getProperty<std::string>(
"DebugDrawerTopicName").getValue();
270 ARMARX_INFO <<
"Using DebugDrawerTopic \"" << debugDrawerName <<
"\"";
273 topicReplayerDummy = getProperty<bool>(
"TopicReplayerDummy").getValue();
274 updatePeriod = getProperty<int>(
"UpdatePeriod").getValue();
275 visuUpdateFrequency = getProperty<int>(
"VisuUpdateFrequency").getValue();
276 gridCellSize = getProperty<float>(
"GridCellSize").getValue();
278 auto framesStrings = splitProperty(getProperty<std::string>(
"Frames"));
279 auto deviceStrings = splitProperty(getProperty<std::string>(
"Devices"));
280 auto minAnglesStrings = splitProperty(getProperty<std::string>(
"MinAngles"));
281 auto maxAnglesStrings = splitProperty(getProperty<std::string>(
"MaxAngles"));
282 auto stepsStrings = splitProperty(getProperty<std::string>(
"Steps"));
283 auto noiseStrings = splitProperty(getProperty<std::string>(
"NoiseStdDev"));
284 std::size_t scannerSize = framesStrings.size();
285 if (deviceStrings.size() == 1)
287 deviceStrings.resize(scannerSize, deviceStrings.front());
289 else if (deviceStrings.size() != scannerSize)
291 ARMARX_WARNING <<
"Unexpected size of property Devices (expected " << scannerSize
292 <<
" but got " << deviceStrings.size() <<
")";
295 if (minAnglesStrings.size() == 1)
297 minAnglesStrings.resize(scannerSize, minAnglesStrings.front());
299 else if (minAnglesStrings.size() != scannerSize)
301 ARMARX_WARNING <<
"Unexpected size of property MinAngles (expected " << scannerSize
302 <<
" but got " << minAnglesStrings.size() <<
")";
305 if (maxAnglesStrings.size() == 1)
307 maxAnglesStrings.resize(scannerSize, maxAnglesStrings.front());
309 else if (maxAnglesStrings.size() != scannerSize)
311 ARMARX_WARNING <<
"Unexpected size of property MaxAngles (expected " << scannerSize
312 <<
" but got " << maxAnglesStrings.size() <<
")";
315 if (stepsStrings.size() == 1)
317 stepsStrings.resize(scannerSize, stepsStrings.front());
319 else if (stepsStrings.size() != scannerSize)
321 ARMARX_WARNING <<
"Unexpected size of property Steps (expected " << scannerSize
322 <<
" but got " << stepsStrings.size() <<
")";
325 if (noiseStrings.size() == 1)
327 noiseStrings.resize(scannerSize, noiseStrings.front());
329 else if (noiseStrings.size() != scannerSize)
331 ARMARX_WARNING <<
"Unexpected size of property NoiseStdDev (expected " << scannerSize
332 <<
" but got " << noiseStrings.size() <<
")";
336 scanners.reserve(scannerSize);
337 connectedDevices.clear();
338 for (std::size_t i = 0; i < scannerSize; ++i)
341 scanner.
frame = framesStrings[i];
344 scanner.
minAngle = std::stof(minAnglesStrings[i]);
345 scanner.
maxAngle = std::stof(maxAnglesStrings[i]);
347 scanner.
steps = std::stoi(stepsStrings[i]);
349 catch (std::exception
const& ex)
351 ARMARX_INFO <<
"Scanner[" << i <<
"] Config error: " << ex.what();
355 scanners.push_back(scanner);
357 LaserScannerInfo info;
358 info.device = topicReplayerDummy ? deviceStrings[i] : scanner.
frame;
359 info.frame = scanner.
frame;
362 info.stepSize = (info.maxAngle - info.minAngle) / scanner.
steps;
363 connectedDevices.push_back(info);
372 if (topicReplayerDummy)
374 ARMARX_INFO <<
"Fake connect (component is used for topic replay)";
377 topic = getTopic<LaserScannerUnitListenerPrx>(topicName);
378 robotState = getProxy<RobotStateComponentInterfacePrx>(robotStateName);
379 sharedRobot = robotState->getSynchronizedRobot();
380 debugDrawer = getTopic<DebugDrawerInterfacePrx>(debugDrawerName);
386 scanner.frameNode = sharedRobot->getRobotNode(scanner.frame);
388 catch (std::exception
const& ex)
390 ARMARX_WARNING <<
"Error while querying robot frame: " << scanner.frame <<
" "
392 scanner.frameNode =
nullptr;
395 if (!scanner.frameNode)
397 ARMARX_WARNING <<
"Tried to use a non-existing robot node as frame for the "
398 "laser scanner simulation: "
408 if (enableVisualization)
414 std::vector<VirtualRobot::SceneObjectPtr> objects;
415 while (objects.empty())
417 worldVisu->synchronizeVisualizationData();
418 objects = worldVisu->getObjects();
427 std::vector<VirtualRobot::RobotPtr>
robots;
430 worldVisu->synchronizeVisualizationData();
431 robots = worldVisu->getRobots();
436 <<
"Could not get any robots from the simulator after syncing";
446 [](
const auto & r) ->
bool { return not r->isPassive(); }),
449 ARMARX_INFO <<
"Got " << objects.size() <<
" scene objects from the simulator";
451 std::vector<VirtualRobot::SceneObjectPtr> validObjects;
454 VirtualRobot::CollisionModelPtr cm = o->getCollisionModel();
457 ARMARX_WARNING <<
"Scene object with no collision model: " << o->getName();
461 const auto pqpModel = cm->getCollisionModelImplementation()->getPQPModel();
464 validObjects.push_back(o);
473 validObjects.insert(validObjects.end(),
robots.begin(),
robots.end());
475 fillOccupancyGrid(validObjects);
479 arvizDrawer->drawOccupancyGrid(grid, boxPosZ);
482 visuThrottler = std::make_unique<Throttler>(visuUpdateFrequency);
485 &LaserScannerSimulation::updateScanData,
488 "LaserScannerSimUpdate");
523 return connectedDevices;
526 void LaserScannerSimulation::updateScanData()
531 const bool updateVisu = enableVisualization and visuThrottler->check(
TimeUtil::GetTime().toMicroSeconds());
535 if (!scanner.frameNode)
539 PosePtr scannerPoseP = PosePtr::dynamicCast(scanner.frameNode->getGlobalPose());
541 Eigen::Vector2f position = scannerPose.col(3).head<2>();
544 Eigen::Vector2f yWorld(0.0f, 1.0f);
545 Eigen::Vector2f yScanner = scannerRot.col(1).head<2>();
546 float theta = acos(yWorld.dot(yScanner));
547 if (yScanner.x() >= 0.0f)
552 float minAngle = scanner.minAngle;
553 float maxAngle = scanner.maxAngle;
554 int scanSteps = scanner.steps;
557 scan.reserve(scanSteps);
561 const Interval skipInterval(0.0, M_PI_2);
563 std::normal_distribution<float> dist(0.0f, scanner.noiseStdDev);
564 for (
int i = 0; i < scanSteps; ++i)
567 step.angle = minAngle + i * (maxAngle - minAngle) / (scanSteps - 1);
569 if (skipInterval.contains(step.angle))
574 const Eigen::Vector2f scanDirGlobal =
575 Eigen::Rotation2Df(step.angle + theta) * Eigen::Vector2f(0.0f, 1.0f);
576 const float distance = grid.computeDistance(position, scanDirGlobal);
579 step.distance =
distance + dist(engine);
580 scan.push_back(step);
584 topic->reportSensorValues(scanner.frame, scanner.frame, scan, now);
588 arvizDrawer->prepareScan(scan, scanner.frame, Eigen::Affine3f(scannerPose));
594 arvizDrawer->drawScans();
598 auto timeElapsed = endTime - startTime;
601 <<
"Time to simulate laser scanners: " << timeElapsed.toMilliSecondsDouble()