VisibilityCheck.h
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#pragma once
24
25#include <array>
26#include <optional>
27#include <vector>
28
29#include <Eigen/Core>
30#include <Eigen/Geometry>
31
32/**
33 * Geometric primitives for the fake object detector.
34 *
35 * This header is deliberately free of Ice, ArmarX component and Simox types so
36 * that the visibility logic can be unit tested in isolation.
37 *
38 * All lengths are in millimetres and all poses are in the global frame, matching
39 * the ArmarX/Simox convention.
40 */
42{
43
44 /// How strictly an object must be perceivable before it is reported.
45 enum class DetectionMode
46 {
47 /// Report every object, regardless of where the camera looks.
49 /// Report an object once its bounding box enters the camera frustum.
51 /// Additionally require a clear line of sight to the front face of the bounding box.
53 };
54
55 /// An oriented bounding box in the global frame.
56 struct Box
57 {
58 /// Rotation = box axes, translation = box centre.
59 Eigen::Matrix4f centerPose = Eigen::Matrix4f::Identity();
60 /// Half the extent along each local box axis.
61 Eigen::Vector3f halfExtents = Eigen::Vector3f::Zero();
62
63 Eigen::Vector3f center() const;
64 Eigen::Matrix3f axes() const;
65
66 /// The eight corners in global coordinates.
67 std::array<Eigen::Vector3f, 8> corners() const;
68 };
69
70 /**
71 * A symmetric view frustum.
72 *
73 * The camera looks along `forwardLocal` in its own frame. ArmarX/Simox render
74 * cameras (the `*Sim` robot nodes) look along +Z with +X pointing up, see
75 * `VirtualRobot::CoinVisualizationFactory`; the defaults reflect that.
76 */
77 struct Frustum
78 {
79 /// Camera -> global.
80 Eigen::Matrix4f cameraPose = Eigen::Matrix4f::Identity();
81
82 /// Full opening angles [rad].
83 float horizontalFov = 1.0F;
84 float verticalFov = 1.0F;
85
86 /// Detection range along the viewing axis [mm].
87 float minDistance = 0.0F;
88 float maxDistance = 1e6F;
89
90 /// Viewing axis in the camera's local frame.
91 Eigen::Vector3f forwardLocal = Eigen::Vector3f::UnitZ();
92 /// Up axis in the camera's local frame.
93 Eigen::Vector3f upLocal = -Eigen::Vector3f::UnitY();
94
95 Eigen::Vector3f position() const;
96
97 /**
98 * Express a global point in the camera's viewing frame.
99 * @return (depth along forward, lateral along right, lateral along up).
100 */
101 Eigen::Vector3f toViewFrame(const Eigen::Vector3f& globalPoint) const;
102
103 /// Whether a global point lies inside the frustum.
104 bool contains(const Eigen::Vector3f& globalPoint) const;
105 };
106
107 /**
108 * Whether `box` is inside the camera frustum.
109 *
110 * @param requireFullyInside If true, all eight corners must be inside. Otherwise it is
111 * enough that any corner or the box centre is inside.
112 */
113 bool isInFieldOfView(const Frustum& frustum, const Box& box, bool requireFullyInside);
114
115 /**
116 * The face of `box` that points most directly at the camera - the "front facing side"
117 * of the bounding box.
118 *
119 * @return A face index in [0, 6): 0 = +X, 1 = -X, 2 = +Y, 3 = -Y, 4 = +Z, 5 = -Z.
120 */
121 int frontFaceIndex(const Frustum& frustum, const Box& box);
122
123 /// Outward unit normal of `faceIndex` in global coordinates.
124 Eigen::Vector3f faceNormal(const Box& box, int faceIndex);
125
126 /// Centre of `faceIndex` in global coordinates.
127 Eigen::Vector3f faceCenter(const Box& box, int faceIndex);
128
129 /**
130 * A `gridN` x `gridN` grid of sample points spread over `faceIndex`, in global coordinates.
131 *
132 * Samples sit at cell centres, so they never lie exactly on an edge shared with an
133 * adjacent object. `gridN == 1` yields the face centre alone.
134 */
135 std::vector<Eigen::Vector3f> sampleFace(const Box& box, int faceIndex, int gridN);
136
137 /**
138 * Intersect a ray with an oriented box using the slab method.
139 *
140 * @param unitDirection Must be normalized.
141 * @return Distance from `origin` to the nearest intersection, or `std::nullopt` if the ray
142 * misses. Returns 0 when `origin` lies inside the box.
143 */
144 std::optional<float> intersectRayBox(const Eigen::Vector3f& origin,
145 const Eigen::Vector3f& unitDirection,
146 const Box& box);
147
148 /**
149 * The fraction of sample points on the front face of `target` that the camera can see.
150 *
151 * A sample is blocked when any occluder is hit closer than the sample itself. An occluder
152 * containing the camera (such as the head link the camera is mounted in) never blocks.
153 *
154 * @param occluders Must NOT contain `target`; otherwise every ray is blocked by the
155 * target's own front face.
156 * @param epsilon Tolerance [mm] to absorb the hit exactly on the target's surface.
157 * @return A value in [0, 1]; 0 if the face has no samples.
158 */
159 float visibleFraction(const Frustum& frustum,
160 const Box& target,
161 const std::vector<Box>& occluders,
162 int gridN,
163 float epsilon);
164
165} // namespace armarx::fake_object_detector
Geometric primitives for the fake object detector.
DetectionMode
How strictly an object must be perceivable before it is reported.
@ LineOfSight
Additionally require a clear line of sight to the front face of the bounding box.
@ Always
Report every object, regardless of where the camera looks.
@ FieldOfView
Report an object once its bounding box enters the camera frustum.
std::vector< Eigen::Vector3f > sampleFace(const Box &box, int faceIndex, int gridN)
A gridN x gridN grid of sample points spread over faceIndex, in global coordinates.
int frontFaceIndex(const Frustum &frustum, const Box &box)
The face of box that points most directly at the camera - the "front facing side" of the bounding box...
std::optional< float > intersectRayBox(const Eigen::Vector3f &origin, const Eigen::Vector3f &unitDirection, const Box &box)
Intersect a ray with an oriented box using the slab method.
bool isInFieldOfView(const Frustum &frustum, const Box &box, bool requireFullyInside)
Whether box is inside the camera frustum.
float visibleFraction(const Frustum &frustum, const Box &target, const std::vector< Box > &occluders, int gridN, float epsilon)
The fraction of sample points on the front face of target that the camera can see.
Eigen::Vector3f faceNormal(const Box &box, int faceIndex)
Outward unit normal of faceIndex in global coordinates.
Eigen::Vector3f faceCenter(const Box &box, int faceIndex)
Centre of faceIndex in global coordinates.
An oriented bounding box in the global frame.
std::array< Eigen::Vector3f, 8 > corners() const
The eight corners in global coordinates.
Eigen::Matrix4f centerPose
Rotation = box axes, translation = box centre.
Eigen::Vector3f halfExtents
Half the extent along each local box axis.
bool contains(const Eigen::Vector3f &globalPoint) const
Whether a global point lies inside the frustum.
Eigen::Vector3f upLocal
Up axis in the camera's local frame.
float minDistance
Detection range along the viewing axis [mm].
Eigen::Matrix4f cameraPose
Camera -> global.
Eigen::Vector3f toViewFrame(const Eigen::Vector3f &globalPoint) const
Express a global point in the camera's viewing frame.
float horizontalFov
Full opening angles [rad].
Eigen::Vector3f forwardLocal
Viewing axis in the camera's local frame.