FakeObjectDetector.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 ArmarXSimulation::components::FakeObjectDetector
17 * @author Timo Birr ( timo dot birr at kit dot edu )
18 * @date 2026
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23#include "FakeObjectDetector.h"
24
25#include <algorithm>
26#include <experimental/map>
27
28#include <SimoxUtility/algorithm/string.h>
29#include <SimoxUtility/math/convert/deg_to_rad.h>
30#include <VirtualRobot/BoundingBox.h>
31#include <VirtualRobot/CollisionDetection/CollisionModel.h>
32#include <VirtualRobot/Robot.h>
33#include <VirtualRobot/RobotNodeSet.h>
34#include <VirtualRobot/XML/RobotIO.h>
35
40
43
44namespace armarx
45{
46 // `armarx::Box` already exists, so the geometry types stay explicitly qualified.
47 namespace fod = armarx::fake_object_detector;
48
49 namespace
50 {
52
53 /// Parse an axis specification such as "+Z" or "-y".
54 std::optional<Eigen::Vector3f>
55 parseAxis(const std::string& text)
56 {
57 const std::string trimmed = simox::alg::trim_copy(text);
58 if (trimmed.size() != 2)
59 {
60 return std::nullopt;
61 }
62
63 float sign = 0;
64 switch (trimmed.front())
65 {
66 case '+':
67 sign = 1.0F;
68 break;
69 case '-':
70 sign = -1.0F;
71 break;
72 default:
73 return std::nullopt;
74 }
75
76 switch (std::toupper(static_cast<unsigned char>(trimmed.back())))
77 {
78 case 'X':
79 return sign * Eigen::Vector3f::UnitX();
80 case 'Y':
81 return sign * Eigen::Vector3f::UnitY();
82 case 'Z':
83 return sign * Eigen::Vector3f::UnitZ();
84 default:
85 return std::nullopt;
86 }
87 }
88
89 std::optional<DetectionMode>
90 parseDetectionMode(const std::string& text)
91 {
92 const std::string lower = simox::alg::to_lower(simox::alg::trim_copy(text));
93 if (lower == "always")
94 {
95 return DetectionMode::Always;
96 }
97 if (lower == "fieldofview")
98 {
99 return DetectionMode::FieldOfView;
100 }
101 if (lower == "lineofsight")
102 {
103 return DetectionMode::LineOfSight;
104 }
105 return std::nullopt;
106 }
107
108 /// Turn an object-local OOBB plus the object's global pose into a global box.
110 toGlobalBox(const simox::OrientedBoxf& localOOBB, const Eigen::Matrix4f& globalPose)
111 {
112 fod::Box box;
113 box.centerPose = globalPose * localOOBB.transformation_centered();
114 box.halfExtents = localOOBB.dimensions() / 2;
115 return box;
116 }
117 } // namespace
118
119 const std::string FakeObjectDetector::defaultName = "FakeObjectDetector";
120
123 {
126
127 def->component(simulatorPrx, "Simulator");
128
129 def->optional(properties.updateFrequency,
130 "UpdateFrequency",
131 "Frequency at which objects are detected and reported [Hz].");
132
133 def->optional(properties.detectionMode,
134 "p.detectionMode",
135 "How strictly an object must be perceivable before it is reported: "
136 "'Always' (report everything), "
137 "'FieldOfView' (report once the bounding box enters the camera frustum), "
138 "'LineOfSight' (additionally require a clear line of sight to the front "
139 "facing side of the bounding box).");
140
141 def->optional(properties.robotName,
142 "p.robotName",
143 "Name of the robot to take the camera pose from. "
144 "If empty, the first robot in the simulated scene is used.");
145 def->optional(properties.cameraFrame,
146 "p.cameraFrame",
147 "Robot node used as the camera. Use a rendering ('...Sim') node so that "
148 "the viewing axis matches the simulated cameras.");
149
150 def->optional(properties.horizontalFovDeg,
151 "p.horizontalFovDeg",
152 "Horizontal opening angle of the camera frustum [deg].");
153 def->optional(properties.verticalFovDeg,
154 "p.verticalFovDeg",
155 "Vertical opening angle of the camera frustum [deg].");
156 def->optional(properties.minDistance,
157 "p.minDistance",
158 "Minimal distance along the viewing axis at which objects are detected "
159 "[mm].");
160 def->optional(properties.maxDistance,
161 "p.maxDistance",
162 "Maximal distance along the viewing axis at which objects are detected "
163 "[mm].");
164 def->optional(properties.cameraForwardAxis,
165 "p.cameraForwardAxis",
166 "Viewing axis of the camera node, e.g. '+Z' or '-X'.");
167 def->optional(
168 properties.cameraUpAxis, "p.cameraUpAxis", "Up axis of the camera node, e.g. '-Y'.");
169 def->optional(properties.requireFullyInFov,
170 "p.requireFullyInFov",
171 "If true, the whole bounding box must be inside the frustum. "
172 "Otherwise it is enough that a part of it is.");
173
174 def->optional(properties.frontFaceSampleGrid,
175 "p.frontFaceSampleGrid",
176 "Line of sight is tested against an N x N grid of points on the front "
177 "facing side of the bounding box. This is N. 1 tests the face centre "
178 "only.");
179 def->optional(properties.minVisibleFraction,
180 "p.minVisibleFraction",
181 "Fraction of the front face sample points that must have a clear line of "
182 "sight for the object to be detected.");
183 def->optional(properties.occlusionByObjects,
184 "p.occlusionByObjects",
185 "If true, other objects in the scene can occlude an object.");
186 def->optional(properties.occlusionByRobot,
187 "p.occlusionByRobot",
188 "If true, the robot's own body can occlude an object.");
189 def->optional(properties.rayEpsilon,
190 "p.rayEpsilon",
191 "Numerical tolerance for the line of sight rays [mm].");
192
193 def->optional(properties.fallbackOobbSize,
194 "p.fallbackOobbSize",
195 "Edge length of the cube used as bounding box for objects that have no "
196 "bounding box on disk [mm].");
197 def->optional(
198 properties.confidence, "p.confidence", "Confidence reported for a detected object.");
199 def->optional(properties.ignoredObjects,
200 "p.ignoredObjects",
201 "Comma separated object IDs (e.g. 'Kitchen/green-cup/0') that are never "
202 "reported.");
203 def->optional(properties.alwaysVisibleDatasets,
204 "p.alwaysVisibleDatasets",
205 "Comma separated object datasets (e.g. 'Interior') that make up the static "
206 "scene. Their objects are reported without any visibility check and marked "
207 "as static, so they never decay out of the object memory. All other "
208 "objects are gated by 'p.detectionMode'.");
209 def->optional(properties.requestedObjectsOnly,
210 "p.requestedObjectsOnly",
211 "If true, only report visible objects that were requested via "
212 "`requestObjects()`. If false, all visible objects are reported.");
213
214 return def;
215 }
216
217 void
219 {
220 if (const std::optional<DetectionMode> mode = parseDetectionMode(properties.detectionMode))
221 {
222 properties.mode = *mode;
223 }
224 else
225 {
226 ARMARX_WARNING << "Unknown detection mode '" << properties.detectionMode
227 << "'. Expected one of 'Always', 'FieldOfView', 'LineOfSight'. "
228 << "Falling back to 'FieldOfView'.";
229 properties.mode = DetectionMode::FieldOfView;
230 }
231
232 if (const std::optional<Eigen::Vector3f> axis = parseAxis(properties.cameraForwardAxis))
233 {
234 cameraForwardLocal = *axis;
235 }
236 else
237 {
238 ARMARX_WARNING << "Could not parse camera forward axis '"
239 << properties.cameraForwardAxis << "'. Falling back to '+Z'.";
240 cameraForwardLocal = Eigen::Vector3f::UnitZ();
241 }
242
243 if (const std::optional<Eigen::Vector3f> axis = parseAxis(properties.cameraUpAxis))
244 {
245 cameraUpLocal = *axis;
246 }
247 else
248 {
249 ARMARX_WARNING << "Could not parse camera up axis '" << properties.cameraUpAxis
250 << "'. Falling back to '-Y'.";
251 cameraUpLocal = -Eigen::Vector3f::UnitY();
252 }
253
254 if (std::abs(cameraForwardLocal.dot(cameraUpLocal)) > 1e-3F)
255 {
256 ARMARX_WARNING << "Camera forward axis '" << properties.cameraForwardAxis
257 << "' and up axis '" << properties.cameraUpAxis
258 << "' are not perpendicular. The frustum will be skewed.";
259 }
260
261 if (properties.frontFaceSampleGrid < 1)
262 {
263 ARMARX_WARNING << "p.frontFaceSampleGrid must be at least 1, but is "
264 << properties.frontFaceSampleGrid << ". Using 1.";
265 properties.frontFaceSampleGrid = 1;
266 }
267
268 for (const std::string& id : simox::alg::split(properties.ignoredObjects, ","))
269 {
270 const std::string trimmed = simox::alg::trim_copy(id);
271 if (not trimmed.empty())
272 {
273 ignoredObjects.insert(trimmed);
274 }
275 }
276 if (not ignoredObjects.empty())
277 {
278 ARMARX_INFO << "Ignoring objects: "
279 << simox::alg::join(std::vector<std::string>(ignoredObjects.begin(),
280 ignoredObjects.end()),
281 ", ");
282 }
283
284 for (const std::string& dataset : simox::alg::split(properties.alwaysVisibleDatasets, ","))
285 {
286 const std::string trimmed = simox::alg::trim_copy(dataset);
287 if (not trimmed.empty())
288 {
289 alwaysVisibleDatasets.insert(trimmed);
290 }
291 }
292 if (not alwaysVisibleDatasets.empty())
293 {
294 ARMARX_INFO << "Reporting these datasets as the always visible static scene: "
295 << simox::alg::join(std::vector<std::string>(alwaysVisibleDatasets.begin(),
296 alwaysVisibleDatasets.end()),
297 ", ");
298 }
299 }
300
301 void
303 {
304 detectionTask = new SimpleRunningTask<>([this]() { this->detectionTaskRun(); });
305 detectionTask->start();
306 }
307
308 void
310 {
311 if (detectionTask)
312 {
313 detectionTask->stop();
314 detectionTask = nullptr;
315 }
316 }
317
318 void
322
323 std::string
325 {
326 return FakeObjectDetector::defaultName;
327 }
328
329 std::string
331 {
332 return FakeObjectDetector::defaultName;
333 }
334
335 objpose::provider::RequestObjectsOutput
336 FakeObjectDetector::requestObjects(const objpose::provider::RequestObjectsInput& input,
337 const Ice::Current& /*unused*/)
338 {
339 objpose::provider::RequestObjectsOutput output;
340
341 const DateTime now = DateTime::Now();
342 const std::scoped_lock lock(activeRequestsMutex);
343
344 for (const auto& id : input.objectIDs)
345 {
346 const std::string entityID = id.dataset + "/" + id.className + "/" + id.instanceName;
347
348 activeRequests[entityID] =
349 now + armarx::core::time::Duration::MilliSeconds(input.relativeTimeoutMS);
350
351 // Whether the object is actually reported depends on visibility, which is decided
352 // per cycle - so the request itself always succeeds.
353 output.results[id].success = true;
354 }
355
356 if (not properties.requestedObjectsOnly and not input.objectIDs.empty())
357 {
359 << "All visible objects are reported by default. Requesting an object "
360 "has no effect unless 'p.requestedObjectsOnly' is enabled.";
361 }
362
363 return output;
364 }
365
366 objpose::ProviderInfo
367 FakeObjectDetector::getProviderInfo(const Ice::Current& /*unused*/)
368 {
369 objpose::ProviderInfo info;
370 info.objectType = objpose::KnownObject;
372 info.supportedObjects = {};
373 return info;
374 }
375
376 void
377 FakeObjectDetector::detectionTaskRun()
378 {
379 Metronome metronome(Frequency::Hertz(properties.updateFrequency));
380
381 while (detectionTask and not detectionTask->isStopped())
382 {
383 metronome.waitForNextTick();
384 detectAndReport();
385 }
386 }
387
388 void
389 FakeObjectDetector::detectAndReport()
390 {
391 armarx::SceneVisuData sceneData;
392 try
393 {
394 sceneData = simulatorPrx->getScene();
395 }
396 catch (const Ice::LocalException& e)
397 {
399 << "Could not get the scene from the simulator: " << e.what();
400 return;
401 }
402
403 const DateTime now = DateTime::Now();
404 removeExpiredRequests(now);
405
406 const std::vector<SceneObject> objects = collectSceneObjects(sceneData);
407 std::vector<objpose::ProvidedObjectPose> providedObjects;
408
409 const armarx::RobotVisuData* robotData = findRobot(sceneData);
410
411 // The static scene is known to the robot at all times, in every mode, and even if the
412 // camera cannot be resolved. It is still used as an occluder for the gated objects.
413 for (const SceneObject& object : objects)
414 {
415 if (object.alwaysVisible and isRequested(object.objectID))
416 {
417 providedObjects.push_back(toProvidedObjectPose(object, now));
418 }
419 }
420
421 if (properties.mode == DetectionMode::Always)
422 {
423 for (const SceneObject& object : objects)
424 {
425 if (not object.alwaysVisible and isRequested(object.objectID))
426 {
427 providedObjects.push_back(toProvidedObjectPose(object, now));
428 }
429 }
430 }
431 else if (robotData != nullptr)
432 {
433 if (const std::optional<fod::Frustum> frustum = buildFrustum(*robotData))
434 {
435 std::vector<fod::Box> robotBoxes;
436 if (properties.mode == DetectionMode::LineOfSight and properties.occlusionByRobot)
437 {
438 robotBoxes = collectRobotBoxes(*robotData);
439 }
440
441 for (std::size_t i = 0; i < objects.size(); ++i)
442 {
443 // Already reported above as part of the static scene.
444 if (objects[i].alwaysVisible or not isRequested(objects[i].objectID))
445 {
446 continue;
447 }
448
449 std::vector<fod::Box> occluders;
450 if (properties.mode == DetectionMode::LineOfSight)
451 {
452 occluders.reserve(objects.size() + robotBoxes.size());
453 if (properties.occlusionByObjects)
454 {
455 for (std::size_t j = 0; j < objects.size(); ++j)
456 {
457 // The target must never occlude itself.
458 if (j != i)
459 {
460 occluders.push_back(objects[j].box);
461 }
462 }
463 }
464 occluders.insert(occluders.end(), robotBoxes.begin(), robotBoxes.end());
465 }
466
467 if (isVisible(objects[i], *frustum, occluders))
468 {
469 providedObjects.push_back(toProvidedObjectPose(objects[i], now));
470 }
471 }
472 }
473 }
474 else
475 {
476 ARMARX_WARNING << deactivateSpam(10) << "No robot '" << properties.robotName
477 << "' in the simulated scene. Reporting no objects.";
478 }
479
480 ARMARX_DEBUG << deactivateSpam(10) << "Detected " << providedObjects.size() << " of "
481 << objects.size() << " objects.";
482
483 objpose::data::ProvidedObjectPoseSeq providedObjectsIce = objpose::toIce(providedObjects);
484
485 // `robotName` only exists on the Ice struct. The object memory needs it to resolve the
486 // robot and fill in `objectPoseRobot`; without it, it logs "Failed to retrieve robot".
487 const std::string robotName = robotData != nullptr ? robotData->name : properties.robotName;
488 for (objpose::data::ProvidedObjectPose& pose : providedObjectsIce)
489 {
490 pose.robotName = robotName;
491 }
492
493 try
494 {
495 objectPoseTopic->reportObjectPoses(getName(), providedObjectsIce);
496 }
497 catch (const Ice::LocalException& e)
498 {
500 << "Could not report object poses to the object memory: " << e.what();
501 }
502 }
503
504 const armarx::RobotVisuData*
505 FakeObjectDetector::findRobot(const armarx::SceneVisuData& sceneData) const
506 {
507 if (sceneData.robots.empty())
508 {
509 return nullptr;
510 }
511 if (properties.robotName.empty())
512 {
513 return &sceneData.robots.front();
514 }
515
516 const auto it = std::find_if(sceneData.robots.begin(),
517 sceneData.robots.end(),
518 [this](const armarx::RobotVisuData& robot)
519 { return robot.name == properties.robotName; });
520
521 return it != sceneData.robots.end() ? &(*it) : nullptr;
522 }
523
524 std::vector<FakeObjectDetector::SceneObject>
525 FakeObjectDetector::collectSceneObjects(const armarx::SceneVisuData& sceneData)
526 {
527 std::vector<SceneObject> objects;
528 objects.reserve(sceneData.objects.size());
529
530 for (const armarx::ObjectVisuData& visuData : sceneData.objects)
531 {
532 if (ignoredObjects.count(visuData.name) > 0)
533 {
534 continue;
535 }
536
537 const auto poseIt = visuData.objectPoses.find(visuData.name);
538 if (poseIt == visuData.objectPoses.end())
539 {
540 ARMARX_WARNING << deactivateSpam(10) << "Simulated object '" << visuData.name
541 << "' has no pose. Skipping it.";
542 continue;
543 }
544
545 SceneObject object;
546 object.objectID = armarx::ObjectID(visuData.name);
547 object.globalPose = armarx::fromIce(poseIt->second);
548 object.localOOBB = getLocalOOBB(object.objectID);
549 object.alwaysVisible = alwaysVisibleDatasets.count(object.objectID.dataset()) > 0;
550
551 const Eigen::Matrix4f identity = Eigen::Matrix4f::Identity();
552 const Eigen::Vector3f fallbackExtents =
553 Eigen::Vector3f::Constant(properties.fallbackOobbSize);
554 const simox::OrientedBoxf oobb =
555 object.localOOBB.value_or(simox::OrientedBoxf(identity, fallbackExtents));
556 object.box = toGlobalBox(oobb, object.globalPose);
557
558 objects.push_back(std::move(object));
559 }
560
561 return objects;
562 }
563
564 std::optional<simox::OrientedBoxf>
565 FakeObjectDetector::getLocalOOBB(const armarx::ObjectID& objectID)
566 {
567 // The OOBB is a property of the object class, so cache it per class.
568 const std::string classID = objectID.getClassID().str();
569
570 if (const auto it = oobbCache.find(classID); it != oobbCache.end())
571 {
572 return it->second;
573 }
574
575 std::optional<simox::OrientedBoxf> oobb;
576 if (const std::optional<ObjectInfo> info = objectFinder.findObject(objectID))
577 {
578 oobb = info->loadOOBB();
579 }
580
581 if (not oobb and warnedMissingOOBB.insert(classID).second)
582 {
583 ARMARX_WARNING << "No bounding box found for object class '" << classID
584 << "'. Using a cube of " << properties.fallbackOobbSize
585 << " mm instead.";
586 }
587
588 oobbCache[classID] = oobb;
589 return oobb;
590 }
591
592 std::optional<fod::Frustum>
593 FakeObjectDetector::buildFrustum(const armarx::RobotVisuData& robot) const
594 {
595 const auto it = robot.robotNodePoses.find(properties.cameraFrame);
596 if (it == robot.robotNodePoses.end())
597 {
598 if (not warnedMissingCameraNode)
599 {
600 std::vector<std::string> nodeNames;
601 nodeNames.reserve(robot.robotNodePoses.size());
602 for (const auto& [name, _] : robot.robotNodePoses)
603 {
604 nodeNames.push_back(name);
605 }
606 ARMARX_WARNING << "Robot '" << robot.name << "' has no node '"
607 << properties.cameraFrame << "' to use as camera. "
608 << "Available nodes are: " << nodeNames;
609 const_cast<FakeObjectDetector*>(this)->warnedMissingCameraNode = true;
610 }
611 return std::nullopt;
612 }
613
614 fod::Frustum frustum;
615 frustum.cameraPose = armarx::fromIce(it->second);
616 frustum.horizontalFov = simox::math::deg_to_rad(properties.horizontalFovDeg);
617 frustum.verticalFov = simox::math::deg_to_rad(properties.verticalFovDeg);
618 frustum.minDistance = properties.minDistance;
619 frustum.maxDistance = properties.maxDistance;
620 frustum.forwardLocal = cameraForwardLocal;
621 frustum.upLocal = cameraUpLocal;
622 return frustum;
623 }
624
626 FakeObjectDetector::loadRobotModel(const armarx::RobotVisuData& robotData)
627 {
628 if (robotModel and robotModelName == robotData.name)
629 {
630 return robotModel;
631 }
632 if (robotModelLoadFailed)
633 {
634 return nullptr;
635 }
636
637 if (robotData.robotFile.empty())
638 {
639 ARMARX_WARNING << "Robot '" << robotData.name
640 << "' has no model file. It cannot occlude objects.";
641 robotModelLoadFailed = true;
642 return nullptr;
643 }
644
645 if (not robotData.project.empty())
646 {
647 const CMakePackageFinder finder(robotData.project);
648 if (finder.packageFound())
649 {
650 ArmarXDataPath::addDataPaths(finder.getDataDir());
651 }
652 else
653 {
654 ARMARX_WARNING << "ArmarX package '" << robotData.project << "' was not found.";
655 }
656 }
657
658 std::string filename = robotData.robotFile;
659 ArmarXDataPath::getAbsolutePath(filename, filename);
660
661 try
662 {
663 // Collision models are required - the default (eStructure) has no geometry at all.
664 robotModel =
665 VirtualRobot::RobotIO::loadRobot(filename, VirtualRobot::RobotIO::eCollisionModel);
666 }
667 catch (const std::exception& e)
668 {
669 ARMARX_WARNING << "Could not load the robot model from '" << filename
670 << "': " << e.what() << ". The robot will not occlude objects.";
671 }
672
673 if (not robotModel)
674 {
675 robotModelLoadFailed = true;
676 return nullptr;
677 }
678
679 robotModelName = robotData.name;
680 ARMARX_INFO << "Loaded robot model '" << robotData.name << "' from '" << filename
681 << "' for self-occlusion checks.";
682 return robotModel;
683 }
684
685 std::vector<fod::Box>
686 FakeObjectDetector::collectRobotBoxes(const armarx::RobotVisuData& robotData)
687 {
688 const VirtualRobot::RobotPtr robot = loadRobotModel(robotData);
689 if (not robot)
690 {
691 return {};
692 }
693
694 std::map<std::string, float> jointValues;
695 for (const auto& [name, value] : robotData.jointValues)
696 {
697 if (robot->hasRobotNode(name))
698 {
699 jointValues[name] = value;
700 }
701 }
702 robot->setJointValues(jointValues);
703 if (robotData.pose)
704 {
705 robot->setGlobalPose(armarx::fromIce(robotData.pose));
706 }
707
708 std::vector<fod::Box> boxes;
709 for (const VirtualRobot::RobotNodePtr& node : robot->getRobotNodes())
710 {
711 const VirtualRobot::CollisionModelPtr collisionModel = node->getCollisionModel();
712 if (not collisionModel)
713 {
714 continue;
715 }
716
717 // Take the bounding box in the link's own frame and orient it with the link, rather
718 // than using getBoundingBox(true). A global axis aligned box around a link lying
719 // diagonally is far larger than the link and would occlude much of the scene.
720 const VirtualRobot::BoundingBox boundingBox = collisionModel->getBoundingBox(false);
721 const Eigen::Vector3f min = boundingBox.getMin();
722 const Eigen::Vector3f max = boundingBox.getMax();
723 const Eigen::Vector3f extents = max - min;
724 if (extents.minCoeff() <= 0)
725 {
726 continue;
727 }
728
729 Eigen::Matrix4f localCenter = Eigen::Matrix4f::Identity();
730 localCenter.topRightCorner<3, 1>() = (min + max) / 2;
731
732 fod::Box box;
733 box.centerPose = collisionModel->getGlobalPose() * localCenter;
734 box.halfExtents = extents / 2;
735 boxes.push_back(box);
736 }
737
738 return boxes;
739 }
740
741 bool
742 FakeObjectDetector::isVisible(const SceneObject& object,
743 const fod::Frustum& frustum,
744 const std::vector<fod::Box>& occluders) const
745 {
746 if (not fod::isInFieldOfView(frustum, object.box, properties.requireFullyInFov))
747 {
748 return false;
749 }
750
751 if (properties.mode != DetectionMode::LineOfSight)
752 {
753 return true;
754 }
755
756 const float fraction = fod::visibleFraction(
757 frustum, object.box, occluders, properties.frontFaceSampleGrid, properties.rayEpsilon);
758
759 return fraction >= properties.minVisibleFraction;
760 }
761
763 FakeObjectDetector::toProvidedObjectPose(const SceneObject& object, const DateTime& time) const
764 {
765 objpose::ProvidedObjectPose pose;
766
767 pose.providerName = getName();
768 pose.objectType = objpose::ObjectType::KnownObject;
769 // Static poses never decay. That is what keeps the static scene permanently in memory,
770 // and exactly why a detected object must not be marked static.
771 pose.isStatic = object.alwaysVisible;
772
773 pose.objectID = object.objectID;
774 pose.objectPose = object.globalPose;
775 pose.objectPoseFrame = armarx::GlobalFrame;
776
777 pose.localOOBB = object.localOOBB;
778 pose.confidence = properties.confidence;
779 // Must advance every cycle, otherwise the memory discards the update.
780 pose.timestamp = time;
781
782 return pose;
783 }
784
785 void
786 FakeObjectDetector::removeExpiredRequests(const DateTime& time)
787 {
788 const std::scoped_lock lock(activeRequestsMutex);
789
790 std::experimental::erase_if(activeRequests,
791 [&time](const auto& request)
792 {
793 const DateTime& until = request.second;
794 return (not until.isInvalid()) and time > until;
795 });
796 }
797
798 bool
799 FakeObjectDetector::isRequested(const armarx::ObjectID& objectID) const
800 {
801 if (not properties.requestedObjectsOnly)
802 {
803 return true;
804 }
805
806 const std::scoped_lock lock(activeRequestsMutex);
807 return activeRequests.count(objectID.str()) > 0;
808 }
809
812
813} // namespace armarx
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
static void addDataPaths(const std::string &dataPathList)
Default component property definition container.
Definition Component.h:70
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:88
static DateTime Now()
Definition DateTime.cpp:51
A fake object detector for simulation.
objpose::ProviderInfo getProviderInfo(const Ice::Current &) override
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
static std::string GetDefaultName()
Get the component's default name.
objpose::provider::RequestObjectsOutput requestObjects(const objpose::provider::RequestObjectsInput &input, const Ice::Current &) override
std::string getDefaultName() const override
static Frequency Hertz(std::int64_t hertz)
Definition Frequency.cpp:20
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.
Definition Logging.cpp:99
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)
ObjectID getClassID() const
Return just the class ID without an intance name.
Definition ObjectID.cpp:71
std::string str() const
Return "dataset/className" or "dataset/className/instanceName".
Definition ObjectID.cpp:60
objpose::ObjectPoseStorageInterfacePrx objectPoseTopic
bool isStopped()
Retrieve whether stop() has been called.
Represents a point in time.
Definition DateTime.h:25
static Duration MilliSeconds(std::int64_t milliSeconds)
Constructs a duration in milliseconds.
Definition Duration.cpp:48
Simple rate limiter for use in loops to maintain a certain frequency given a clock.
Definition Metronome.h:57
An object pose provided by an ObjectPoseProvider.
std::string providerName
Name of the providing component.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:179
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:182
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:191
std::string const GlobalFrame
Variable of the global coordinate system.
Definition FramedPose.h:65
std::shared_ptr< class Robot > RobotPtr
Definition Bus.h:19
Geometric primitives for the fake object detector.
DetectionMode
How strictly an object must be perceivable before it is reported.
objpose::AABB toIce(const simox::AxisAlignedBoundingBox &aabb)
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
std::vector< T > max(const std::vector< T > &v1, const std::vector< T > &v2)
T sign(T t)
Definition algorithm.h:214
void fromIce(const std::map< IceKeyT, IceValueT > &iceMap, boost::container::flat_map< CppKeyT, CppValueT > &cppMap)
std::vector< T > min(const std::vector< T > &v1, const std::vector< T > &v2)
SimpleRunningTask(Ts...) -> SimpleRunningTask< std::function< void(void)> >
std::shared_ptr< Value > value()
Definition cxxopts.hpp:855
An oriented bounding box in the global frame.
Eigen::Matrix4f centerPose
Rotation = box axes, translation = box centre.