geolib2
LaserRangeFinder.cpp
Go to the documentation of this file.
2 #include "geolib/Shape.h"
3 
4 #include <cmath>
5 
6 namespace geo {
7 
8 LaserRangeFinder::LaserRangeFinder() : a_min_(0), a_max_(0), range_min_(0), range_max_(0), num_beams_(0), angle_incr_(0) {
9 }
10 
12 }
13 
14 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15 //
16 // RENDERING
17 //
18 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19 
20 // ----------------------------------------------------------------------------------------------------
21 
23 {
24  Vec2 diff = p2 - p1;
25  double line_length_sq = diff.length2();
26 
27  // Get rid of null cases
28  if ((p1.x == 0 && p1.y == 0) || (p2.x == 0 && p2.y == 0) || line_length_sq == 0)
29  return;
30 
31 
32  if (lrf_->range_max_ > 0)
33  {
34  // Calculate distance to the line
35 
36  double t = p1.dot(diff) / -line_length_sq;
37 
38  double distance_sq;
39 
40  if (t < 0)
41  distance_sq = p1.length2();
42  else if (t > 1)
43  distance_sq = p2.length2();
44  else
45  distance_sq = (p1 + t * diff).length2();
46 
47  // If too far, skip
48  if (distance_sq > lrf_->range_max_ * lrf_->range_max_)
49  return;
50  }
51 
52  // Get the angle / beam indices based on the slope
53  int i_p1 = lrf_->getAngleUpperIndexRaw(p1.x, p1.y);
54  int i_p2 = lrf_->getAngleUpperIndexRaw(p2.x, p2.y);
55 
56  // Get the minimum and maximum
57  int i_min = std::min<int>(i_p1, i_p2);
58  int i_max = std::max<int>(i_p1, i_p2);
59 
60  // We need to differentiate between two cases:
61  // - from min to max is less than half a circle
62  // - from min to max is more than half a circle (the line can be 'occluded' by the blind spot of the sensor)
63 
64  // In the latter case, we may need to render two parts
65 
66  uint i_min1, i_max1, i_min2, i_max2;
67  if (i_max - i_min < static_cast<int>(lrf_->i_half_circle_))
68  {
69  // Back-face culling: if the normal is pointing outwards, ommit this line
70  if (i_p1 > i_p2)
71  return;
72 
73  // Both points in the blind spot (i's are both larger number of beams), so don't render a line
74  if (i_min >= static_cast<int>(lrf_->num_beams_))
75  return;
76 
77  // The line is fully in view, so only need to render one part
78  i_min1 = static_cast<uint>(std::max<int>(0, i_min));
79  i_max1 = std::min<uint>(lrf_->num_beams_, static_cast<uint>(std::max<int>(0, i_max)));
80 
81  // No second part
82  i_min2 = 0;
83  i_max2 = 0;
84 
85  min_i = std::min<uint>(min_i, i_min1);
86  max_i = std::max<uint>(max_i, i_max1);
87  }
88  else
89  {
90  // Back-face culling: if the normal is pointing outwards, ommit this line
91  if (i_p2 > i_p1)
92  return;
93 
94  // We may need to draw two parts, because the line can be 'occluded' by the blind spot of the sensor
95  i_min1 = static_cast<uint>(std::max<int>(0, i_max));
96  i_max1 = lrf_->num_beams_;
97 
98  i_min2 = 0;
99  i_max2 = std::min<uint>(lrf_->num_beams_, static_cast<uint>(std::max<int>(0, i_min)));
100 
101  min_i = 0;
102  max_i = lrf_->num_beams_;
103  }
104 
105  // d = (q1 - ray_start) x s / (r x s)
106  // = (q1 x s) / (r x s)
107  Vec2& s = diff;
108 
109  // For all beam regions found above (1 or 2 regions), calculate the intersection
110  // of each beam with the line
111 
112  // Draw part 1
113  for(uint i = i_min1; i < i_max1; ++i)
114  {
115  const geo::Vec2& r = lrf_->ray_dirs_[i].projectTo2d();
116  double d = p1.cross(s) / r.cross(s);
117  if (d > 0)
118  renderPoint(i, d);
119  }
120 
121  // Draw part 2
122  for(uint i = i_min2; i < i_max2; ++i)
123  {
124  const geo::Vec2& r = lrf_->ray_dirs_[i].projectTo2d();
125  double d = p1.cross(s) / r.cross(s);
126  if (d > 0)
127  renderPoint(i, d);
128  }
129 }
130 
131 // ----------------------------------------------------------------------------------------------------
132 
134 {
135  if (ranges[i] == 0 || d < ranges[i]) {
136  ranges[i] = d;
137  }
138 }
139 
140 // ----------------------------------------------------------------------------------------------------
141 
143  res.min_i = 0;
144  res.max_i = ray_dirs_.size();
145 
146  if (res.ranges.size() != ray_dirs_.size()) {
147  res.ranges.resize(ray_dirs_.size(), 0);
148  }
149 
150  res.lrf_ = this;
151 
152  const geo::Pose3D& pose = opt.getPose();
153 
154  double max_radius = opt.getMesh().getMaxRadius();
155  if (max_radius > 0)
156  {
157  // If object is too far above or below the laser plane, do not render
158  if (std::abs(pose.getOrigin().getZ()) > max_radius)
159  return;
160 
161  double dist_sq = pose.getOrigin().length2();
162 
163  if (dist_sq > max_radius * max_radius) {
164  // If nearest object point is certainly further away than max_range, do not render
165  if (sqrt(dist_sq) - max_radius > range_max_)
166  return;
167  }
168  }
169 
170  const std::vector<TriangleI>& triangles = opt.getMesh().getTriangleIs();
171  const std::vector<Vector3>& points = opt.getMesh().getPoints();
172 
173  // transform Z-coordinates of all vertices
174  std::vector<double> zs_t(points.size());
175  Vector3 Rz = pose.getBasis().getRow(2);
176  double z_offset = pose.getOrigin().getZ();
177  for(uint i = 0; i < points.size(); ++i)
178  zs_t[i] = Rz.dot(points[i]) + z_offset;
179 
180  Vector3 Rx = pose.getBasis().getRow(0);
181  Vector3 Ry = pose.getBasis().getRow(1);
182 
183  // Iterate over all triangles
184  for(std::vector<TriangleI>::const_iterator it_tri = triangles.begin(); it_tri != triangles.end(); ++it_tri)
185  {
186  double z1 = zs_t[it_tri->i1_];
187  double z2 = zs_t[it_tri->i2_];
188  double z3 = zs_t[it_tri->i3_];
189 
190  bool p1_under_plane = z1 < 0;
191  bool p2_under_plane = z2 < 0;
192  bool p3_under_plane = z3 < 0;
193 
194  // Check if not all points of the triangle are on the same side of the plane
195  if (p1_under_plane != p2_under_plane || p2_under_plane != p3_under_plane)
196  {
197  // Transform the vertices to the sensor frame
198  Vec2 p1_3d(Rx.dot(points[it_tri->i1_]) + pose.t.x, Ry.dot(points[it_tri->i1_]) + pose.t.y);
199  Vec2 p2_3d(Rx.dot(points[it_tri->i2_]) + pose.t.x, Ry.dot(points[it_tri->i2_]) + pose.t.y);
200  Vec2 p3_3d(Rx.dot(points[it_tri->i3_]) + pose.t.x, Ry.dot(points[it_tri->i3_]) + pose.t.y);
201 
202  // Calculate the distances of the vertices to the plane
203  double z1_abs = std::abs(z1);
204  double z2_abs = std::abs(z2);
205  double z3_abs = std::abs(z3);
206 
207  // Calculate the intersections of the triangle edges with the plane,
208  // respecting the orientation of the triangle (normal is towards or away from sensor)
209  // such that later on we can do back-face culling.
210 
211  Vec2 q1, q2;
212  if (p2_under_plane == p3_under_plane) {
213  if (p2_under_plane)
214  {
215  q2 = (p1_3d * z2_abs + p2_3d * z1_abs) / (z1_abs + z2_abs);
216  q1 = (p1_3d * z3_abs + p3_3d * z1_abs) / (z1_abs + z3_abs);
217  }
218  else
219  {
220  q1 = (p1_3d * z2_abs + p2_3d * z1_abs) / (z1_abs + z2_abs);
221  q2 = (p1_3d * z3_abs + p3_3d * z1_abs) / (z1_abs + z3_abs);
222  }
223  } else if (p1_under_plane == p3_under_plane) {
224  if (p1_under_plane)
225  {
226  q1 = (p2_3d * z1_abs + p1_3d * z2_abs) / (z2_abs + z1_abs);
227  q2 = (p2_3d * z3_abs + p3_3d * z2_abs) / (z2_abs + z3_abs);
228  }
229  else
230  {
231  q1 = (p2_3d * z3_abs + p3_3d * z2_abs) / (z2_abs + z3_abs);
232  q2 = (p2_3d * z1_abs + p1_3d * z2_abs) / (z2_abs + z1_abs);
233  }
234  }
235 
236  if (p1_under_plane == p2_under_plane) {
237  if (p1_under_plane)
238  {
239  q1 = (p3_3d * z2_abs + p2_3d * z3_abs) / (z3_abs + z2_abs);
240  q2 = (p3_3d * z1_abs + p1_3d * z3_abs) / (z3_abs + z1_abs);
241  }
242  else
243  {
244  q1 = (p3_3d * z1_abs + p1_3d * z3_abs) / (z3_abs + z1_abs);
245  q2 = (p3_3d * z2_abs + p2_3d * z3_abs) / (z3_abs + z2_abs);
246  }
247  }
248 
249  // Render the line
250  res.renderLine(geo::Vec2(q1.x, q1.y), geo::Vec2(q2.x, q2.y));
251  }
252  }
253 
254 }
255 
256 // ----------------------------------------------------------------------------------------------------
257 
258 LaserRangeFinder::RenderResult LaserRangeFinder::render(const Shape& shape, const Pose3D& cam_pose, const Pose3D& obj_pose, std::vector<double>& ranges) const
259 {
260 
262  options.setMesh(shape.getMesh(), cam_pose.inverse() * obj_pose);
263 
264  LaserRangeFinder::RenderResult res(ranges);
265  render(options, res);
266 
267  return res;
268 }
269 
270 // ----------------------------------------------------------------------------------------------------
271 
272 void LaserRangeFinder::renderLine(const geo::Vec2& p1, const geo::Vec2& p2, std::vector<double>& ranges) const
273 {
274  LaserRangeFinder::RenderResult res(ranges);
275  res.lrf_ = this;
276  res.renderLine(p1, p2);
277 }
278 
279 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
280 //
281 // PARAMETERS
282 //
283 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
284 
285 void LaserRangeFinder::setAngleLimits(double min, double max) {
286  a_min_ = min;
287  a_max_ = max;
288  if (num_beams_ > 0 && a_max_ - a_min_ > 0) {
289  calculateRays();
290  }
291 }
292 
293 void LaserRangeFinder::setNumBeams(uint num_beams) {
294  num_beams_ = num_beams;
295  if (num_beams > 0 && a_max_ - a_min_ > 0) {
296  calculateRays();
297  }
298 }
299 
301  ray_dirs_.clear();
302  angles_.clear();
303  angle_incr_ = (a_max_ - a_min_) / std::max<uint>(num_beams_ - 1, 1);
304 
307 
308  // Pre-calculate the unit direction vectors of all the rays
309  double a = a_min_;
310  for(uint i = 0; i < num_beams_; ++i) {
311  ray_dirs_[i] = polarTo2D(a, 1);
312  angles_[i] = a;
313  a += angle_incr_;
314  }
315 
316  i_half_circle_ = M_PI / angle_incr_;
317 }
318 
320  return angle_incr_;
321 }
322 
323 uint LaserRangeFinder::getAngleUpperIndex(double angle) const {
324  return std::min<uint>(num_beams_, std::max<int>(0, getAngleUpperIndexRaw(angle)));
325 }
326 
327 uint LaserRangeFinder::getAngleUpperIndex(double x, double y) const {
328  // Calculate the ray index corresponding to the cartesian point (x, y)
329  return getAngleUpperIndex(atan2(y, x));
330 }
331 
333  return (angle - a_min_) / angle_incr_ + 1;
334 }
335 
336 int LaserRangeFinder::getAngleUpperIndexRaw(double x, double y) const {
337  // Calculate the ray index corresponding to the cartesian point (x, y)
338  return getAngleUpperIndexRaw(atan2(y, x));
339 }
340 
341 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
342 //
343 // CONVERSIONS
344 //
345 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
346 
347 geo::Vector3 LaserRangeFinder::rangeToPoint(double range, uint i) const {
348  return ray_dirs_[i] * range;
349 }
350 
352  return ray_dirs_[i];
353 }
354 
356  if (ranges.size() != ray_dirs_.size()) {
357  return false;
358  }
359  points.resize(ray_dirs_.size());
360  for(uint i = 0; i < ray_dirs_.size(); ++i) {
361  points[i] = ray_dirs_[i] * ranges[i];
362  }
363  return true;
364 }
365 
366 geo::Vector3 LaserRangeFinder::polarTo2D(double angle, double range) {
367  return geo::Vector3(cos(angle), sin(angle), 0) * range;
368 }
369 
370 geo::Vector3 LaserRangeFinder::polarTo3D(const geo::Pose3D& laser_pose, double angle, double range) {
371  return laser_pose.getBasis() * polarTo2D(angle, range);
372 }
373 
374 }
geo::LaserRangeFinder::polarTo3D
static geo::Vector3 polarTo3D(const geo::Pose3D &laser_pose, double angle, double range)
Definition: LaserRangeFinder.cpp:370
Shape.h
geo::Vector3
Vec3 Vector3
Definition: datatypes.h:32
geo::LaserRangeFinder::setAngleLimits
void setAngleLimits(double min, double max)
Definition: LaserRangeFinder.cpp:285
std::vector::resize
T resize(T... args)
geo::Transform3T::getBasis
const Mat3T< T > & getBasis() const
Definition: math_types.h:740
geo
Definition: Box.h:6
geo::Mesh::getTriangleIs
const std::vector< TriangleI > & getTriangleIs() const
Definition: Mesh.cpp:46
t
Timer t
a
void a()
geo::Vec2T::y
T y
Definition: math_types.h:114
std::vector
std::vector::size
T size(T... args)
geo::Transform3T::getOrigin
const Vec3T< T > & getOrigin() const
Definition: math_types.h:730
geo::Vec2T::x
T x
Definition: math_types.h:114
geo::LaserRangeFinder::RenderResult::min_i
uint min_i
Definition: LaserRangeFinder.h:46
geo::LaserRangeFinder::RenderOptions::getPose
const geo::Pose3D & getPose() const
Definition: LaserRangeFinder.h:26
geo::Transform3T
Definition: math_types.h:19
geo::Mesh::getMaxRadius
double getMaxRadius() const
Definition: Mesh.cpp:125
geo::Shape::getMesh
virtual const Mesh & getMesh() const
return the mesh defining the shape
Definition: Shape.cpp:425
cmath
geo::LaserRangeFinder::render
void render(const geo::LaserRangeFinder::RenderOptions &options, geo::LaserRangeFinder::RenderResult &res) const
Definition: LaserRangeFinder.cpp:142
geo::LaserRangeFinder::getRayDirection
const geo::Vector3 getRayDirection(uint i) const
Definition: LaserRangeFinder.cpp:351
std::vector::clear
T clear(T... args)
geo::LaserRangeFinder::polarTo2D
static geo::Vector3 polarTo2D(double angle, double range)
Definition: LaserRangeFinder.cpp:366
geo::LaserRangeFinder::LaserRangeFinder
LaserRangeFinder()
Definition: LaserRangeFinder.cpp:8
geo::LaserRangeFinder::i_half_circle_
uint i_half_circle_
Definition: LaserRangeFinder.h:132
geo::LaserRangeFinder::getAngleUpperIndexRaw
int getAngleUpperIndexRaw(double angle) const
Get the index of the first beam with a higher angle than angle. Results may lie outside of the sensor...
Definition: LaserRangeFinder.cpp:332
geo::Transform3T::inverse
Transform3T inverse() const
Definition: math_types.h:747
geo::LaserRangeFinder::angles_
std::vector< double > angles_
Definition: LaserRangeFinder.h:125
geo::Mesh::getPoints
const std::vector< geo::Vector3 > & getPoints() const
Definition: Mesh.cpp:42
geo::LaserRangeFinder::a_min_
double a_min_
Definition: LaserRangeFinder.h:119
geo::LaserRangeFinder::calculateRays
void calculateRays()
Definition: LaserRangeFinder.cpp:300
geo::LaserRangeFinder::RenderResult::renderLine
virtual void renderLine(const Vec2 &p1, const Vec2 &p2)
Definition: LaserRangeFinder.cpp:22
geo::Vec2T::cross
T cross(const Vec2T &v) const
returns cross product
Definition: math_types.h:60
geo::Vector3::dot
real dot(const Vector3 &v) const
Definition: matrix.h:56
geo::LaserRangeFinder::RenderResult::ranges
std::vector< double > & ranges
Definition: LaserRangeFinder.h:49
geo::Transform3T::t
Vec3T< T > t
Definition: math_types.h:794
geo::LaserRangeFinder::RenderResult::renderPoint
virtual void renderPoint(uint index, float depth)
Definition: LaserRangeFinder.cpp:133
geo::LaserRangeFinder::setNumBeams
void setNumBeams(uint n)
Definition: LaserRangeFinder.cpp:293
geo::LaserRangeFinder::RenderResult::lrf_
const geo::LaserRangeFinder * lrf_
Definition: LaserRangeFinder.h:51
geo::LaserRangeFinder::RenderOptions::setMesh
void setMesh(const geo::Mesh &mesh, const geo::Pose3D &pose)
Definition: LaserRangeFinder.h:21
geo::Vector3
Definition: matrix.h:12
geo::LaserRangeFinder::a_max_
double a_max_
Definition: LaserRangeFinder.h:119
geo::Vec2
Vec2T< real > Vec2
Definition: math_types.h:800
geo::LaserRangeFinder::rangeToPoint
geo::Vector3 rangeToPoint(double range, uint i) const
Definition: LaserRangeFinder.cpp:347
geo::LaserRangeFinder::ray_dirs_
std::vector< geo::Vector3 > ray_dirs_
Definition: LaserRangeFinder.h:127
geo::LaserRangeFinder::num_beams_
uint num_beams_
Definition: LaserRangeFinder.h:123
geo::LaserRangeFinder::getAngleIncrement
double getAngleIncrement() const
Angle increment between two beams.
Definition: LaserRangeFinder.cpp:319
geo::LaserRangeFinder::RenderResult::max_i
uint max_i
Definition: LaserRangeFinder.h:47
geo::LaserRangeFinder::~LaserRangeFinder
virtual ~LaserRangeFinder()
Definition: LaserRangeFinder.cpp:11
geo::Vec2T::length2
T length2() const
Returns the squared length of the vector.
Definition: math_types.h:81
std::vector::begin
T begin(T... args)
geo::LaserRangeFinder::RenderOptions
Definition: LaserRangeFinder.h:17
geo::LaserRangeFinder::getAngleUpperIndex
uint getAngleUpperIndex(double angle) const
Get the index of the first beam with a higher angle than this beam.
Definition: LaserRangeFinder.cpp:323
geo::LaserRangeFinder::renderLine
void renderLine(const geo::Vec2 &p1, const geo::Vec2 &p2, std::vector< double > &ranges) const
Definition: LaserRangeFinder.cpp:272
geo::LaserRangeFinder::angle_incr_
double angle_incr_
Definition: LaserRangeFinder.h:129
std::vector::end
T end(T... args)
LaserRangeFinder.h
geo::LaserRangeFinder::RenderOptions::getMesh
const geo::Mesh & getMesh() const
Definition: LaserRangeFinder.h:28
geo::LaserRangeFinder::rangesToPoints
bool rangesToPoints(const std::vector< double > &ranges, std::vector< geo::Vector3 > &points) const
Definition: LaserRangeFinder.cpp:355
geo::LaserRangeFinder::RenderResult
Definition: LaserRangeFinder.h:36
geo::Vec2T
Definition: math_types.h:24
geo::Shape
A geometric description of a shape.
Definition: Shape.h:19
geo::LaserRangeFinder::range_max_
double range_max_
Definition: LaserRangeFinder.h:121