激光点云去畸变_原理及实现

激光点云去畸变:原理及实现

机械式激光雷达产生畸变的原因

Lidar扫描周期内(一般0.1s)自车有一定幅度的旋转(Rotation)和平移(Translation),因此不同时间点打出去的激光点束并不在严格统一的Lidar坐标系内,需要对同一帧Lidar转化在统一时间戳对应的Lidar坐标系
上(一般转化到第一束激光点对应的自车坐标系上,或者扫描周期的中心时刻)。

image

附赠自动驾驶最全的学习资料和量产经验:链接

输入的传感器和参数

传感器主要使用IMU和INS, 两者分别提供的参数和频率如下

IMU 惯性测量单元 : 提供自车xyz加速度和角速度 /100HZ

GPS/INS 惯导测量系统 : 提供自车分别相对于全局坐标系的欧拉角 /100HZ

两个message结构如下:

IMU Message

# Header
std_msgs/Header header

# Sensor status
string status
uint8 quality
# Sensor quality enum
uint8 INVALID=0
uint8 DOWNGRADE=1
uint8 GOOD=2

# Linear acceleration in vehicle reference frame (m/s^2)
# Y for forward, X for right, Z for up
geometry_msgs/Vector3 accel

# Angular velocity in vehicle reference frame (deg/s)
# Y across forward axis, X across right axis, Z across up axis
geometry_msgs/Vector3 gyro

INS Message

# Header
std_msgs/Header header

# Sensor status
string status
uint8 quality
# Sensor quality enum
uint8 INVALID=0
uint8 DOWNGRADE=1
uint8 GOOD=2

# Position (WGS84)
float64 longitude
float64 latitude
float64 altitude

# Position standard deviation in degrees
float64 longitude_std_dev
float64 latitude_std_dev
float64 altitude_std_dev

# Velocity (m/s)
# X for east, Y for north, Z for up
geometry_msgs/Vector3 velocity

# Velocity standard deviation in meter per second
geometry_msgs/Vector3 velocity_std_dev

# Attitude (degrees)
# Right-handed rotation from local level around y-axis
float64 roll
# Right-handed rotation from local level around x-axis
float64 pitch
# Right-handed rotation from local level around z-axis
float64 yaw

# Attitude standard deviation in degrees
float64 roll_std_dev
float64 pitch_std_dev
float64 yaw_std_dev

其中ins提供xyz方向的速度, 但是考虑到ins与imu位置并不严格一致,这里没有直接使用ins提供的速度,而是使用imu提供加速度积分求的速度。

于是从ins中获取imu坐标系的三个方向的欧拉角(x-roll, y-pitch, z-yaw, 简称rpy角), 从imu传感器获取imu在xyz三个方向的加速度和角速度。

注意: 这里的roll-pitch-yaw 严格相对于imu坐标系的三个方向xyz而言,imu坐标系的方向与我们的习惯方向不一致(见下图), 因此’roll’和’pitch’并不符合我们习惯的’翻滚’和’点头’的直觉,而是刚好反过来,但这并不影响放射变换的计算。

image

在纠正之前需要作如下步骤:

  1. Lidar→Imu : 首先,点云数据一定是基于Lidar坐标系(见下图),而传感器是从imu坐标系获取的,因此需要首先根据给定的标定矩阵将点云投射到imu坐标系上。

  2. Imu→ Global : Imu坐标系和Lidar坐标系一样都是随车体运动的,因此需要根据传感器数据,计算imu坐标系(一般将imu坐标系看作自车坐标系)本身相对于全局坐标系的位姿。

  3. 数值计算 : 根据ins传感器提供的rpy三个欧拉角,获取imu坐标系的欧拉旋转矩阵; 根据Imu传感器获取加速度(acc),注意这个加速度是imu坐标系上xyz方向的加速度,需要使用第二步得到的旋转矩阵计算_imu坐标系本身_ 相对于全局坐标系的加速度。

  4. 积分: 根据3获取的acc, 分别作一次积分和二次积分获取前后imu数据注册时间段内imu坐标系相对于全局坐标系的位移。

欧拉角→ 旋转矩阵 :

image

对应代码摘抄如下:

Eigen::AngleAxisf t_Vz(rot_xyz(2)/180 * pi_, Eigen::Vector3f::UnitZ());
Eigen::AngleAxisf t_Vy(rot_xyz(1)/180 * pi_, Eigen::Vector3f::UnitY());
Eigen::AngleAxisf t_Vx(rot_xyz(0)/180 * pi_, Eigen::Vector3f::UnitX());
Eigen::AngleAxisf t_V;
t_V = t_Vz * t_Vy * t_Vx;
Eigen::Matrix3f rot = t_V.matrix();
acc = rot * acc;
acc.z() -= 9.80665 ;

注意imu在全局坐标系下z方向总是有向上加速度为g的速度分量(因为重力的原因),但这个acc并没有贡献任何位移,因此需要减去这个重力加速度。

acc→ 全局位移 :

imu_shift_x_[imu_ptr_last_] =
imu_shift_x_[imu_ptr_back] +imu_velo_x_[imu_ptr_back] * time_diff + acc(0) * time_diff * time_diff * 0.5;
imu_shift_y_[imu_ptr_last_] =
imu_shift_y_[imu_ptr_back] + imu_velo_y_[imu_ptr_back] * time_diff + acc(1) * time_diff * time_diff * 0.5;
imu_shift_z_[imu_ptr_last_] =
imu_shift_z_[imu_ptr_back] + imu_velo_z_[imu_ptr_back] * time_diff + acc(2) * time_diff * time_diff * 0.5;

imu_velo_x_[imu_ptr_last_] = imu_velo_x_[imu_ptr_back] + acc(0) * time_diff;
imu_velo_y_[imu_ptr_last_] = imu_velo_y_[imu_ptr_back] + acc(1) * time_diff;
imu_velo_z_[imu_ptr_last_] = imu_velo_z_[imu_ptr_back] + acc(2) * time_diff;

去畸变与运动补偿

时间同步与插值运算 : 对于点云中任意一点(自带时间戳),遍历队列中imu数据集合,直到当前点时间戳处于两个Imu时间戳之间,根据线性插值法计算当前点对应的imu位姿:

int imu_ptr_back = (imu_ptr_cur_ - 1 + imu_que_length_) % imu_que_length_;
float ratio_front_a = (scan_time + rel_time - imu_time_[imu_ptr_back]) * 1.0 ;
float ratio_front_b = (imu_time_[imu_ptr_cur_] - imu_time_[imu_ptr_back]) * 1.0;
float ratio_front = ratio_front_a / ratio_front_b;

float ratio_back = 1.0 - ratio_front;
rpy_cur(0) = imu_roll_[imu_ptr_cur_] * ratio_front + imu_roll_[imu_ptr_back] * ratio_back;
rpy_cur(1) = imu_pitch_[imu_ptr_cur_] * ratio_front + imu_pitch_[imu_ptr_back] * ratio_back;
rpy_cur(2) = imu_yaw_[imu_ptr_cur_] * ratio_front + imu_yaw_[imu_ptr_back] * ratio_back;

shift_cur(0) = imu_shift_x_[imu_ptr_cur_] * ratio_front + imu_shift_x_[imu_ptr_back] * ratio_back;
shift_cur(1) = imu_shift_y_[imu_ptr_cur_] * ratio_front + imu_shift_y_[imu_ptr_back] * ratio_back;
shift_cur(2) = imu_shift_z_[imu_ptr_cur_] * ratio_front + imu_shift_z_[imu_ptr_back] * ratio_back;

根据当前欧拉角计算旋转矩阵:

r_c = (
Eigen::AngleAxisf(rpy_cur(2) / 180.0 * pi_, Eigen::Vector3f::UnitZ()) *
Eigen::AngleAxisf(rpy_cur(1) / 180.0 * pi_, Eigen::Vector3f::UnitY()) *
Eigen::AngleAxisf(rpy_cur(0) / 180.0 * pi_, Eigen::Vector3f::UnitX())
).toRotationMatrix();

注意第一个点默认为最早打出去的一个点,我们将其他点转移到初始坐标系中,初始点状态记录为rt_start:

rpy_start = rpy_cur;
shift_start = shift_cur;
velo_start = velo_cur;
rt_start.translate(shift_start);
rt_start.rotate(r_c);

根据上述计算得到的当前点状态,计算当前点在全局坐标系下的状态(imu i->global i),然后再根据初始点状态,反向计算当前点全局状态相对于初始点的状态(global i->imu0):

adjusted_p = rt_start.inverse() * (r_c * Eigen::Vector3f(p.x, p.y, p.z) + shift_cur );
p.x = adjusted_p.x();
p.y = adjusted_p.y();
p.z = adjusted_p.z();

image

坐标转换——纠正之后

最后需要将纠正之后的点云统一转移到Lidar坐标系上,至此,点云去畸变流程计算完毕。

pcl::transformPointCloud(*point_cloud_ptr, *point_cloud_ptr, TransInsLidar.inverse());

纠正效果如下:

将相同id两帧点云投射到通一张图上,红色代表去畸变之前的点,蓝色代表去畸变之后的点,黄色箭头代表lidar旋转的方向,

黑箭头代表第一束和最后一束激光点重合的方向,这个方向积累了0.1秒的畸变,所以断层会沿着这个方向出现。

加速的场景如下(黄线代表lidar转动方向,顺时针):

image

旋转的场景如下(lidar顺时针旋转的时候,原点云会应对与lidar做逆时针的摆动):

image

路崖子断层 :

image

image

运动中的目标车辆断层:

image

原目标车辆(带强度)

image

去畸变之后的效果(带强度):

image

墙体的去畸变效果

image

image

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值