11#ifndef ED_SENSOR_INTEGRATION_FITTER_PLUGIN_H_
22#define ED_SENSOR_INTEGRATION_FITTER_PLUGIN_H_
33
4+ #include < exception>
5+
46#include < ed/plugin.h>
57#include < ed/types.h>
68
1820
1921typedef std::vector<std::vector<geo::Vec2> > Shape2D;
2022
23+ /* *
24+ * Forward declaration classes
25+ */
26+ class Candidate ;
27+ class OptimalFit ;
28+ struct YawRange ;
29+
30+ // ----------------------------------------------------------------------------------------------------
31+
32+ /* *
33+ * @brief The FitterError class exception is thrown in case of errors in the fitter algorithm
34+ */
35+ class FitterError : public std ::exception
36+ {
37+ public:
38+ FitterError (const std::string& msg){error_message_ = msg;}
39+
40+ virtual const char * what () const throw ()
41+ {
42+ return error_message_.c_str ();
43+ }
44+ private:
45+ std::string error_message_;
46+ };
47+
2148// ----------------------------------------------------------------------------------------------------
2249
50+ /* *
51+ * @brief The EntityRepresentation2D struct contains the (downprojected) shape that is used for the
52+ * fitting
53+ */
2354struct EntityRepresentation2D
2455{
2556 unsigned int shape_revision;
@@ -28,6 +59,11 @@ struct EntityRepresentation2D
2859
2960// ----------------------------------------------------------------------------------------------------
3061
62+ /* *
63+ * @brief The FitterData struct contains the downprojected (hence 2D) sensor readings as well as the
64+ * sensor pose while taking the image (including decomposition in x, y yaw and roll, pitch, z
65+ * coordinates)
66+ */
3167struct FitterData
3268{
3369 std::vector<double > sensor_ranges;
@@ -38,43 +74,172 @@ struct FitterData
3874
3975// ----------------------------------------------------------------------------------------------------
4076
77+ /* *
78+ * @brief The EstimationInputData struct contains processed data from the world model (entity, 2D shape
79+ * w.r.t. shape center at (0, 0), the shape center in the world model, the beam index that is expected
80+ * to pass close to the shape center, the rendered 'model ranges' *without the entity to fit*
81+ * and the (2D) sensor ranges.
82+ */
83+ struct EstimationInputData
84+ {
85+ ed::EntityConstPtr entity;
86+ Shape2D shape2d_transformed;
87+ geo::Vec2 shape_center;
88+ int expected_center_beam;
89+ std::vector<double > model_ranges;
90+ std::vector<double > sensor_ranges;
91+ };
92+
93+ // ----------------------------------------------------------------------------------------------------
94+
95+ /* *
96+ * @brief The Fitter class contains the algorithm to do the 2D fit
97+ */
4198class Fitter
4299{
43100
44101public:
45102
46- Fitter ();
103+ /* *
104+ * @brief Fitter constructor
105+ * @param nr_data_points nr_data_points for the beam model
106+ */
107+ Fitter (uint nr_data_points = 200 ); // TODO: remove hard-coded values
47108
48109 ~Fitter ();
49110
111+ /* *
112+ * @brief processSensorData pre-processes sensor data, i.e., performs a downprojection of the input
113+ * depth image based on the provided sensor pose and stores the result in the FitterData struct
114+ * @param image input (depth) image
115+ * @param sensor_pose pose of the sensor in the world while taking the image
116+ * @param data processed data is stored here
117+ */
50118 void processSensorData (const rgbd::Image& image, const geo::Pose3D& sensor_pose, FitterData& data) const ;
51119
52- void renderEntity (const ed::EntityConstPtr& e, const geo::Pose3D& sensor_pose_xya, int identifier,
53- std::vector<double >& model_ranges, std::vector<int >& identifiers);
54-
120+ /* *
121+ * @brief estimateEntityPose performs the entity pose estimation. Basically, tries to call estimateEntityPoseImp and
122+ * return the results, catches any FitterErrors and returns false
123+ * @param data pre-processed sensor data
124+ * @param world world model
125+ * @param id id of the entity we're trying to fit
126+ * @param expected_pose pose where the entity is expected
127+ * @param fitted_pose the fitted pose is stored here
128+ * @param max_yaw_change maximum allowed yaw rotation
129+ * @return success or failure
130+ */
55131 bool estimateEntityPose (const FitterData& data, const ed::WorldModel& world, const ed::UUID& id,
56- const geo::Pose3D& expected_pose, geo::Pose3D& fitted_pose, double max_yaw_change = M_PI);
57-
58- EntityRepresentation2D GetOrCreateEntity2D (const ed::EntityConstPtr& e);
132+ const geo::Pose3D& expected_pose, geo::Pose3D& fitted_pose, double max_yaw_change = M_PI) const ;
133+
134+ /* *
135+ * @brief findOptimum finds the 'optimal' fit by evaluating candidates over all beams and over the entire
136+ * yaw range.
137+ * @param input_data EstimationInputData: everything that's required
138+ * @param yaw_range min and max yaw range to sample over
139+ * @return pointer to the optimum
140+ */
141+ std::unique_ptr<OptimalFit> findOptimum (const EstimationInputData& input_data, const YawRange& yaw_range) const ;
142+
143+ /* *
144+ * @brief GetOrCreateEntity2D returns the downprojected shape of the entity. If it's already in the cache,
145+ * it's returned directly. If not, it's obtained from the entity
146+ * @param e input entity
147+ * @return 2D entity representation
148+ */
149+ EntityRepresentation2D GetOrCreateEntity2D (const ed::EntityConstPtr& e) const ;
59150
60151private:
61152
62- // Fitting
153+ /* *
154+ * @brief renderEntity renders the entity in 2D using the beam model with the provided (2D) sensor pose.
155+ * The ranges of the simulated measurement are stored in 'model ranges' and the 'identifier' is stored
156+ * in the 'identifiers' vector in order to be able to distinguish multiple entity shapes.
157+ * @param e entity to render
158+ * @param sensor_pose_xya 2D sensor pose (i.e., only x, y and yaw are used)
159+ * @param identifier
160+ * @param model_ranges
161+ * @param identifiers
162+ */
163+ void renderEntity (const ed::EntityConstPtr& e, const geo::Pose3D& sensor_pose_xya, int identifier,
164+ std::vector<double >& model_ranges, std::vector<int >& identifiers) const ;
165+
166+ /* *
167+ * @brief estimateEntityPoseImp actual implementation of the entity pose estimation. Preprocess input data
168+ * iterates over all beams and the yaw range to compuate the best fit. Finally, a correction of the sensor
169+ * pose is computed and the result is transformed back to 3D
170+ * @param data pre-processed sensor data
171+ * @param world world model
172+ * @param id id of the entity we're trying to fit
173+ * @param expected_pose pose where the entity is expected
174+ * @param fitted_pose the fitted pose is stored here
175+ * @param max_yaw_change maximum allowed yaw rotation
176+ * @return success or failure
177+ */
178+ bool estimateEntityPoseImp (const FitterData& data, const ed::WorldModel& world, const ed::UUID& id,
179+ const geo::Pose3D& expected_pose, geo::Pose3D& fitted_pose, double max_yaw_change) const ;
180+
181+ /* *
182+ * @brief preProcessInputData pre-processes the inputs for the fitting algorithm, i.e., gets the shape of
183+ * the entity, compute its center, transform the shape to (0, 0), and compute the expected center beam.
184+ * Everything is stored in a struct that can be used (const) throughout the rest of the algorithm
185+ * @param world world model
186+ * @param id id of the entity to fit
187+ * @param expected_pose expected pose of the entity
188+ * @param data input sensor data
189+ * @return EstimationInputData used throughout the rest of the algorithm
190+ */
191+ EstimationInputData preProcessInputData (const ed::WorldModel &world, const ed::UUID &id, const geo::Pose3D &expected_pose, const FitterData &data) const ;
192+
193+ /* *
194+ * @brief get2DShape gets the downprojected, 2D shape from an entity
195+ * @param entity_ptr points to the entity
196+ * @return the computed shape
197+ */
198+ Shape2D get2DShape (ed::EntityConstPtr entity_ptr) const ;
199+
200+ /* *
201+ * @brief renderWorldModel2D renders all world model entities
202+ * @param world world model
203+ * @param sensor_pose_xya sensor pose
204+ * @param skip_id id to skip (so we don't render the entity we're fitting
205+ * @param model_ranges data is stored here
206+ * @param identifiers to distinguish which 'range' belongs to which entity. In this case, -1 will
207+ * be used as an identifier
208+ */
209+ void renderWorldModel2D (const ed::WorldModel& world, const geo::Pose3D& sensor_pose_xya, const ed::UUID& skip_id,
210+ std::vector<double >& model_ranges, std::vector<int >& identifiers) const ;
211+
212+ /* *
213+ * @brief checkExpectedBeamThroughEntity checks if the expected center beam passes through the entity. If
214+ * not: something went wrong
215+ * @param model_ranges
216+ * @param entity
217+ * @param sensor_pose_xya
218+ * @param expected_center_beam
219+ */
220+ void checkExpectedBeamThroughEntity (const std::vector<double > &model_ranges, ed::EntityConstPtr entity,
221+ const geo::Pose3D &sensor_pose_xya, const int expected_center_beam) const ;
222+
223+ /* *
224+ * @brief evaluateCandidate renders the model, transform the model along the beam direction to get this
225+ * distance right and render once again
226+ * @param static_data 'static' input data that was the result from the pre-processing
227+ * @param candidate contains the 'candidate' beam index and yaw that is evaluated
228+ * @return
229+ */
230+ bool evaluateCandidate (const EstimationInputData& static_data, Candidate& candidate) const ;
63231
232+ // Fitting
64233 BeamModel beam_model_;
65234
66-
67235 // 2D Entity shapes
68-
69- std::map<ed::UUID, EntityRepresentation2D> entity_shapes_;
70-
236+ mutable std::map<ed::UUID, EntityRepresentation2D> entity_shapes_;
71237
72238 // Models
73-
74- std::map<std::string, EntityRepresentation2D> models_;
75-
76239 ed::models::ModelLoader model_loader_;
77240
241+ uint nr_data_points_;
242+
78243};
79244
80245#endif
0 commit comments