0%

LiDAR 初探

LiDAR: Light Detection and Ranging

Radar: Radio Detection and Ranging

Lidar工作原理

LiDAR physics principles

两组关键量:

  1. 激光的方向
  2. 测得的距离

测量与计算方式

  1. 飞行时间法(TOF, Pulsed Time of Flight)

    • 单个光脉冲

    img

  2. 调幅连续波(AMCW, amplitude modulated continuous waveform laser )

    • 连续光波,检测相位差

    • 可认为属于TOF

    img

  3. 调频连续波(FMCW, frequency modulated continuous wave)

    img

  • 发射一束连续的光束,频率随时间稳定地发生变化。由于源光束的频率在不断变化,光束传输距离的差异会导致频率的差异,将回波信号与本振信号混频并经低通滤波后,得到的差频信号是光束往返时间的函数。
  • 通过频率差值算出与物体的距离
  • 通过多普勒效应算出物体的速度
  • 毫米波雷达的主要原理

Lidar功能特点

优势

  1. 数据采集速度快且准确度高
  2. 得益于主动照明传感器,LiDAR 技术可以在白天或黑暗中使用
  3. 可用于收集有关无法到达的地方的数据
  4. 便于收集大片地形数据(机载雷达)

劣势

  1. 如果环境有雾或烟雾或透明障碍物,如大雨、低空云层,可能会干扰雷达的使用
  2. 分析收集的大量数据可能会消耗时间和资源
  3. 某些激光雷达使用的强大激光束可能会损害人眼
  4. 难以穿透高密度物质

Lidar类别

结构

机械式

Velodyne Says It's Got a "Breakthrough" in Solid State Lidar Design - IEEE  Spectrum

机械式Lidar

混合固态式(较主流)
  1. MEMS(Microelectromechanical Systems,微机电系统)

img

  1. 二维转镜

    1. 二维扫描

img

  1. 一维扫描

img

固态式(未来发展)
  1. 相控阵(OPA)

    在相控阵中,来自发射器的功率通过称为移相器的设备馈送到辐射元件,该设备由计算机系统控制,可以电子方式改变相位或信号延迟,从而将无线电波束转向不同的方向。

    img

    一束光经过光分束器分为多路光信号。在各路光信号附加相位差之后(以各路光信号赋予均匀的相位差为例,第二个波导与第一个波导的相位差为ΔϕB,第三个波导与第一个波导的相位差为2ΔϕB,以此类推),此时的等相位面不再垂直于波导方向,而是有了一定的偏转,满足等相位关系的波束会相干相长,不满足等相位条件的光束就会相互抵消,故光束的指向总是垂直于等相位面。

img

  1. 泛光面阵(FLASH)

    Flash 激光雷达在短时间直接发射出一大片覆盖探测区域的激光,再以高度灵敏的接收器,来完成对环境周围图像的绘制。

img

维度

  • 1D
img
  • 2D

    一道旋转激光

img
  • 3D

    多道旋转激光

img

使用场景

  • 室内
  • 室外
    • 阳光产生的干扰
    • 雨雪对透光度的影响
    • 室外温度影响

Lidar参数

波长

常见车载雷达使用波长:

  • 905nm

    • 器件相对成熟,成本较低
    • 考虑到人眼安全要求,激光功率受到明显限制
    • 太阳光中存在较多近红外背景光,传感器信噪比物理上受限,最大探测距离限制在150米左右
    • 穿透力更强,能够应对复杂环境情况
  • 1550nm

    • 同等功率的1550 nm激光人眼安全性提高40倍
    • 背景光干扰问题相对较小,可以实现远距离探测
    • 探测器只对自身发射的激光回波响应,信噪比远高于905nm-ToF激光雷达,最大探测距离可以达到1000米以上
    • 1550 nm配合调频连续波(FMCW)的技术,不仅可以检测距离,同时可以利用多普勒频移来测量物体的速度

    img

测距能力

在车载lidar的表征上经常会通过10%反射率板标注最远测距距离,表达形式如XXm@10% reflectivity的测距能力。测距距离越长表明在系统中接触到物体的时间越早,留给系统判断和决策的时间越长

反射率(reflectivity),是材料表面反射辐射能的有效性。它是在边界处反射电磁功率与入射电磁功率的比值。

勒克斯(Lux,通常简写为lx)是一个标识照度的国际单位制单位,1流明每平方米面积,就是1勒克斯。

测距精度

测距精度主要表征lidar测量物体时数据的一致性。精度越高表示误差越小,每一次获取到的数值更为一致。

例如:<2cm (1σ@20m)即表示在20m测量位置,在1σ(标准差)的概率下,每次测量的误差值小于2cm。

视场角(FOV, Field of View)

分为水平视场角,垂直视场角。表示lidar前方能扫描到的区域。视场角越大则扫描范围越大。

角分辨率

两点之间的角度间隔(水平与竖直)。间隔越小对物体细节表现越细致。若有开发ROI功能,角分辨率会更小。

帧率

lidar每秒传递的帧数,每一帧都是完整的一轮扫描结果,以Hz为单位,通常为5、10、20Hz

点频

lidar扫描1s的总探测点数(点/秒)

$单帧点数 * 帧率数值 = 点频$

盲区

考虑雷达盒体内的光束探测反馈(玻璃片前导信号及其他反馈信号)可能会使得近距离的探测精度大幅度下降使得数据没有意义,所以会存在直接舍去近距离点云的部分数据,这部分会被划定为盲区。

  • 补盲雷达:专门负责检测近处物体

[2] 若目标物体距离 0.1 m 至 0.2 m,激光探测测距仪可探测并输出点云数据,但由于无法保证探测精度,此数据仅供参考。

人眼安全级别(Laser class)

几乎所有激光雷达扫描仪都属于激光等级1,这是最安全的级别。它表示扫描仪是完全无害的,你可以在眼睛没有任何保护措施的情况下直视它,而不会受到任何负面影响。

防护等级

IP等级的第一位数字代表其抗尘性,第二个数字是其抗液性 (IP67)

功率

雷达发热情况

IMU

使用IMU更新Lidar的朝向

Tinker使用的Lidar——Livox Mid 360

业界首款 360° 混合固态激光雷达

¥3999

觅道  Mid-360

image-20230924124405603

image-20230921181441600

LiDAR数据处理

PointCloud

sensor_msgs/PointCloud

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# This message holds a collection of 3d points, plus optional additional
# information about each point.

# Time of sensor data acquisition, coordinate frame ID.
Header header

# Array of 3d points. Each Point32 should be interpreted as a 3d point
# in the frame given in the header.
geometry_msgs/Point32[] points

# Each channel should have the same number of elements as points array,
# and the data in each channel should correspond 1:1 with each point.
# Channel names in common practice are listed in ChannelFloat32.msg.
ChannelFloat32[] channels

sensor_msgs/ChannelFloat32

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# This message is used by the PointCloud message to hold optional data
# associated with each point in the cloud. The length of the values
# array should be the same as the length of the points array in the
# PointCloud, and each value should be associated with the corresponding
# point.

# Channel names in existing practice include:
# "u", "v" - row and column (respectively) in the left stereo image.
# This is opposite to usual conventions but remains for
# historical reasons. The newer PointCloud2 message has no
# such problem.
# "rgb" - For point clouds produced by color stereo cameras. uint8
# (R,G,B) values packed into the least significant 24 bits,
# in order.
# "intensity" - laser or pixel intensity.
# "distance"

# The channel name should give semantics of the channel (e.g.
# "intensity" instead of "value").
string name

# The values array should be 1-1 with the elements of the associated
# PointCloud.
float32[] values

典型应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//将数据通过消息发送出去
sensor_msgs::PointCloud cloud;
cloud.header.stamp = ros::Time::now();
cloud.header.frame_id = "sensor_frame";//帧id
cloud.points.resize(LINE*CIRCLEPT);
cloud.channels.resize(2);//设置增加通道数
cloud.channels[0].name = "intensity";//增加反射强度信道,并设置其大小,使与点云数量相匹配
cloud.channels[0].values.resize(LINE*CIRCLEPT);
cloud.channels[1].name = "distance";//增加距离信道,并设置其大小,使与点云数量相匹配
cloud.channels[1].values.resize(LINE*CIRCLEPT);
int i=0;
for (int l = 0; l < LINE; l++)
for (int c = 0; c < CIRCLEPT; c++)
{
cloud.points[i].x = mdecoder.mpointcloud[l][c].x;
cloud.points[i].y = mdecoder.mpointcloud[l][c].y;
cloud.points[i].z = mdecoder.mpointcloud[l][c].z;
cloud.channels[0].values[i] = mdecoder.mpointcloud[l][c].r;//设置反射强度
cloud.channels[1].values[i] = mdecoder.mpointcloud[l][c].d;
i++;
}
cloud_pub.publish(cloud);

PointCloud2

sensor_msgs/PointCloud2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# This message holds a collection of N-dimensional points, which may
# contain additional information such as normals, intensity, etc. The
# point data is stored as a binary blob, its layout described by the
# contents of the "fields" array.

# The point cloud data may be organized 2d (image-like) or 1d
# (unordered). Point clouds organized as 2d images may be produced by
# camera depth sensors such as stereo or time-of-flight.

# Time of sensor data acquisition, and the coordinate frame ID (for 3d
# points).
Header header

# 2D structure of the point cloud. If the cloud is unordered, height is
# 1 and width is the length of the point cloud.
uint32 height # height != 1: 有组织的二维点云,通过了解相邻点(例如像素)之间的关系,最近邻操作更加高效
uint32 width # point的行数,若height = 1则width为所有点的数量

# Describes the channels and their layout in the binary data blob.
PointField[] fields

bool is_bigendian # Is this data bigendian?
uint32 point_step # Length of a point in bytes
uint32 row_step # Length of a row in bytes
# row_step = width * point_step 存所有数据(一个数组)的字节
uint8[] data # Actual point data, size is (row_step*height)

bool is_dense # True if there are no invalid points

big endian & small endian

img

sensor_msgs/PointField

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# This message holds the description of one point entry in the
# PointCloud2 message format.
uint8 INT8 = 1
uint8 UINT8 = 2
uint8 INT16 = 3
uint8 UINT16 = 4
uint8 INT32 = 5
uint8 UINT32 = 6
uint8 FLOAT32 = 7
uint8 FLOAT64 = 8

string name # Name of field
uint32 offset # Offset from start of point struct
uint8 datatype # Datatype enumeration, see above
uint32 count # How many elements in the field

典型应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
header: 
seq: 1071
stamp:
secs: 1521699326
nsecs: 676390000
frame_id: "velodyne"
height: 1
width: 66811
fields:
-
name: "x"
offset: 0
datatype: 7
count: 1
-
name: "y"
offset: 4
datatype: 7
count: 1
-
name: "z"
offset: 8
datatype: 7
count: 1
-
name: "intensity"
offset: 16
datatype: 7
count: 1
-
name: "ring"
offset: 20
datatype: 4
count: 1
is_bigendian: False
point_step: 32
// FLOAT32(4) + FLOAT32(4) + FLOAT32(4) + FLOAT32(4) + UINT16(2) + EMPTY(10) = 20
row_step: 2137952 // = 66811 * 32
data: [235, 171, 54, 190, 53, 107, 250, ...

offset:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ~31
x x x x y y y y z z z z - - - - i i i i r r r r -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <sensor_msgs/PointCloud2.h>
#include <sensor_msgs/PointField.h>
#include <sensor_msgs/point_cloud2_iterator.h>

sensor_msgs::PointCloud2 pcl_msg;

//Modifier to describe what the fields are.
sensor_msgs::PointCloud2Modifier modifier(pcl_msg);

modifier.setPointCloud2Fields(4,
"x", 1, sensor_msgs::PointField::FLOAT32,
"y", 1, sensor_msgs::PointField::FLOAT32,
"z", 1, sensor_msgs::PointField::FLOAT32,
"intensity", 1, sensor_msgs::PointField::FLOAT32);

//Msg header
pcl_msg.header = std_msgs::Header();
pcl_msg.header.stamp = ros::Time::now();
pcl_msg.header.frame_id = "frame";

pcl_msg.height = height;
pcl_msg.width = width;
pcl_msg.is_dense = true;

//Total number of bytes per point
pcl_msg.point_step = 16;
pcl_msg.row_step = pcl_msg.point_step * pcl_msg.width;
pcl_msg.data.resize(pcl_msg.row_step);

//Iterators for PointCloud msg
sensor_msgs::PointCloud2Iterator<float> iterX(pcl_msg, "x");
sensor_msgs::PointCloud2Iterator<float> iterY(pcl_msg, "y");
sensor_msgs::PointCloud2Iterator<float> iterZ(pcl_msg, "z");
sensor_msgs::PointCloud2Iterator<float> iterIntensity(pcl_msg, "intensity");

//iterate over the message and populate the fields.
for (int i = 0; i < width; i++)
{
*iterX = //Your x data
*iterY = //Your y data
*iterZ = //Your z data
*iterIntensity = //Your intensity data

// Increment the iterators
++iterX;
++iterY;
++iterZ;
++iterIntensity;
}