26 #include <Eigen/Geometry>
48 bool frustum_culling =
true;
56 {
"enable", make_schema(&enabled,
"enable or disable component")},
57 {
"frustum_culling", make_schema(&frustum_culling,
"enable or disable frustum culling")},
71 for (uint64_t i = 1; i < points.size(); ++i) {
72 length += (points[i] - points[i - 1]).norm();
77 void cleanup_lane_boundary_points(std::vector<Eigen::Vector3d>& points) {
79 const double min_dist = 0.01;
80 std::vector<Eigen::Vector3d> pts_mod{points.front()};
81 for (uint64_t i = 1; i < points.size(); ++i) {
82 if ((points[i] - points[i - 1]).norm() > min_dist) {
83 pts_mod.push_back(points[i]);
90 const uint8_t PLANE_RIGHT_OK{0b0000'0001};
91 const uint8_t PLANE_LEFT_OK{0b0000'0010};
92 const uint8_t PLANE_LOW_OK{0b0000'0100};
93 const uint8_t PLANE_HIGH_OK{0b0000'1000};
94 const uint8_t PLANE_NEAR_OK{0b0001'0000};
95 const uint8_t PLANE_FAR_OK{0b0010'0000};
96 const uint8_t all_set =
97 PLANE_RIGHT_OK | PLANE_LEFT_OK | PLANE_LOW_OK | PLANE_HIGH_OK | PLANE_NEAR_OK | PLANE_FAR_OK;
99 uint8_t get_plane_mask(
const Frustum& frustum,
const Eigen::Vector3d& pt) {
100 double fov_h_l = -0.5 * frustum.fov_h + frustum.offset_h;
101 double fov_h_u = 0.5 * frustum.fov_h + frustum.offset_h;
102 double fov_v_l = -0.5 * frustum.fov_v + frustum.offset_v;
103 double fov_v_u = 0.5 * frustum.fov_v + frustum.offset_v;
105 if (fov_h_l < -M_PI || fov_h_u > M_PI || fov_v_l < -M_PI || fov_v_u > M_PI) {
107 throw std::runtime_error(fmt::format(
108 "The get_plane_mask function of the clothoid fit plugin only works in the range [-M_PI, M_PI]."
109 "However, the following values were calculated and at least one condition was not met: \n"
110 "fov_h_l: {}; has to be greater equal to -M_PI, \n"
111 "fov_h_u: {}; has to be smaller equal to M_PI, \n"
112 "fov_v_l: {}; has to be greater equal to -M_PI,\n"
113 "fov_v_u: {}; has to be smaller equal to M_PI. \n",
114 fov_h_l, fov_h_u, fov_v_l, fov_v_u));
117 double range_mult = cos(frustum.offset_h) * cos(frustum.offset_v);
118 uint8_t plane_mask{0};
120 double x = pt.x() == 0.0 ? 1.0e-10 : pt.x();
121 double phi = atan2(pt.y(), x);
122 if (phi >= fov_h_l) {
124 plane_mask |= PLANE_RIGHT_OK;
126 if (phi <= fov_h_u) {
128 plane_mask |= PLANE_LEFT_OK;
131 phi = atan2(pt.z(), x);
132 if (phi >= fov_v_l) {
134 plane_mask |= PLANE_LOW_OK;
136 if (phi <= fov_v_u) {
138 plane_mask |= PLANE_HIGH_OK;
141 x = pt.x() * range_mult;
142 if (x >= frustum.clip_near) {
144 plane_mask |= PLANE_NEAR_OK;
146 if (x <= frustum.clip_far) {
148 plane_mask |= PLANE_FAR_OK;
153 std::vector<Eigen::Vector3d> interpolate_to_frustum_planes(
const Frustum& frustum,
154 const Eigen::Vector3d& pt1,
156 const Eigen::Vector3d& pt2,
158 auto interpolate_point_on_plane = [](
const Eigen::Vector3d& x1,
const Eigen::Vector3d& x2,
159 const Eigen::Vector3d& xp,
160 const Eigen::Vector3d& nv) -> Eigen::Vector3d {
162 Eigen::Vector3d dir = x2 - x1;
163 double int_loc = nv.dot((xp - x1)) / nv.dot(dir);
164 return x1 + int_loc * dir;
167 double offset = 0.001;
168 std::vector<Eigen::Vector3d> pts_intrp;
170 if (((mask1 ^ mask2) & PLANE_RIGHT_OK) == PLANE_RIGHT_OK) {
172 double fov_h_l = -0.5 * frustum.fov_h + frustum.offset_h;
174 Eigen::Vector3d nvec =
175 Eigen::Vector3d(cos(fov_h_l), sin(fov_h_l), 0).cross(Eigen::Vector3d(0, 0, -1));
176 nvec = nvec / nvec.norm();
177 Eigen::Vector3d pt = interpolate_point_on_plane(pt1, pt2, Eigen::Vector3d(0, 0, 0), nvec);
178 pts_intrp.push_back(pt + offset * nvec);
180 if (((mask1 ^ mask2) & PLANE_LEFT_OK) == PLANE_LEFT_OK) {
182 double fov_h_u = 0.5 * frustum.fov_h + frustum.offset_h;
184 Eigen::Vector3d nvec =
185 Eigen::Vector3d(cos(fov_h_u), sin(fov_h_u), 0).cross(Eigen::Vector3d(0, 0, 1));
186 nvec = nvec / nvec.norm();
187 Eigen::Vector3d pt = interpolate_point_on_plane(pt1, pt2, Eigen::Vector3d(0, 0, 0), nvec);
188 pts_intrp.push_back(pt + offset * nvec);
190 if (((mask1 ^ mask2) & PLANE_LOW_OK) == PLANE_LOW_OK) {
192 double fov_v_l = -0.5 * frustum.fov_v + frustum.offset_v;
194 Eigen::Vector3d nvec =
195 Eigen::Vector3d(cos(fov_v_l), 0, sin(fov_v_l)).cross(Eigen::Vector3d(0, 1, 0));
196 nvec = nvec / nvec.norm();
197 Eigen::Vector3d pt = interpolate_point_on_plane(pt1, pt2, Eigen::Vector3d(0, 0, 0), nvec);
198 pts_intrp.push_back(pt + offset * nvec);
200 if (((mask1 ^ mask2) & PLANE_HIGH_OK) == PLANE_HIGH_OK) {
202 double fov_v_u = 0.5 * frustum.fov_v + frustum.offset_v;
204 Eigen::Vector3d nvec =
205 Eigen::Vector3d(cos(fov_v_u), 0, sin(fov_v_u)).cross(Eigen::Vector3d(0, -1, 0));
206 nvec = nvec / nvec.norm();
207 Eigen::Vector3d pt = interpolate_point_on_plane(pt1, pt2, Eigen::Vector3d(0, 0, 0), nvec);
208 pts_intrp.push_back(pt + offset * nvec);
210 if (((mask1 ^ mask2) & PLANE_NEAR_OK) == PLANE_NEAR_OK) {
212 double a = cos(frustum.offset_v);
213 Eigen::Vector3d nvec = Eigen::Vector3d(a * cos(frustum.offset_h), a * sin(frustum.offset_h),
214 sin(frustum.offset_v));
216 nvec = nvec / nvec.norm();
217 Eigen::Vector3d pt = interpolate_point_on_plane(pt1, pt2, frustum.clip_near * nvec, nvec);
218 pts_intrp.push_back(pt + offset * nvec);
220 if (((mask1 ^ mask2) & PLANE_FAR_OK) == PLANE_FAR_OK) {
222 double a = cos(frustum.offset_v);
223 Eigen::Vector3d nvec = Eigen::Vector3d(a * cos(frustum.offset_h), a * sin(frustum.offset_h),
224 sin(frustum.offset_v));
226 nvec = -1.0 * nvec / nvec.norm();
227 Eigen::Vector3d pt = interpolate_point_on_plane(pt1, pt2, -1.0 * frustum.clip_far * nvec, nvec);
228 pts_intrp.push_back(pt + offset * nvec);
231 auto it_rm = std::remove_if(
232 pts_intrp.begin(), pts_intrp.end(),
233 [&frustum](
const Eigen::Vector3d& pt) { return get_plane_mask(frustum, pt) != all_set; });
234 pts_intrp.erase(it_rm, pts_intrp.end());
240 void lane_boundary_point_culling(
const Frustum& frustum, std::vector<Eigen::Vector3d>& points,
241 std::vector<Eigen::Vector3d>& pts_out_start,
242 std::vector<Eigen::Vector3d>& pts_out_end) {
243 std::vector<uint8_t> mask_out_start, mask_out_end;
245 std::vector<Eigen::Vector3d> pts_frustum;
246 pts_frustum.reserve(points.size());
247 bool found_pts{
false};
248 for (
const auto& pt : points) {
249 uint8_t plane_mask = get_plane_mask(frustum, pt);
251 if (plane_mask == all_set) {
252 if (pts_out_end.size() > 0) {
256 if (pts_frustum.size() == 0 && pts_out_start.size() > 0) {
258 auto pt_intrp = interpolate_to_frustum_planes(frustum, pts_out_start.back(),
259 mask_out_start.back(), pt, plane_mask);
260 if (pt_intrp.size() == 1) {
261 pts_frustum.push_back(pt_intrp.front());
263 throw cloe::ModelError(
"Interpolation of first point inside frustum should be unique.");
266 pts_frustum.push_back(pt);
268 }
else if (!found_pts) {
270 if (pts_out_start.size() == 0) {
272 pts_out_start.push_back(pt);
273 mask_out_start.push_back(plane_mask);
276 Eigen::Vector3d pt_prev = pts_out_start.back();
279 interpolate_to_frustum_planes(frustum, pt_prev, mask_out_start.back(), pt, plane_mask);
280 if (pts_intrp.size() > 0) {
282 std::sort(pts_intrp.begin(), pts_intrp.end(),
283 [pt_prev](
const Eigen::Vector3d& pt1,
const Eigen::Vector3d& pt2) {
284 return ((pt1 - pt_prev).norm() < (pt2 - pt_prev).norm());
287 pts_frustum.insert(pts_frustum.end(), pts_intrp.begin(), pts_intrp.end());
289 pts_out_end.push_back(pt);
290 mask_out_end.push_back(plane_mask);
293 pts_out_start.push_back(pt);
294 mask_out_start.push_back(plane_mask);
299 if (pts_out_end.size() == 0) {
302 interpolate_to_frustum_planes(frustum, pts_frustum.back(), all_set, pt, plane_mask);
303 if (pt_intrp.size() == 1) {
304 pts_frustum.push_back(pt_intrp.front());
306 throw cloe::ModelError(
"Interpolation of last point inside frustum should be unique.");
309 pts_out_end.push_back(pt);
310 mask_out_end.push_back(plane_mask);
313 points = pts_frustum;
323 double dx = pt1.x() - pt0.x();
324 dx = dx == 0.0 ? 1.0e-10 : dx;
325 double slope = (pt1.y() - pt0.y()) / dx;
330 double calc_heading_angle_adv(
const Eigen::Vector3d& ptm1,
const Eigen::Vector3d& pt0,
331 const Eigen::Vector3d& pt1) {
332 double dx1 = pt1.x() - pt0.x();
333 dx1 = dx1 == 0.0 ? 1.0e-10 : dx1;
334 double dx0 = pt0.x() - ptm1.x();
335 dx0 = dx0 == 0.0 ? 1.0e-10 : dx0;
336 double dx = dx1 + dx0;
337 dx = dx == 0.0 ? 1.0e-10 : dx;
338 double slope = -1.0 * dx1 / (dx0 * dx) * ptm1.y() + (dx1 - dx0) / (dx0 * dx1) * pt0.y() +
339 dx0 / (dx1 * dx) * pt1.y();
354 logger->debug(
"Discarding short lane boundary segment < 0.5m.");
357 cleanup_lane_boundary_points(lb.points);
359 std::vector<Eigen::Vector3d> pts_out_start, pts_out_end;
360 if (frustum_culling) {
361 lane_boundary_point_culling(frustum, lb.points, pts_out_start, pts_out_end);
363 if (lb.points.size() < 2) {
364 logger->debug(
"Clothoid fit requires at least two points.");
367 Eigen::Vector3d x0 = lb.points.at(0);
368 lb.dx_start = x0.x();
369 lb.dy_start = x0.y();
371 if (pts_out_start.size() > 0) {
372 hdg0 = calc_heading_angle_adv(pts_out_start.back(), lb.points.at(0), lb.points.at(1));
376 lb.heading_start = hdg0;
377 int n = lb.points.size() - 1;
378 Eigen::Vector3d x1 = lb.points.at(n);
380 if (pts_out_end.size() > 0) {
381 hdg1 = calc_heading_angle_adv(lb.points.at(n - 1), lb.points.at(n), pts_out_end.front());
386 g1_fit::calc_clothoid(x0.x(), x0.y(), hdg0, x1.x(), x1.y(), hdg1, lb.curv_hor_start,
387 lb.curv_hor_change, lb.dx_end);
395 std::shared_ptr<LaneBoundarySensor> sensor)
401 &this->config_,
"config",
"configure clothoid fitting component"));
403 "activation",
"switch clothoid fitting on/off",
"enable", &config_.enabled);
410 if (config_.enabled) {
411 for (
auto& kv : sensor_in_->sensed_lane_boundaries()) {
419 lbs_ = sensor_in_->sensed_lane_boundaries();
427 const Eigen::Isometry3d&
mount_pose()
const override {
return sensor_in_->mount_pose(); }
439 if (t < sync.
time()) {
444 t = sensor_in_->process(sync);
469 std::shared_ptr<LaneBoundarySensor> sensor_in_;
470 mutable bool cached_;
471 mutable LaneBoundaries lbs_;
Duration process(const Sync &sync) override
Definition: component.hpp:181
void abort() override
Definition: component.hpp:191
void reset() override
Definition: component.hpp:186
virtual Logger logger() const
Definition: entity.hpp:101
const std::string & name() const
Definition: entity.hpp:67
Definition: lane_sensor.hpp:33
Definition: lane_boundary.hpp:36
Definition: registrar.hpp:121
virtual void register_action(std::unique_ptr< ActionFactory > &&)=0
virtual Duration time() const =0
Definition: set_action.hpp:88
Definition: clothoid_fit.hpp:392
void enroll(Registrar &r) override
Definition: clothoid_fit.hpp:399
const LaneBoundaries & sensed_lane_boundaries() const override
Definition: clothoid_fit.hpp:406
void abort() override
Definition: clothoid_fit.hpp:456
const Eigen::Isometry3d & mount_pose() const override
Definition: clothoid_fit.hpp:427
const Frustum & frustum() const override
Definition: clothoid_fit.hpp:425
void reset() override
Definition: clothoid_fit.hpp:450
Duration process(const Sync &sync) override
Definition: clothoid_fit.hpp:435
Definition: confable.hpp:98
Definition: schema.hpp:173
std::chrono::nanoseconds Duration
Definition: cloe_fwd.hpp:36
double estimate_lane_boundary_length(const std::vector< Eigen::Vector3d > &points)
Definition: clothoid_fit.hpp:69
bool fit_clothoid(const cloe::Logger &logger, bool frustum_culling, const Frustum &frustum, LaneBoundary &lb)
Definition: clothoid_fit.hpp:350
double calc_heading_angle(const Eigen::Vector3d &pt0, const Eigen::Vector3d &pt1)
Definition: clothoid_fit.hpp:322
Definition: clothoid_fit.hpp:46
Definition: frustum.hpp:37