30#include <Eigen/Geometry>
32#include <SimoxUtility/shapes/AxisAlignedBoundingBox.h>
33#include <SimoxUtility/shapes/OrientedBox.h>
34#include <VirtualRobot/BoundingBox.h>
35#include <VirtualRobot/CollisionDetection/CollisionChecker.h>
36#include <VirtualRobot/CollisionDetection/CollisionModel.h>
37#include <VirtualRobot/Robot.h>
38#include <VirtualRobot/SceneObjectSet.h>
39#include <VirtualRobot/VirtualRobot.h>
40#include <VirtualRobot/Visualization/CoinVisualization/CoinVisualizationFactory.h>
41#include <VirtualRobot/Visualization/VisualizationFactory.h>
57 nextMultipleOf(std::size_t value, std::size_t multiple)
64 std::size_t remainder =
value % multiple;
70 return value + multiple - remainder;
73 static std::vector<std::string>
74 splitProperty(Property<std::string> prop)
76 std::string propString(prop.getValue());
77 std::vector<std::string> result =
Split(propString,
",");
84 LaserScannerSimulation::calculateGridDimension(
85 const std::vector<VirtualRobot::BoundingBox>& boundingBoxes)
const
87 simox::AxisAlignedBoundingBox box;
88 for (
const auto& boundingBox : boundingBoxes)
90 box.expand_to(boundingBox.getMin());
91 box.expand_to(boundingBox.getMax());
94 const auto extents = box.extents();
96 std::size_t gridSizeX =
static_cast<std::size_t
>(std::ceil(extents.x() / gridCellSize));
97 gridSizeX = nextMultipleOf(gridSizeX,
sizeof(std::size_t) * CHAR_BIT);
99 std::size_t gridSizeY =
static_cast<std::size_t
>(std::ceil(extents.y() / gridCellSize));
100 gridSizeY = nextMultipleOf(gridSizeY,
sizeof(std::size_t) * CHAR_BIT);
102 return {.originX = box.min_x(),
103 .originY = box.min_y(),
104 .sizeX = std::max<std::size_t>(gridSizeX, 1),
105 .sizeY = std::max<std::size_t>(gridSizeY, 1)};
108 VirtualRobot::SceneObjectSetPtr
109 LaserScannerSimulation::getCollisionObjects(
110 const std::vector<VirtualRobot::SceneObjectPtr>& sceneObjects)
const
112 VirtualRobot::SceneObjectSetPtr objects(
new VirtualRobot::SceneObjectSet());
114 for (
auto&
object : sceneObjects)
116 VirtualRobot::Robot* robot =
dynamic_cast<VirtualRobot::Robot*
>(
object.get());
117 if (robot ==
nullptr)
119 objects->addSceneObject(
object);
123 for (
const auto& rns : robot->getRobotNodeSets())
125 objects->addSceneObjects(rns);
134 LaserScannerSimulation::calculateGridDimension(
135 const std::vector<VirtualRobot::SceneObjectPtr>& sceneObjects)
const
137 VirtualRobot::SceneObjectSetPtr objectSet(
new VirtualRobot::SceneObjectSet());
139 for (
auto&
object : sceneObjects)
141 VirtualRobot::Robot* robot =
dynamic_cast<VirtualRobot::Robot*
>(
object.get());
142 if (robot ==
nullptr)
144 objectSet->addSceneObject(
object);
152 objectSet->addSceneObject(
object);
157 return calculateGridDimension(objectSet);
160 std::vector<VirtualRobot::BoundingBox>
161 LaserScannerSimulation::boundingBoxes(VirtualRobot::SceneObjectSetPtr
const& objects)
const
163 std::vector<VirtualRobot::BoundingBox> objectBoudingBoxes;
165 for (
unsigned int objIndex = 0; objIndex < objects->getSize(); ++objIndex)
167 VirtualRobot::SceneObjectPtr
object = objects->getSceneObject(objIndex);
169 VirtualRobot::Robot* robot =
dynamic_cast<VirtualRobot::Robot*
>(
object.get());
170 if (robot ==
nullptr)
172 objectBoudingBoxes.push_back(object->getCollisionModel()->getBoundingBox());
176 for (
const auto& collisionModel : robot->getCollisionModels())
178 if (collisionModel->getCollisionModelImplementation()->getPQPModel())
180 objectBoudingBoxes.push_back(collisionModel->getBoundingBox());
186 return objectBoudingBoxes;
190 LaserScannerSimulation::calculateGridDimension(
191 VirtualRobot::SceneObjectSetPtr
const& objects)
const
193 if (objects->getSize() == 0)
195 return {.originX = 0.0F, .originY = 0.0F, .sizeX = 1, .sizeY = 1};
198 const auto objectBoundingBoxes = boundingBoxes(objects);
199 ARMARX_INFO << objectBoundingBoxes.size() <<
" bounding boxes";
203 return calculateGridDimension(objectBoundingBoxes);
207 LaserScannerSimulation::fillOccupancyGrid(
208 std::vector<VirtualRobot::SceneObjectPtr>
const& sceneObjects)
211 const auto gridDim = calculateGridDimension(sceneObjects);
212 grid.init(gridDim.sizeX, gridDim.sizeY, gridDim.originX, gridDim.originY, gridCellSize);
214 ARMARX_INFO_S <<
"Creating grid with size (" << gridDim.sizeX <<
", " << gridDim.sizeY
217 VirtualRobot::CollisionCheckerPtr collisionChecker =
218 VirtualRobot::CollisionChecker::getGlobalCollisionChecker();
219 VirtualRobot::VisualizationFactoryPtr factory(
new VirtualRobot::CoinVisualizationFactory());
222 float boxSize = 1.1f * gridCellSize;
223 VirtualRobot::VisualizationNodePtr boxVisu = factory->createBox(
224 boxSize, boxSize, boxSize, VirtualRobot::VisualizationFactory::Color::Green());
225 VirtualRobot::CollisionModelPtr boxCollisionModel(
226 new VirtualRobot::CollisionModel(boxVisu,
"", collisionChecker));
227 VirtualRobot::SceneObjectPtr box(
228 new VirtualRobot::SceneObject(
"my_box", boxVisu, boxCollisionModel));
230 if (not collisionChecker)
237 const auto collisionObjects = getCollisionObjects(sceneObjects);
239 for (std::size_t indexY = 0; indexY < grid.sizeY; ++indexY)
241 for (std::size_t indexX = 0; indexX < grid.sizeX; ++indexX)
243 const Eigen::Vector2f pos = grid.getCentralPosition(indexX, indexY);
244 const Eigen::Vector3f boxPos(pos.x(), pos.y(), boxPosZ);
245 const Eigen::Matrix4f boxPose =
246 Eigen::Affine3f(Eigen::Translation3f(boxPos)).matrix();
247 box->setGlobalPose(boxPose);
249 const bool collision = collisionChecker->checkCollision(box, collisionObjects);
251 grid.setOccupied(indexX, indexY, collision);
262 VirtualRobot::init(
"SimulatorViewerApp");
268 ARMARX_INFO <<
"Visualization will be " << (enableVisualization ?
"enabled" :
"disabled");
271 ARMARX_INFO <<
"Reporting on topic \"" << topicName <<
"\"";
275 ARMARX_INFO <<
"Using RobotStateComponent \"" << robotStateName <<
"\"";
279 ARMARX_INFO <<
"Using DebugDrawerTopic \"" << debugDrawerName <<
"\"";
293 std::size_t scannerSize = framesStrings.size();
294 if (deviceStrings.size() == 1)
296 deviceStrings.resize(scannerSize, deviceStrings.front());
298 else if (deviceStrings.size() != scannerSize)
300 ARMARX_WARNING <<
"Unexpected size of property Devices (expected " << scannerSize
301 <<
" but got " << deviceStrings.size() <<
")";
304 if (minAnglesStrings.size() == 1)
306 minAnglesStrings.resize(scannerSize, minAnglesStrings.front());
308 else if (minAnglesStrings.size() != scannerSize)
310 ARMARX_WARNING <<
"Unexpected size of property MinAngles (expected " << scannerSize
311 <<
" but got " << minAnglesStrings.size() <<
")";
314 if (maxAnglesStrings.size() == 1)
316 maxAnglesStrings.resize(scannerSize, maxAnglesStrings.front());
318 else if (maxAnglesStrings.size() != scannerSize)
320 ARMARX_WARNING <<
"Unexpected size of property MaxAngles (expected " << scannerSize
321 <<
" but got " << maxAnglesStrings.size() <<
")";
324 if (stepsStrings.size() == 1)
326 stepsStrings.resize(scannerSize, stepsStrings.front());
328 else if (stepsStrings.size() != scannerSize)
330 ARMARX_WARNING <<
"Unexpected size of property Steps (expected " << scannerSize
331 <<
" but got " << stepsStrings.size() <<
")";
334 if (noiseStrings.size() == 1)
336 noiseStrings.resize(scannerSize, noiseStrings.front());
338 else if (noiseStrings.size() != scannerSize)
340 ARMARX_WARNING <<
"Unexpected size of property NoiseStdDev (expected " << scannerSize
341 <<
" but got " << noiseStrings.size() <<
")";
345 scanners.reserve(scannerSize);
346 connectedDevices.clear();
347 for (std::size_t i = 0; i < scannerSize; ++i)
350 scanner.
frame = framesStrings[i];
353 scanner.
minAngle = std::stof(minAnglesStrings[i]);
354 scanner.
maxAngle = std::stof(maxAnglesStrings[i]);
356 scanner.
steps = std::stoi(stepsStrings[i]);
358 catch (std::exception
const& ex)
360 ARMARX_INFO <<
"Scanner[" << i <<
"] Config error: " << ex.what();
364 scanners.push_back(scanner);
366 LaserScannerInfo info;
367 info.device = topicReplayerDummy ? deviceStrings[i] : scanner.
frame;
368 info.frame = scanner.
frame;
371 info.stepSize = (info.maxAngle - info.minAngle) / scanner.
steps;
372 connectedDevices.push_back(info);
382 if (topicReplayerDummy)
384 ARMARX_INFO <<
"Fake connect (component is used for topic replay)";
389 sharedRobot = robotState->getSynchronizedRobot();
396 scanner.frameNode = sharedRobot->getRobotNode(scanner.frame);
398 catch (std::exception
const& ex)
400 ARMARX_WARNING <<
"Error while querying robot frame: " << scanner.frame <<
" "
402 scanner.frameNode =
nullptr;
405 if (!scanner.frameNode)
407 ARMARX_WARNING <<
"Tried to use a non-existing robot node as frame for the "
408 "laser scanner simulation: "
418 if (enableVisualization)
424 std::vector<VirtualRobot::SceneObjectPtr>
objects;
427 worldVisu->synchronizeVisualizationData();
428 objects = worldVisu->getObjects();
430 <<
" objects from the simulator";
434 <<
"Could not get any objects from the simulator after syncing";
439 std::vector<VirtualRobot::RobotPtr> robots;
440 while (robots.empty())
442 worldVisu->synchronizeVisualizationData();
443 robots = worldVisu->getRobots();
448 <<
"Could not get any robots from the simulator after syncing";
456 robots.erase(std::remove_if(robots.begin(),
458 [](
const auto& r) ->
bool { return not r->isPassive(); }),
463 std::vector<VirtualRobot::SceneObjectPtr> validObjects;
464 for (VirtualRobot::SceneObjectPtr
const& o :
objects)
466 VirtualRobot::CollisionModelPtr cm = o->getCollisionModel();
469 ARMARX_WARNING <<
"Scene object with no collision model: " << o->getName();
473 const auto pqpModel = cm->getCollisionModelImplementation()->getPQPModel();
476 validObjects.push_back(o);
485 validObjects.insert(validObjects.end(), robots.begin(), robots.end());
487 fillOccupancyGrid(validObjects);
491 arvizDrawer->drawOccupancyGrid(grid, boxPosZ);
494 visuThrottler = std::make_unique<Throttler>(visuUpdateFrequency);
497 &LaserScannerSimulation::updateScanData,
500 "LaserScannerSimUpdate");
540 return connectedDevices;
544 LaserScannerSimulation::updateScanData()
549 const bool updateVisu =
550 enableVisualization and visuThrottler->check(
TimeUtil::GetTime().toMicroSeconds());
554 if (!scanner.frameNode)
558 PosePtr scannerPoseP = PosePtr::dynamicCast(scanner.frameNode->getGlobalPose());
559 Eigen::Matrix4f scannerPose = scannerPoseP->toEigen();
560 Eigen::Vector2f position = scannerPose.col(3).head<2>();
562 Eigen::Matrix3f scannerRot = scannerPose.block<3, 3>(0, 0);
563 Eigen::Vector2f yWorld(0.0f, 1.0f);
564 Eigen::Vector2f yScanner = scannerRot.col(1).head<2>();
565 float theta = acos(yWorld.dot(yScanner));
566 if (yScanner.x() >= 0.0f)
571 float minAngle = scanner.minAngle;
572 float maxAngle = scanner.maxAngle;
573 int scanSteps = scanner.steps;
576 scan.reserve(scanSteps);
580 const Interval skipInterval(0.0, M_PI_2);
582 std::normal_distribution<float> dist(0.0f, scanner.noiseStdDev);
583 for (
int i = 0; i < scanSteps; ++i)
586 step.angle = minAngle + i * (maxAngle - minAngle) / (scanSteps - 1);
588 if (skipInterval.contains(step.angle))
593 const Eigen::Vector2f scanDirGlobal =
594 Eigen::Rotation2Df(step.angle + theta) * Eigen::Vector2f(0.0f, 1.0f);
595 const float distance = grid.computeDistance(position, scanDirGlobal);
598 step.distance =
distance + dist(engine);
599 scan.push_back(step);
603 topic->reportSensorValues(scanner.frame, scanner.frame, scan, now);
607 arvizDrawer->prepareScan(scan, scanner.frame, Eigen::Affine3f(scannerPose));
613 arvizDrawer->drawScans();
617 auto timeElapsed = endTime - startTime;
620 <<
"Time to simulate laser scanners: " << timeElapsed.toMilliSecondsDouble()
627 return "LaserScannerSimulation";
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
armarx::viz::Client & getArvizClient()
static DateTime Now()
Current time on the virtual clock.
static TPtr create(Ice::PropertiesPtr properties=Ice::createProperties(), const std::string &configName="", const std::string &configDomain="ArmarX")
Factory method for a component.
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Property< PropertyType > getProperty(const std::string &name)
SpamFilterDataPtr deactivateSpam(float deactivationDurationSec=10.0f, const std::string &identifier="", bool deactivate=true) const
disables the logging for the current line for the given amount of seconds.
void offeringTopic(const std::string &name)
Registers a topic for retrival after initialization.
TopicProxyType getTopic(const std::string &name)
Returns a proxy of the specified topic.
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
std::string getName() const
Retrieve name of object.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
ArmarXManagerPtr getArmarXManager() const
Returns the ArmarX manager used to add and remove components.
The periodic task executes one thread method repeatedly using the time period specified in the constr...
Ice::PropertiesPtr getIceProperties() const
Returns the set of Ice properties.
static IceUtil::Time GetTime(TimeMode timeMode=TimeMode::VirtualTime)
Get the current time.
static void MSSleep(int durationMS)
lock the calling thread for a given duration (like usleep(...) but using Timeserver time)
Implements a Variant type for timestamps.
Brief description of class LaserScannerSimulation.
void onInitComponent() override
std::string getReportTopicName(const Ice::Current &) const override
void onDisconnectComponent() override
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void onConnectComponent() override
static std::string GetDefaultName()
LaserScannerInfoSeq getConnectedDevices(const Ice::Current &) const override
void onExitComponent() override
virtual ~LaserScannerSimulation() override
#define ARMARX_INFO
The normal logging level.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
#define ARMARX_VERBOSE
The logging level for verbose information.
#define ARMARX_WARNING_S
The logging level for unexpected behaviour, but not a serious problem.
std::vector< std::string > Split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
IceInternal::Handle< Pose > PosePtr
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
IceInternal::Handle< TimestampVariant > TimestampVariantPtr
std::shared_ptr< Value > value()
double distance(const Point &a, const Point &b)