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;
104 double range_mult = cos(frustum.offset_h) * cos(frustum.offset_v);
105 uint8_t plane_mask{0};
107 double x = pt.x() == 0.0 ? 1.0e-10 : pt.x();
108 double phi = atan2(pt.y(), x);
109 if (phi >= fov_h_l) {
111 plane_mask |= PLANE_RIGHT_OK;
113 if (phi <= fov_h_u) {
115 plane_mask |= PLANE_LEFT_OK;
118 phi = atan2(pt.z(), x);
119 if (phi >= fov_v_l) {
121 plane_mask |= PLANE_LOW_OK;
123 if (phi <= fov_v_u) {
125 plane_mask |= PLANE_HIGH_OK;
128 x = pt.x() * range_mult;
129 if (x >= frustum.clip_near) {
131 plane_mask |= PLANE_NEAR_OK;
133 if (x <= frustum.clip_far) {
135 plane_mask |= PLANE_FAR_OK;
140 std::vector<Eigen::Vector3d> interpolate_to_frustum_planes(
const Frustum& frustum,
141 const Eigen::Vector3d& pt1,
143 const Eigen::Vector3d& pt2,
145 auto interpolate_point_on_plane = [](
const Eigen::Vector3d& x1,
const Eigen::Vector3d& x2,
146 const Eigen::Vector3d& xp,
147 const Eigen::Vector3d& nv) -> Eigen::Vector3d {
149 Eigen::Vector3d dir = x2 - x1;
150 double int_loc = nv.dot((xp - x1)) / nv.dot(dir);
151 return x1 + int_loc * dir;
154 double offset = 0.001;
155 std::vector<Eigen::Vector3d> pts_intrp;
157 if (((mask1 ^ mask2) & PLANE_RIGHT_OK) == PLANE_RIGHT_OK) {
159 double fov_h_l = -0.5 * frustum.fov_h + frustum.offset_h;
161 Eigen::Vector3d nvec =
162 Eigen::Vector3d(cos(fov_h_l), sin(fov_h_l), 0).cross(Eigen::Vector3d(0, 0, -1));
163 nvec = nvec / nvec.norm();
164 Eigen::Vector3d pt = interpolate_point_on_plane(pt1, pt2, Eigen::Vector3d(0, 0, 0), nvec);
165 pts_intrp.push_back(pt + offset * nvec);
167 if (((mask1 ^ mask2) & PLANE_LEFT_OK) == PLANE_LEFT_OK) {
169 double fov_h_u = 0.5 * frustum.fov_h + frustum.offset_h;
171 Eigen::Vector3d nvec =
172 Eigen::Vector3d(cos(fov_h_u), sin(fov_h_u), 0).cross(Eigen::Vector3d(0, 0, 1));
173 nvec = nvec / nvec.norm();
174 Eigen::Vector3d pt = interpolate_point_on_plane(pt1, pt2, Eigen::Vector3d(0, 0, 0), nvec);
175 pts_intrp.push_back(pt + offset * nvec);
177 if (((mask1 ^ mask2) & PLANE_LOW_OK) == PLANE_LOW_OK) {
179 double fov_v_l = -0.5 * frustum.fov_v + frustum.offset_v;
181 Eigen::Vector3d nvec =
182 Eigen::Vector3d(cos(fov_v_l), 0, sin(fov_v_l)).cross(Eigen::Vector3d(0, 1, 0));
183 nvec = nvec / nvec.norm();
184 Eigen::Vector3d pt = interpolate_point_on_plane(pt1, pt2, Eigen::Vector3d(0, 0, 0), nvec);
185 pts_intrp.push_back(pt + offset * nvec);
187 if (((mask1 ^ mask2) & PLANE_HIGH_OK) == PLANE_HIGH_OK) {
189 double fov_v_u = 0.5 * frustum.fov_v + frustum.offset_v;
191 Eigen::Vector3d nvec =
192 Eigen::Vector3d(cos(fov_v_u), 0, sin(fov_v_u)).cross(Eigen::Vector3d(0, -1, 0));
193 nvec = nvec / nvec.norm();
194 Eigen::Vector3d pt = interpolate_point_on_plane(pt1, pt2, Eigen::Vector3d(0, 0, 0), nvec);
195 pts_intrp.push_back(pt + offset * nvec);
197 if (((mask1 ^ mask2) & PLANE_NEAR_OK) == PLANE_NEAR_OK) {
199 double a = cos(frustum.offset_v);
200 Eigen::Vector3d nvec = Eigen::Vector3d(a * cos(frustum.offset_h), a * sin(frustum.offset_h),
201 sin(frustum.offset_v));
203 nvec = nvec / nvec.norm();
204 Eigen::Vector3d pt = interpolate_point_on_plane(pt1, pt2, frustum.clip_near * nvec, nvec);
205 pts_intrp.push_back(pt + offset * nvec);
207 if (((mask1 ^ mask2) & PLANE_FAR_OK) == PLANE_FAR_OK) {
209 double a = cos(frustum.offset_v);
210 Eigen::Vector3d nvec = Eigen::Vector3d(a * cos(frustum.offset_h), a * sin(frustum.offset_h),
211 sin(frustum.offset_v));
213 nvec = -1.0 * nvec / nvec.norm();
214 Eigen::Vector3d pt = interpolate_point_on_plane(pt1, pt2, -1.0 * frustum.clip_far * nvec, nvec);
215 pts_intrp.push_back(pt + offset * nvec);
218 auto it_rm = std::remove_if(
219 pts_intrp.begin(), pts_intrp.end(),
220 [&frustum](
const Eigen::Vector3d& pt) { return get_plane_mask(frustum, pt) != all_set; });
221 pts_intrp.erase(it_rm, pts_intrp.end());
227 void lane_boundary_point_culling(
const Frustum& frustum, std::vector<Eigen::Vector3d>& points,
228 std::vector<Eigen::Vector3d>& pts_out_start,
229 std::vector<Eigen::Vector3d>& pts_out_end) {
230 std::vector<uint8_t> mask_out_start, mask_out_end;
232 std::vector<Eigen::Vector3d> pts_frustum;
233 pts_frustum.reserve(points.size());
234 bool found_pts{
false};
235 for (
const auto& pt : points) {
236 uint8_t plane_mask = get_plane_mask(frustum, pt);
238 if (plane_mask == all_set) {
239 if (pts_out_end.size() > 0) {
243 if (pts_frustum.size() == 0 && pts_out_start.size() > 0) {
245 auto pt_intrp = interpolate_to_frustum_planes(frustum, pts_out_start.back(),
246 mask_out_start.back(), pt, plane_mask);
247 if (pt_intrp.size() == 1) {
248 pts_frustum.push_back(pt_intrp.front());
250 throw cloe::ModelError(
"Interpolation of first point inside frustum should be unique.");
253 pts_frustum.push_back(pt);
255 }
else if (!found_pts) {
257 if (pts_out_start.size() == 0) {
259 pts_out_start.push_back(pt);
260 mask_out_start.push_back(plane_mask);
263 Eigen::Vector3d pt_prev = pts_out_start.back();
266 interpolate_to_frustum_planes(frustum, pt_prev, mask_out_start.back(), pt, plane_mask);
267 if (pts_intrp.size() > 0) {
269 std::sort(pts_intrp.begin(), pts_intrp.end(),
270 [pt_prev](
const Eigen::Vector3d& pt1,
const Eigen::Vector3d& pt2) {
271 return ((pt1 - pt_prev).norm() < (pt2 - pt_prev).norm());
274 pts_frustum.insert(pts_frustum.end(), pts_intrp.begin(), pts_intrp.end());
276 pts_out_end.push_back(pt);
277 mask_out_end.push_back(plane_mask);
280 pts_out_start.push_back(pt);
281 mask_out_start.push_back(plane_mask);
286 if (pts_out_end.size() == 0) {
289 interpolate_to_frustum_planes(frustum, pts_frustum.back(), all_set, pt, plane_mask);
290 if (pt_intrp.size() == 1) {
291 pts_frustum.push_back(pt_intrp.front());
293 throw cloe::ModelError(
"Interpolation of last point inside frustum should be unique.");
296 pts_out_end.push_back(pt);
297 mask_out_end.push_back(plane_mask);
300 points = pts_frustum;
310 double dx = pt1.x() - pt0.x();
311 dx = dx == 0.0 ? 1.0e-10 : dx;
312 double slope = (pt1.y() - pt0.y()) / dx;
317 double calc_heading_angle_adv(
const Eigen::Vector3d& ptm1,
const Eigen::Vector3d& pt0,
318 const Eigen::Vector3d& pt1) {
319 double dx1 = pt1.x() - pt0.x();
320 dx1 = dx1 == 0.0 ? 1.0e-10 : dx1;
321 double dx0 = pt0.x() - ptm1.x();
322 dx0 = dx0 == 0.0 ? 1.0e-10 : dx0;
323 double dx = dx1 + dx0;
324 dx = dx == 0.0 ? 1.0e-10 : dx;
325 double slope = -1.0 * dx1 / (dx0 * dx) * ptm1.y() + (dx1 - dx0) / (dx0 * dx1) * pt0.y() +
326 dx0 / (dx1 * dx) * pt1.y();
341 logger->debug(
"Discarding short lane boundary segment < 0.5m.");
344 cleanup_lane_boundary_points(lb.points);
346 std::vector<Eigen::Vector3d> pts_out_start, pts_out_end;
347 if (frustum_culling) {
348 lane_boundary_point_culling(frustum, lb.points, pts_out_start, pts_out_end);
350 if (lb.points.size() < 2) {
351 logger->debug(
"Clothoid fit requires at least two points.");
354 Eigen::Vector3d x0 = lb.points.at(0);
355 lb.dx_start = x0.x();
356 lb.dy_start = x0.y();
358 if (pts_out_start.size() > 0) {
359 hdg0 = calc_heading_angle_adv(pts_out_start.back(), lb.points.at(0), lb.points.at(1));
363 lb.heading_start = hdg0;
364 int n = lb.points.size() - 1;
365 Eigen::Vector3d x1 = lb.points.at(n);
367 if (pts_out_end.size() > 0) {
368 hdg1 = calc_heading_angle_adv(lb.points.at(n - 1), lb.points.at(n), pts_out_end.front());
373 g1_fit::calc_clothoid(x0.x(), x0.y(), hdg0, x1.x(), x1.y(), hdg1, lb.curv_hor_start,
374 lb.curv_hor_change, lb.dx_end);
382 std::shared_ptr<LaneBoundarySensor> sensor)
388 &this->config_,
"config",
"configure clothoid fitting component"));
390 "activation",
"switch clothoid fitting on/off",
"enable", &config_.enabled);
397 if (config_.enabled) {
398 for (
auto& kv : sensor_in_->sensed_lane_boundaries()) {
406 lbs_ = sensor_in_->sensed_lane_boundaries();
414 const Eigen::Isometry3d&
mount_pose()
const override {
return sensor_in_->mount_pose(); }
426 if (t < sync.
time()) {
431 t = sensor_in_->process(sync);
456 std::shared_ptr<LaneBoundarySensor> sensor_in_;
457 mutable bool cached_;
458 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:116
virtual void register_action(std::unique_ptr< ActionFactory > &&)=0
virtual Duration time() const =0
Definition: set_action.hpp:86
Definition: clothoid_fit.hpp:379
void enroll(Registrar &r) override
Definition: clothoid_fit.hpp:386
const LaneBoundaries & sensed_lane_boundaries() const override
Definition: clothoid_fit.hpp:393
void abort() override
Definition: clothoid_fit.hpp:443
const Eigen::Isometry3d & mount_pose() const override
Definition: clothoid_fit.hpp:414
const Frustum & frustum() const override
Definition: clothoid_fit.hpp:412
void reset() override
Definition: clothoid_fit.hpp:437
Duration process(const Sync &sync) override
Definition: clothoid_fit.hpp:422
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:337
double calc_heading_angle(const Eigen::Vector3d &pt0, const Eigen::Vector3d &pt1)
Definition: clothoid_fit.hpp:309
Definition: clothoid_fit.hpp:46
Definition: frustum.hpp:37