Skip to content

Commit 9e7358d

Browse files
authored
Merge pull request #38 from tue-robotics/feature/test-tools
Feature/test tools: refactored 'Fitter'
2 parents 6161275 + fce1452 commit 9e7358d

File tree

4 files changed

+725
-296
lines changed

4 files changed

+725
-296
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.autosave
2+
Lines changed: 180 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
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

@@ -18,8 +20,37 @@
1820

1921
typedef 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+
*/
2354
struct 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+
*/
3167
struct 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+
*/
4198
class Fitter
4299
{
43100

44101
public:
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

60151
private:
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

Comments
 (0)