Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
MAAIQIANG123
gpt-vue_01
提交
d4080bba
G
gpt-vue_01
项目概览
MAAIQIANG123
/
gpt-vue_01
与 Fork 源项目一致
Fork自
inscode / ChatGPT Template With Vue
通知
1
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
G
gpt-vue_01
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
d4080bba
编写于
5月 20, 2023
作者:
6
6467a2983a260c3da56c82e1
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
UPDATE
上级
3daf8636
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
1587 addition
and
0 deletion
+1587
-0
imu.cpp
imu.cpp
+1104
-0
imu222.cpp
imu222.cpp
+483
-0
未找到文件。
imu.cpp
0 → 100644
浏览文件 @
d4080bba
#include "preprocess.h"
#define RETURN0 0x00
#define RETURN0AND1 0x10
Preprocess
::
Preprocess
()
:
feature_enabled
(
0
),
lidar_type
(
AVIA
),
blind
(
0.01
),
point_filter_num
(
1
)
{
inf_bound
=
10
;
N_SCANS
=
6
;
group_size
=
8
;
disA
=
0.01
;
disA
=
0.1
;
// B?
p2l_ratio
=
225
;
limit_maxmid
=
6.25
;
limit_midmin
=
6.25
;
limit_maxmin
=
3.24
;
jump_up_limit
=
170.0
;
jump_down_limit
=
8.0
;
cos160
=
160.0
;
edgea
=
2
;
edgeb
=
0.1
;
smallp_intersect
=
172.5
;
smallp_ratio
=
1.2
;
given_offset_time
=
false
;
jump_up_limit
=
cos
(
jump_up_limit
/
180
*
M_PI
);
jump_down_limit
=
cos
(
jump_down_limit
/
180
*
M_PI
);
cos160
=
cos
(
cos160
/
180
*
M_PI
);
smallp_intersect
=
cos
(
smallp_intersect
/
180
*
M_PI
);
}
//这段代码定义了Preprocess类的构造函数Preprocess(),该函数初始化了类的各个成员变量。其中:
// feature_enabled:是否启用特征提取,默认为0。
// lidar_type:激光雷达类型,默认为AVIA。
// blind:盲区大小,默认为0.01。
// point_filter_num:点云滤波数量,默认为1。
// inf_bound:最大边界值,默认为10。
//N_SCANS:激光雷达扫描线数量,默认为6。
// group_size:一组点云包含的点数,默认为8。
// disA:两组激光束之间的距离,默认为0.01。
// p2l_ratio:点到线距离与点到点距离的比值,默认为225。
// limit_maxmid:最大中间距离,默认为6.25。
// limit_midmin:中间距离和最小距离的比值,默认为6.25。
// limit_maxmin:最大距离和最小距离的比值,默认为3.24。
// jump_up_limit:上跳阈值,默认为170.0度。
// jump_down_limit:下跳阈值,默认为8.0度。
// cos160:cos(160.0度)的值,默认为0.94。
// edgea:边缘判断参数A,默认为2。
// edgeb:边缘判断参数B,默认为0.1。
//smallp_intersect:小角度交叉判断参数,默认为cos(172.5度)。
// smallp_ratio:小角度交叉判断参数,默认为1.2。
//given_offset_time:是否给定位移时间,默认为false。
//接下来,代码对 jump_up_limit、jump_down_limit、cos160和smallp_intersect 进行了一些计算,将角度转换为弧度并计算其余弦值,以便于后续使用。
//总体来说,这段代码主要是对类的成员变量进行初始化,并进行了少量的计算和赋值//
// Preprocess类的析构函数
Preprocess
::~
Preprocess
()
{}
// 设置预处理参数的函数
void
Preprocess
::
set
(
bool
feat_en
,
int
lid_type
,
double
bld
,
int
pfilt_num
)
{
feature_enabled
=
feat_en
;
//启用/禁用特征提取
lidar_type
=
lid_type
;
//使用的LiDAR传感器类型
blind
=
bld
;
//删除距离超出一定范围之外的点的距离阈值
point_filter_num
=
pfilt_num
;
//单次过滤需要过滤的点数
}
// 处理LiDAR点云数据的函数
void
Preprocess
::
process
(
const
livox_ros_driver
::
CustomMsg
::
ConstPtr
&
msg
,
PointCloudXYZI
::
Ptr
&
pcl_out
)
{
avia_handler
(
msg
);
//将Avia滤波器应用于LiDAR点云数据
*
pcl_out
=
pl_surf
;
//将过滤后的点云赋值给输出点云
}
void
Preprocess
::
process
(
const
sensor_msgs
::
PointCloud2
::
ConstPtr
&
msg
,
PointCloudXYZI
::
Ptr
&
pcl_out
)
{
switch
(
lidar_type
)
// 根据激光雷达类型选择处理函数
{
case
OUST64
:
oust64_handler
(
msg
);
// 处理 Ouster OS1-64 的点云数据
break
;
case
VELO16
:
velodyne_handler
(
msg
);
// 处理 Velodyne VLP-16 的点云数据
break
;
default:
printf
(
"Error LiDAR Type"
);
// 如果输入的激光雷达类型无法识别,则打印错误信息
break
;
}
*
pcl_out
=
pl_surf
;
// 将处理后的点云数据赋值给输出参数
}
void
Preprocess
::
avia_handler
(
const
livox_ros_driver
::
CustomMsg
::
ConstPtr
&
msg
)
{
pl_surf
.
clear
();
//清空表面点云
pl_corn
.
clear
();
//清空角点点云
pl_full
.
clear
();
//清空完整点云
double
t1
=
omp_get_wtime
();
//获取当前时间
int
plsize
=
msg
->
point_num
;
//获取点云数据数量
pl_corn
.
reserve
(
plsize
);
//预留角点点云空间
pl_surf
.
reserve
(
plsize
);
//预留表面点云空间
pl_full
.
resize
(
plsize
);
//设置完整点云大小为点云数据数量
for
(
int
i
=
0
;
i
<
N_SCANS
;
i
++
)
//循环遍历激光雷达扫描数次
{
pl_buff
[
i
].
clear
();
//清空每次扫描的点云缓存
pl_buff
[
i
].
reserve
(
plsize
);
//预留每次扫描点云缓存的空间
}
uint
valid_num
=
0
;
//有效点云数量初始化为0
if
(
feature_enabled
)
//判断特征是否可用
{
for
(
uint
i
=
1
;
i
<
plsize
;
i
++
)
//循环遍历点云数据
{
if
((
msg
->
points
[
i
].
line
<
N_SCANS
)
&&
((
msg
->
points
[
i
].
tag
&
0x30
)
==
0x10
||
(
msg
->
points
[
i
].
tag
&
0x30
)
==
0x00
))
//判断点的线数与点类型是否符合条件
{
pl_full
[
i
].
x
=
msg
->
points
[
i
].
x
;
//获取点的x坐标
pl_full
[
i
].
y
=
msg
->
points
[
i
].
y
;
//获取点的y坐标
pl_full
[
i
].
z
=
msg
->
points
[
i
].
z
;
//获取点的z坐标
pl_full
[
i
].
intensity
=
msg
->
points
[
i
].
reflectivity
;
//获取点的反射强度
pl_full
[
i
].
curvature
=
msg
->
points
[
i
].
offset_time
/
float
(
1000000
);
//获取点的偏移时间并将其作为曲率值
bool
is_new
=
false
;
//初始化点是否为新的变量为false
if
((
abs
(
pl_full
[
i
].
x
-
pl_full
[
i
-
1
].
x
)
>
1e-7
)
//判断点的x,y,z坐标与上一个点是否相同
||
(
abs
(
pl_full
[
i
].
y
-
pl_full
[
i
-
1
].
y
)
>
1e-7
)
||
(
abs
(
pl_full
[
i
].
z
-
pl_full
[
i
-
1
].
z
)
>
1e-7
))
{
pl_buff
[
msg
->
points
[
i
].
line
].
push_back
(
pl_full
[
i
]);
//将点加入点云缓存
}
}
}
// 静态变量,记录运行次数和时间
static
int
count
=
0
;
static
double
time
=
0.0
;
count
++
;
// 获取当前时间
double
t0
=
omp_get_wtime
();
// 循环进行点云特征提取
for
(
int
j
=
0
;
j
<
N_SCANS
;
j
++
)
{
// 如果点云数量小于等于5,则跳过本次循环
if
(
pl_buff
[
j
].
size
()
<=
5
)
continue
;
// 获取当前循环中的点云和类型
pcl
::
PointCloud
<
PointType
>
&
pl
=
pl_buff
[
j
];
plsize
=
pl
.
size
();
vector
<
orgtype
>
&
types
=
typess
[
j
];
types
.
clear
();
types
.
resize
(
plsize
);
plsize
--
;
// 对于点云中每个点分别计算它的range和dista
for
(
uint
i
=
0
;
i
<
plsize
;
i
++
)
{
types
[
i
].
range
=
pl
[
i
].
x
*
pl
[
i
].
x
+
pl
[
i
].
y
*
pl
[
i
].
y
;
vx
=
pl
[
i
].
x
-
pl
[
i
+
1
].
x
;
vy
=
pl
[
i
].
y
-
pl
[
i
+
1
].
y
;
vz
=
pl
[
i
].
z
-
pl
[
i
+
1
].
z
;
types
[
i
].
dista
=
vx
*
vx
+
vy
*
vy
+
vz
*
vz
;
}
// 计算最后一个点的range,并进行特征提取
types
[
plsize
].
range
=
pl
[
plsize
].
x
*
pl
[
plsize
].
x
+
pl
[
plsize
].
y
*
pl
[
plsize
].
y
;
give_feature
(
pl
,
types
);
// pl_surf = pl;
}
// 计算特征提取的时间并打印出来
time
=
omp_get_wtime
()
-
t0
;
printf
(
"Feature extraction time: %lf
\n
"
,
time
/
count
);
// 如果不需要进行点云特征提取,则将点云中的有效点提取到pl_surf点云序列中
else
{
// 遍历所有点,判断是否为有效点
for
(
uint
i
=
1
;
i
<
plsize
;
i
++
)
{
if
((
msg
->
points
[
i
].
line
<
N_SCANS
)
&&
((
msg
->
points
[
i
].
tag
&
0x30
)
==
0x10
||
(
msg
->
points
[
i
].
tag
&
0x30
)
==
0x00
))
{
valid_num
++
;
// 如果满足筛选条件,则记录为有效点
if
(
valid_num
%
point_filter_num
==
0
)
{
pl_full
[
i
].
x
=
msg
->
points
[
i
].
x
;
pl_full
[
i
].
y
=
msg
->
points
[
i
].
y
;
pl_full
[
i
].
z
=
msg
->
points
[
i
].
z
;
pl_full
[
i
].
intensity
=
msg
->
points
[
i
].
reflectivity
;
pl_full
[
i
].
curvature
=
msg
->
points
[
i
].
offset_time
/
float
(
1000000
);
// use curvature as time of each laser points
// 判断是否为遮挡区域,如果不是则记录在pl_surf点云序列中
if
((
abs
(
pl_full
[
i
].
x
-
pl_full
[
i
-
1
].
x
)
>
1e-7
)
||
(
abs
(
pl_full
[
i
].
y
-
pl_full
[
i
-
1
].
y
)
>
1e-7
)
||
(
abs
(
pl_full
[
i
].
z
-
pl_full
[
i
-
1
].
z
)
>
1e-7
)
&&
(
pl_full
[
i
].
x
*
pl_full
[
i
].
x
+
pl_full
[
i
].
y
*
pl_full
[
i
].
y
>
blind
))
{
pl_surf
.
push_back
(
pl_full
[
i
]);
}
}
}
}
}
void
Preprocess
::
oust64_handler
(
const
sensor_msgs
::
PointCloud2
::
ConstPtr
&
msg
)
{
pl_surf
.
clear
();
// 清表面点云
pl_corn
.
clear
();
// 清除角点点云
pl_full
.
clear
();
// 清除完整点云
pcl
::
PointCloud
<
ouster_ros
::
Point
>
pl_orig
;
// 定义 ouster_ros::Point 类型的点云
pcl
::
fromROSMsg
(
*
msg
,
pl_orig
);
// 将 ROS 消息转换成 PCL 点云格式
int
plsize
=
pl_orig
.
size
();
// 获取点云的数量
pl_corn
.
reserve
(
plsize
);
// 为角点点云预留空间
pl_surf
.
reserve
(
plsize
);
// 为表面点云预留空间
if
(
feature_enabled
)
// 如果开启了特征点提取
{
for
(
int
i
=
0
;
i
<
N_SCANS
;
i
++
)
// 遍历所有扫描线
{
pl_buff
[
i
].
clear
();
// 清空扫描线点云
pl_buff
[
i
].
reserve
(
plsize
);
// 为扫描线点云预留空间
}
for
(
uint
i
=
0
;
i
<
plsize
;
i
++
)
// 遍历所有点云
{
double
range
=
pl_orig
.
points
[
i
].
x
*
pl_orig
.
points
[
i
].
x
+
pl_orig
.
points
[
i
].
y
*
pl_orig
.
points
[
i
].
y
+
pl_orig
.
points
[
i
].
z
*
pl_orig
.
points
[
i
].
z
;
// 计算点云距离原点的距离
if
(
range
<
blind
)
continue
;
// 如果距离小于盲区跳过
Eigen
::
Vector3d
pt_vec
;
// 定义一个三维向量
PointType
added_pt
;
// 定义一个加入点云的点
added_pt
.
x
=
pl_orig
.
points
[
i
].
x
;
// 记录点云的 x 坐标
added_pt
.
y
=
pl_orig
.
points
[
i
].
y
;
// 记录点云的 y 坐标
added_pt
.
z
=
pl_orig
.
points
[
i
].
z
;
// 记录点云的 z 坐标
added_pt
.
intensity
=
pl_orig
.
points
[
i
].
intensity
;
// 记录点云的强度值
added_pt
.
normal_x
=
0
;
// 记录点云的法向量 x 坐标(初始值为 0)
added_pt
.
normal_y
=
0
;
// 记录点云的法向量 y 坐标(初始值为 0)
added_pt
.
normal_z
=
0
;
// 记录点云的法向量 z 坐标(初始值为 0)
double
yaw_angle
=
atan2
(
added_pt
.
y
,
added_pt
.
x
)
*
57.3
;
// 计算点云的方位角(角度制)
if
(
yaw_angle
>=
180.0
)
yaw_angle
-=
360.0
;
if
(
yaw_angle
<=
-
180.0
)
yaw_angle
+=
360.0
;
added_pt
.
curvature
=
pl_orig
.
points
[
i
].
t
/
1e6
;
// 记录点云的曲率
if
(
pl_orig
.
points
[
i
].
ring
<
N_SCANS
)
// 如果点云在扫描范围内
{
pl_buff
[
pl_orig
.
points
[
i
].
ring
].
push_back
(
added_pt
);
// 将点云加入对应的扫描线点云中
}
}
for
(
int
j
=
0
;
j
<
N_SCANS
;
j
++
)
// 遍历所有扫描线
{
PointCloudXYZI
&
pl
=
pl_buff
[
j
];
// 获取当前扫描线点云
int
linesize
=
pl
.
size
();
// 获取扫描线点云的数量
vector
<
orgtype
>
&
types
=
typess
[
j
];
// 获取当前扫描线的特征点类型
types
.
clear
();
// 清空特征点类型
types
.
resize
(
linesize
);
// 为特征点类型预留空间
linesize
--
;
for
(
uint
i
=
0
;
i
<
linesize
;
i
++
)
// 遍历扫描线点云
{
types
[
i
].
range
=
sqrt
(
pl
[
i
].
x
*
pl
[
i
].
x
+
pl
[
i
].
y
*
pl
[
i
].
y
);
// 计算点云距离原点的距离
vx
=
pl
[
i
].
x
-
pl
[
i
+
1
].
x
;
// 计算点云的横向距离
vy
=
pl
[
i
].
y
-
pl
[
i
+
1
].
y
;
// 计算点云的纵向距离
vz
=
pl
[
i
].
z
-
pl
[
i
+
1
].
z
;
// 计算点云的垂直距离
types
[
i
].
dista
=
vx
*
vx
+
vy
*
vy
+
vz
*
vz
;
// 计算点云之间的距离
}
types
[
linesize
].
range
=
sqrt
(
pl
[
linesize
].
x
*
pl
[
linesize
].
x
+
pl
[
linesize
].
y
*
pl
[
linesize
].
y
);
// 处理最后一个点云的距离
give_feature
(
pl
,
types
);
// 将扫描线点云的特征提取出来
}
}
else
// 如果未开启特征点提取
{
double
time_stamp
=
msg
->
header
.
stamp
.
toSec
();
// 获取消息的时间戳
// 循环遍历原始点云中的每个点
for
(
int
i
=
0
;
i
<
pl_orig
.
points
.
size
();
i
++
)
{
// 如果当前点的索引不是筛选倍数的整数,则跳过
if
(
i
%
point_filter_num
!=
0
)
continue
;
// 计算当前点到原点的距离
double
range
=
pl_orig
.
points
[
i
].
x
*
pl_orig
.
points
[
i
].
x
+
pl_orig
.
points
[
i
].
y
*
pl_orig
.
points
[
i
].
y
+
pl_orig
.
points
[
i
].
z
*
pl_orig
.
points
[
i
].
z
;
// 如果当前点距离太小,则跳过
if
(
range
<
blind
)
continue
;
// 将当前点的坐标、强度、曲率信息保存到新点云中
PointType
added_pt
;
added_pt
.
x
=
pl_orig
.
points
[
i
].
x
;
added_pt
.
y
=
pl_orig
.
points
[
i
].
y
;
added_pt
.
z
=
pl_orig
.
points
[
i
].
z
;
added_pt
.
intensity
=
pl_orig
.
points
[
i
].
intensity
;
added_pt
.
curvature
=
pl_orig
.
points
[
i
].
t
/
1e6
;
// 将当前点的法向量信息设为 0
added_pt
.
normal_x
=
0
;
added_pt
.
normal_y
=
0
;
added_pt
.
normal_z
=
0
;
// 计算当前点的偏航角
double
yaw_angle
=
atan2
(
added_pt
.
y
,
added_pt
.
x
)
*
57.3
;
if
(
yaw_angle
>=
180.0
)
yaw_angle
-=
360.0
;
if
(
yaw_angle
<=
-
180.0
)
yaw_angle
=
360.0
;
// 将新点添加到新点云中
pl_surf
.
points
.
push_back
(
added_pt
);
}
// pub_func(pl_surf, pub_full, msg->header.stamp);
// pub_func(pl_surf, pub_corn, msg->header.stamp);
}
// 定义点云数量的最大值
#define MAX_LINE_NUM 64
void
Preprocess
::
velodyne_handler
(
const
sensor_msgs
::
PointCloud2
::
ConstPtr
&
msg
)
{
// 清空三个点云容器
pl_surf
.
clear
();
pl_corn
.
clear
();
pl_full
.
clear
();
// 将输入的ros消息类型的点云转换为pcl中的点云
pcl
::
PointCloud
<
velodyne_ros
::
Point
>
pl_orig
;
pcl
::
fromROSMsg
(
*
msg
,
pl_orig
);
int
plsize
=
pl_orig
.
points
.
size
();
pl_surf
.
reserve
(
plsize
);
bool
is_first
[
MAX_LINE_NUM
];
// 标记每行数据的第一个点
double
yaw_fp
[
MAX_LINE_NUM
]
=
{
0
};
// 存储每行数据的第一个点的偏航角
double
omega_l
=
3.61
;
// Velodyne激光雷达扫描的相对角速率
float
yaw_last
[
MAX_LINE_NUM
]
=
{
0.0
};
// 存储每行数据的最后一个点的偏航角
float
time_last
[
MAX_LINE_NUM
]
=
{
0.0
};
// 存储最后一个点时间戳
// 判断点云数据是否存在时间戳
if
(
pl_orig
.
points
[
plsize
-
1
].
time
>
0
)
{
given_offset_time
=
true
;
}
else
{
given_offset_time
=
false
;
memset
(
is_first
,
true
,
sizeof
(
is_first
));
double
yaw_first
=
atan2
(
pl_orig
.
points
[
0
].
y
,
pl_orig
.
points
[
0
].
x
)
*
57.29578
;
// 计算第一个点的偏航角
double
yaw_end
=
yaw_first
;
int
layer_first
=
pl_orig
.
points
[
0
].
ring
;
// 获取第一层的激光线数量
for
(
uint
i
=
plsize
-
1
;
i
>
0
;
i
--
)
{
if
(
pl_orig
.
points
[
i
].
ring
==
layer_first
)
// 找到最后一层的激光线数量
{
yaw_end
=
atan2
(
pl_orig
.
points
[
i
].
y
,
pl_orig
.
points
[
i
].
x
)
*
57.29578
;
// 计算最后一个点的偏航角
break
;
}
}
}
if
(
feature_enabled
)
//检查特征是否已启用
{
for
(
int
i
=
0
;
i
<
N_SCANS
;
i
++
)
//循环遍历每一个激光雷达扫描
{
pl_buff
[
i
].
clear
();
//清空激光雷达缓冲区
pl_buff
[
i
].
reserve
(
plsize
);
//为激光雷达缓冲区预留空间
}
for
(
int
i
=
0
;
i
<
plsize
;
i
++
)
//循环遍历激光雷达点云的每一个点
{
PointType
added_pt
;
//定义一个点类型
added_pt
.
normal_x
=
0
;
//将该点的法线向量赋值为0
added_pt
.
normal_y
=
0
;
added_pt
.
normal_z
=
0
;
int
layer
=
pl_orig
.
points
[
i
].
ring
;
//获取该点的环号(对应于扫描线数目)
if
(
layer
>=
N_SCANS
)
continue
;
//如果环号大于等于扫描线数目,则跳过该点
added_pt
.
x
=
pl_orig
.
points
[
i
].
x
;
//将该点的坐标赋值给定义的点类型
added_pt
.
y
=
pl_orig
.
points
[
i
].
y
;
added_pt
.
z
=
pl_orig
.
points
[
i
].
z
;
added_pt
.
intensity
=
pl_orig
.
points
[
i
].
intensity
;
//将该点的强度值赋值给定义的点类型
added_pt
.
curvature
=
pl_orig
.
points
[
i
].
time
/
1000.0
;
//将该点的曲率值赋值给定义的点类型,单位为ms
if
(
!
given_offset_time
)
// 如果偏移时间为空
{
// 计算航向角
double
yaw_angle
=
atan2
(
added_pt
.
y
,
added_pt
.
x
)
*
57.2957
;
if
(
is_first
[
layer
])
// 如果当前层为第一次处理
{
// 将当前航向角作为起始航向角
yaw_fp
[
layer
]
=
yaw_angle
;
// 将is_first[layer]设置为false
is_first
[
layer
]
=
false
;
// 将曲率设为0
added_pt
.
curvature
=
0.0
;
// 更新最后一次处理的航向角(yaw_last)为当前航向角(yaw_angle)
yaw_last
[
layer
]
=
yaw_angle
;
// 更新最后一次处理的曲率(time_last)为当前曲率(added_pt.curvature)
time_last
[
layer
]
=
added_pt
.
curvature
;
// 跳过当前循环
continue
;
}
}
if
(
yaw_angle
<=
yaw_fp
[
layer
])
//如果当前航向角小于等于目标航向角
{
added_pt
.
curvature
=
(
yaw_fp
[
layer
]
-
yaw_angle
)
/
omega_l
;
//计算运动点的曲率
}
else
//如果当前航向角大于目标航向角
{
added_pt
.
curvature
=
(
yaw_fp
[
layer
]
-
yaw_angle
360.0
)
/
omega_l
;
//计算运动点的曲率
}
if
(
added_pt
.
curvature
<
time_last
[
layer
])
added_pt
.
curvature
=
360.0
/
omega_l
;
//如果曲率小于上一个点的曲率,则赋值为最小曲率,即直线
yaw_last
[
layer
]
=
yaw_angle
;
//记录航向角作为上一个点的航向角
time_last
[
layer
]
=
added_pt
.
curvature
;
//记录曲率作为上一个点的曲率
pl_buff
[
layer
].
points
.
push_back
(
added_pt
);
//将添加的点加入点云缓存中的第layer层
for
(
int
j
=
0
;
j
<
N_SCANS
;
j
++
)
//循环遍历点云中的每一层
{
PointCloudXYZI
&
pl
=
pl_buff
[
j
];
//获取第j层点云
int
linesize
=
pl
.
size
();
//获取该层点云的点数
if
(
linesize
<
2
)
continue
;
//如果该层点云的点数小于2,则跳过该层
vector
<
orgtype
>
&
types
=
typess
[
j
];
//获取该层点云的特征类型
types
.
clear
();
//清空特征类型向量
types
.
resize
(
linesize
);
//将特征类型向量大小设置为该层点云的点数
linesize
--
;
//将点数减1,以便后续计算
//遍历该层点云的每个点,计算特征类型
for
(
uint
i
=
0
;
i
<
linesize
;
i
++
)
{
types
[
i
].
range
=
sqrt
(
pl
[
i
].
x
*
pl
[
i
].
x
+
pl
[
i
].
y
*
pl
[
i
].
y
);
//计算该点到雷达的距离
vx
=
pl
[
i
].
x
-
pl
[
i
+
1
].
x
;
//计算该点与下一个点在x方向上的距离差
vy
=
pl
[
i
].
y
-
pl
[
i
+
1
].
y
;
//计算该点与下一个点在y方向上的距离差
vz
=
pl
[
i
].
z
-
pl
[
i
+
1
].
z
;
//计算该点与下一个点在z方向上的距离差
types
[
i
].
dista
=
vx
*
vx
+
vy
*
vy
+
vz
*
vz
;
//计算该点与下一个点之间的距离
}
types
[
linesize
].
range
=
sqrt
(
pl
[
linesize
].
x
*
pl
[
linesize
].
x
+
pl
[
linesize
].
y
*
pl
[
linesize
].
y
);
//计算该层点云最后一个点到雷达的距离
give_feature
(
pl
,
types
);
//对该层点云进行特征提取
}
else
{
// 循环遍历plsize个点
for
(
int
i
=
0
;
i
<
plsize
;
i
++
)
{
PointType
added_pt
;
// 初始化added_pt的normal和坐标
added_pt
.
normal_x
=
0
;
added_pt
.
normal_y
=
0
;
added_pt
.
normal_z
=
0
;
added_pt
.
x
=
pl_orig
.
points
[
i
].
x
;
added_pt
.
y
=
pl_orig
.
points
[
i
].
y
;
added_pt
.
z
=
pl_orig
.
points
[
i
].
z
;
added_pt
.
intensity
=
pl_orig
.
points
[
i
].
intensity
;
// added_pt的曲率属性为时间值除以1000
added_pt
.
curvature
=
pl_orig
.
points
[
i
].
time
/
1000.0
;
// 如果没有给定时间偏移,进入以下操作
if
(
!
given_offset_time
)
{
// 获取点所在的激光环编号和偏航角度
int
layer
=
pl_orig
.
points
[
i
].
ring
;
double
yaw_angle
=
atan2
(
added_pt
.
y
,
added_pt
.
x
)
*
57.2957
;
// 如果该激光环是第一个点,更新yaw_fp、is_first,设置曲率属性为0,跳过该点
if
(
is_first
[
layer
])
{
yaw_fp
[
layer
]
=
yaw_angle
;
is_first
[
layer
]
=
false
;
added_pt
.
curvature
=
0.0
;
yaw_last
[
layer
]
=
yaw_angle
;
time_last
[
layer
]
=
added_pt
.
curvature
;
continue
;
}
// 计算偏移时间
if
(
yaw_angle
<=
yaw_fp
[
layer
])
// 如果当前的偏航角小于等于目标偏航角
{
added_pt
.
curvature
=
(
yaw_fp
[
layer
]
-
yaw_angle
)
/
omega_l
;
// 偏移时间为目标偏航角减去当前偏航角的值再除以角速度omega_l
}
else
// 如果当前偏航角大于目标偏航角
{
added_pt
.
curvature
=
(
yaw_fp
[
layer
]
-
yaw_angle
+
360.0
)
/
omega_l
;
// 偏移时间为目标偏航角减去360度再加上当前偏航角的值再除以角速度omega_l
}
if
(
added_pt
.
curvature
<
time_last
[
layer
])
added_pt
.
curvature
=
360.0
/
omega_l
;
// 如果偏移时间小于上一次的偏移时间,则将偏移时间设置为一个很大的值
yaw_last
[
layer
]
=
yaw_angle
;
// 更新上一次的偏航角为当前偏航角
time_last
[
layer
]
=
added_pt
.
curvature
;
// 更新上一次的偏移时间为当前偏移时间
// if(i==(plsize-1)) printf("index: %d layer: %d, yaw: %lf, offset-time: %lf, condition: %d\n", i, layer, yaw_angle, added_pt.curvature, prints);
if
(
i
%
point_filter_num
==
0
)
{
if
(
added_pt
.
x
*
added_pt
.
x
+
added_pt
.
y
*
added_pt
.
y
+
added_pt
.
z
*
added_pt
.
z
>
blind
)
{
pl_surf
.
points
.
push_back
(
added_pt
);
// printf("time mode: %d time: %d \n", given_offset_time, pl_orig.points[i].t);
}
}
}
}
// pub_func(pl_surf, pub_full, msg->header.stamp);
// pub_func(pl_surf, pub_surf, msg->header.stamp);
// pub_func(pl_surf, pub_corn, msg->header.stamp);
}
void
Preprocess
::
give_feature
(
pcl
::
PointCloud
<
PointType
>
&
pl
,
vector
<
orgtype
>
&
types
)
{
int
plsize
=
pl
.
size
();
int
plsize2
;
if
(
plsize
==
0
)
{
printf
(
"something wrong
\n
"
);
return
;
}
uint
head
=
0
;
while
(
types
[
head
].
range
<
blind
)
{
head
++
;
}
// Surf
// 计算剩余IMU数据点数
plsize2
=
(
plsize
>
group_size
)
?
(
plsize
-
group_size
)
:
0
;
// 定义当前方向向量和上一个方向向量
Eigen
::
Vector3d
curr_direct
(
Eigen
::
Vector3d
::
Zero
());
Eigen
::
Vector3d
last_direct
(
Eigen
::
Vector3d
::
Zero
());
// 定义两个索引变量和最后一个有效索引变量,以及一个状态和平面类型
uint
i_nex
=
0
,
i2
;
uint
last_i
=
0
;
uint
last_i_nex
=
0
;
int
last_state
=
0
;
int
plane_type
;
// 循环处理IMU数据
for
(
uint
i
=
head
;
i
<
plsize2
;
i
++
)
{
// 如果数据点的距离小于盲区距离,则跳过当前循环,不进行处理
if
(
types
[
i
].
range
<
blind
)
{
continue
;
}
i2
=
i
;
// 判断当前路径的飞机类型
plane_type
=
plane_judge
(
pl
,
types
,
i
,
i_nex
,
curr_direct
);
if
(
plane_type
==
1
)
// 如果为确立路径,则进行以下操作
{
for
(
uint
j
=
i
;
j
<=
i_nex
;
j
++
)
// 遍历路径中所有点
{
if
(
j
!=
i
&&
j
!=
i_nex
)
{
types
[
j
].
ftype
=
Real_Plane
;
// 非起点和终点的点标记为实际路径点
}
else
{
types
[
j
].
ftype
=
Poss_Plane
;
// 起点和终点标记为可能路径点
}
}
}
// if(last_state==1 && fabs(last_direct.sum())>0.5)
// 如果上一个平面状态为1且上一个方向向量的模大于0.1
if
(
last_state
==
1
&&
last_direct
.
norm
()
>
0.1
)
{
// 计算上一个方向向量和当前方向向量的点积
double
mod
=
last_direct
.
transpose
()
*
curr_direct
;
// 如果点积的值在-0.707到0.707之间,将边缘平面的类型设为Edge_Plane
if
(
mod
>
-
0.707
&&
mod
<
0.707
)
{
types
[
i
].
ftype
=
Edge_Plane
;
}
// 否则将其设为Real_Plane
else
{
types
[
i
].
ftype
=
Real_Plane
;
}
}
// 将计数器移动到下一个索引位置并相应地更新上一个平面状态
i
=
i_nex
-
1
;
last_state
=
1
;
// 如果平面类型为2,将计数器移动到下一个索引位置并将上一个状态设为0
else
{
i
=
i_nex
;
last_state
=
0
;
}
// else if(plane_type == 0)
// {
// if(last_state == 1)
// {
// uint i_nex_tem;
// uint j;
// for(j=last_i+1; j<=last_i_nex; j++)
// {
// uint i_nex_tem2 = i_nex_tem;
// Eigen::Vector3d curr_direct2;
// uint ttem = plane_judge(pl, types, j, i_nex_tem, curr_direct2);
// if(ttem != 1)
// {
// i_nex_tem = i_nex_tem2;
// break;
// }
// curr_direct = curr_direct2;
// }
// if(j == last_i+1)
// {
// last_state = 0;
// }
// else
// {
// for(uint k=last_i_nex; k<=i_nex_tem; k++)
// {
// if(k != i_nex_tem)
// {
// types[k].ftype = Real_Plane;
// }
// else
// {
// types[k].ftype = Poss_Plane;
// }
// }
// i = i_nex_tem-1;
// i_nex = i_nex_tem;
// i2 = j-1;
// last_state = 1;
// }
// }
// }
// 记录上个平面的信息
last_i
=
i2
;
last_i_nex
=
i_nex
;
last_direct
=
curr_direct
;
// 计算平面局部法向量
plsize2
=
plsize
>
3
?
plsize
-
3
:
0
;
for
(
uint
i
=
head
+
3
;
i
<
plsize2
;
i
++
)
// 从第4个点开始计算
{
if
(
types
[
i
].
range
<
blind
||
types
[
i
].
ftype
>=
Real_Plane
)
// 排除无效点或非地面点
{
continue
;
}
if
(
types
[
i
-
1
].
dista
<
1e-16
||
types
[
i
].
dista
<
1e-16
)
// 排除距离过小的点
{
continue
;
}
// 计算当前点和相邻两个点的向量
Eigen
::
Vector3d
vec_a
(
pl
[
i
].
x
,
pl
[
i
].
y
,
pl
[
i
].
z
);
Eigen
::
Vector3d
vecs
[
2
];
for
(
int
j
=
0
;
j
<
2
;
j
++
)
// 对于当前点的前一个点和后一个点
{
int
m
=
-
1
;
if
(
j
==
1
)
{
m
=
1
;
}
// 如果相邻的点无效,则直接标记为不可连接
if
(
types
[
i
+
m
].
range
<
blind
)
{
if
(
types
[
i
].
range
>
inf_bound
)
{
types
[
i
].
edj
[
j
]
=
Nr_inf
;
}
else
{
types
[
i
].
edj
[
j
]
=
Nr_blind
;
}
continue
;
}
// 计算向量并计算夹角
vecs
[
j
]
=
Eigen
::
Vector3d
(
pl
[
i
+
m
].
x
,
pl
[
i
+
m
].
y
,
pl
[
i
+
m
].
z
);
vecs
[
j
]
=
vecs
[
j
]
-
vec_a
;
types
[
i
].
angle
[
j
]
=
vec_a
.
dot
(
vecs
[
j
])
/
vec_a
.
norm
()
/
vecs
[
j
].
norm
();
// 标记当前点和相邻点之间的连接类型
if
(
types
[
i
].
angle
[
j
]
<
jump_up_limit
)
{
types
[
i
].
edj
[
j
]
=
Nr_180
;
}
else
if
(
types
[
i
].
angle
[
j
]
>
jump_down_limit
)
{
types
[
i
].
edj
[
j
]
=
Nr_zero
;
}
}
}
// 计算两个向量之间的交点
types
[
i
].
intersect
=
vecs
[
Prev
].
dot
(
vecs
[
Next
])
/
vecs
[
Prev
].
norm
()
/
vecs
[
Next
].
norm
();
// 检查当前边缘是否是一个潜在的边缘跳跃,通过比较边的类型和距离来确定
if
(
types
[
i
].
edj
[
Prev
]
==
Nr_nor
&&
types
[
i
].
edj
[
Next
]
==
Nr_zero
&&
types
[
i
].
dista
>
0.0225
&&
types
[
i
].
dista
>
4
*
types
[
i
-
1
].
dista
)
{
// 检查交点角度是否大于160度
if
(
types
[
i
].
intersect
>
cos160
)
{
// 如果确认了边缘跳跃,请检查上一个边缘的类型是否正常
if
(
edge_jump_judge
(
pl
,
types
,
i
,
Prev
))
{
// 将边的类型设置为Edge_Jump
types
[
i
].
ftype
=
Edge_Jump
;
}
}
}
// 检查当前边缘是否是一个潜在的边缘跳跃,通过比较边的类型和距离来确定
else
if
(
types
[
i
].
edj
[
Prev
]
==
Nr_zero
&&
types
[
i
].
edj
[
Next
]
==
Nr_nor
&&
types
[
i
-
1
].
dista
>
0.0225
&&
types
[
i
-
1
].
dista
>
4
*
types
[
i
].
dista
)
{
// 检查交点角度是否大于160度
if
(
types
[
i
].
intersect
>
cos160
)
{
// 如果确认了边缘跳跃,请检查下一个边缘的类型是否正常
if
(
edge_jump_judge
(
pl
,
types
,
i
,
Next
))
{
// 将边的类型设置为Edge_Jump
types
[
i
].
ftype
=
Edge_Jump
;
}
}
}
//检查前一个边缘是不是普通边缘且下一个边缘是无穷边缘
else
if
(
types
[
i
].
edj
[
Prev
]
==
Nr_nor
&&
types
[
i
].
edj
[
Next
]
==
Nr_inf
)
{
//如果检测到边缘跳变,将ftype设置为Edge_Jump
if
(
edge_jump_judge
(
pl
,
types
,
i
,
Prev
))
{
types
[
i
].
ftype
=
Edge_Jump
;
}
}
//检查前一个边缘是不是无穷边缘且下一个边缘是普通边缘
else
if
(
types
[
i
].
edj
[
Prev
]
==
Nr_inf
&&
types
[
i
].
edj
[
Next
]
==
Nr_nor
)
{
//如果检测到边缘跳变,将ftype设置为Edge_Jump
if
(
edge_jump_judge
(
pl
,
types
,
i
,
Next
))
{
types
[
i
].
ftype
=
Edge_Jump
;
}
}
//检查前一个边缘和下一个边缘都不是普通边缘
else
if
(
types
[
i
].
edj
[
Prev
]
>
Nr_nor
&&
types
[
i
].
edj
[
Next
]
>
Nr_nor
)
{
//如果当前ftype为Nor,则将其设置为Wire
if
(
types
[
i
].
ftype
==
Nor
)
{
types
[
i
].
ftype
=
Wire
;
}
}
// 将 plsize 减 1 的结果赋值给 plsize2 变量
plsize2
=
plsize
-
1
;
// 声明变量 ratio 为 double 类型
double
ratio
;
// 使用 for 循环遍历一个范围的值,从头部 + 1 的位置到 plsize2 的位置
for
(
uint
i
=
head
+
1
;
i
<
plsize2
;
i
++
)
{
// 检查当前点和它相邻点的 range 值是否小于 blind 值,如果是,则跳过当前循环,继续下一次循环
if
(
types
[
i
].
range
<
blind
||
types
[
i
-
1
].
range
<
blind
||
types
[
i
+
1
].
range
<
blind
)
{
continue
;
}
// 检查当前点和它前一个点的 dista 值是否小于 1e-8,如果是,则跳过当前循环,继续下一次循环
if
(
types
[
i
-
1
].
dista
<
1e-8
||
types
[
i
].
dista
<
1e-8
)
{
continue
;
}
// 检查当前点的 feature type 是否为 Nor
if
(
types
[
i
].
ftype
==
Nor
)
{
// 如果前一个点的 dista 值大于当前点的 dista 值,则将 ratio 值赋值为前一个点的 dista 值除以当前点的 dista 值;否则将 ratio 值赋值为当前点的 dista 值除以前一个点的 dista 值
if
(
types
[
i
-
1
].
dista
>
types
[
i
].
dista
)
{
ratio
=
types
[
i
-
1
].
dista
/
types
[
i
].
dista
;
}
else
{
ratio
=
types
[
i
].
dista
/
types
[
i
-
1
].
dista
;
}
// 如果当前点和其相邻点的 feature type 均为 Nor,且当前点的 intersect 值小于 smallp_intersect,且 ratio 值小于 smallp_ratio,将这些点的 feature type 均修改为 Real_Plane
if
(
types
[
i
].
intersect
<
smallp_intersect
&&
ratio
<
smallp_ratio
)
{
if
(
types
[
i
-
1
].
ftype
==
Nor
)
{
types
[
i
-
1
].
ftype
=
Real_Plane
;
}
if
(
types
[
i
+
1
].
ftype
==
Nor
)
{
types
[
i
+
1
].
ftype
=
Real_Plane
;
}
types
[
i
].
ftype
=
Real_Plane
;
}
}
}
// 初始化last_surface为-1
int
last_surface
=
-
1
;
// 遍历点云数据
for
(
uint
j
=
head
;
j
<
plsize
;
j
++
)
{
// 如果当前点类型是平面类型(Possible_Plane 或 Real_Plane)
if
(
types
[
j
].
ftype
==
Poss_Plane
||
types
[
j
].
ftype
==
Real_Plane
)
{
// 如果上一个点不是平面,则记录当前点为第一个平面点
if
(
last_surface
==
-
1
)
{
last_surface
=
j
;
}
// 判断当前的点是否为上一个平面点的下一个点
if
(
j
==
uint
(
last_surface
+
point_filter_num
-
1
))
{
// 如果是,则将该平面点加入到pl_surf数组中
PointType
ap
;
ap
.
x
=
pl
[
j
].
x
;
ap
.
y
=
pl
[
j
].
y
;
ap
.
z
=
pl
[
j
].
z
;
ap
.
intensity
=
pl
[
j
].
intensity
;
ap
.
curvature
=
pl
[
j
].
curvature
;
pl_surf
.
push_back
(
ap
);
// 重置last_surface为-1,表示当前平面已经处理完毕
last_surface
=
-
1
;
}
}
else
{
// 如果当前点类型不是平面类型,且上一个点是平面类型,说明当前点是边角点
if
(
types
[
j
].
ftype
==
Edge_Jump
||
types
[
j
].
ftype
==
Edge_Plane
)
{
// 将该点加入到pl_corn数组中
pl_corn
.
push_back
(
pl
[
j
]);
}
// 如果上一个点是平面类型,则将该平面点的平均值加入到pl_surf数组中
if
(
last_surface
!=
-
1
)
{
PointType
ap
;
for
(
uint
k
=
last_surface
;
k
<
j
;
k
++
)
{
ap
.
x
+=
pl
[
k
].
x
;
ap
.
y
+=
pl
[
k
].
y
;
ap
.
z
+=
pl
[
k
].
z
;
ap
.
intensity
+=
pl
[
k
].
intensity
;
ap
.
curvature
+=
pl
[
k
].
curvature
;
}
ap
.
x
/=
(
j
-
last_surface
);
ap
.
y
/=
(
j
-
last_surface
);
ap
.
z
/=
(
j
-
last_surface
);
ap
.
intensity
/=
(
j
-
last_surface
);
ap
.
curvature
/=
(
j
-
last_surface
);
pl_surf
.
push_back
(
ap
);
}
// 重置last_surface为-1
last_surface
=
-
1
;
}
}
// 这是一个名为Preprocess的类中的pub_func方法,用于将PointCloudXYZI类型的点云数据转换为sensor_msgs::PointCloud2类型的数据并发布出去
void
Preprocess
::
pub_func
(
PointCloudXYZI
&
pl
,
const
ros
::
Time
&
ct
)
{
// 将点云的高度设置为1,宽度设置为点云的大小
pl
.
height
=
1
;
pl
.
width
=
pl
.
size
();
// 将点云数据转换为ROS消息类型
sensor_msgs
::
PointCloud2
output
;
pcl
::
toROSMsg
(
pl
,
output
);
// 设置ROS消息的帧id为“livox”,时间戳为当前时间
output
.
header
.
frame_id
=
"livox"
;
output
.
header
.
stamp
=
ct
;
}
int
Preprocess
::
plane_judge
(
const
PointCloudXYZI
&
pl
,
vector
<
orgtype
>
&
types
,
uint
i_cur
,
uint
&
i_nex
,
Eigen
::
Vector3d
&
curr_direct
)
{
double
group_dis
=
disA
*
types
[
i_cur
].
range
-
disB
;
// 计算检测范围的距离
group_dis
=
group_dis
*
group_dis
;
// i_nex = i_cur;
double
two_dis
;
vector
<
double
>
disarr
;
disarr
.
reserve
(
20
);
for
(
i_nex
=
i_cur
;
i_nex
<
i_cur
+
group_size
;
i_nex
++
)
// 选取当前点为起始点,向后取group_size个点进行处理
{
if
(
types
[
i_nex
].
range
<
blind
)
// 如果范围小于盲区,则无法进行处理
{
curr_direct
.
setZero
();
// 清零向量
return
2
;
}
disarr
.
push_back
(
types
[
i_nex
].
dista
);
// 将距离值添加到列表中
}
// 无限循环,直到满足条件跳出
for
(;;)
{
// 如果索引超出数组大小则跳出循环
if
((
i_cur
>=
pl
.
size
())
||
(
i_nex
>=
pl
.
size
()))
break
;
// 判断下一点的距离是否小于盲区距离
if
(
types
[
i_nex
].
range
<
blind
)
{
curr_direct
.
setZero
();
return
2
;
// 返回2
}
// 计算下一点与当前点的距离
vx
=
pl
[
i_nex
].
x
-
pl
[
i_cur
].
x
;
vy
=
pl
[
i_nex
].
y
-
pl
[
i_cur
].
y
;
vz
=
pl
[
i_nex
].
z
-
pl
[
i_cur
].
z
;
two_dis
=
vx
*
vx
+
vy
*
vy
+
vz
*
vz
;
// 如果当前距离大于等于组距,则退出循环
if
(
two_dis
>=
group_dis
)
{
break
;
}
// 如果当前距离小于组距,则将当前点的特征距离存入disarr中,并将下一点索引+1
disarr
.
push_back
(
types
[
i_nex
].
dista
);
i_nex
++
;
}
// 初始化长度和宽度为0
double
leng_wid
=
0
;
double
v1
[
3
],
v2
[
3
];
// 循环遍历当前点与下一点之间的所有点
for
(
uint
j
=
i_cur
+
1
;
j
<
i_nex
;
j
++
)
{
// 如果索引超出数组大小则跳出循环
if
((
j
>=
pl
.
size
())
||
(
i_cur
>=
pl
.
size
()))
break
;
// 计算当前点与下一点的向量
v1
[
0
]
=
pl
[
j
].
x
-
pl
[
i_cur
].
x
;
v1
[
1
]
=
pl
[
j
].
y
-
pl
[
i_cur
].
y
;
v1
[
2
]
=
pl
[
j
].
z
-
pl
[
i_cur
].
z
;
// 计算当前点与下一点的法向量
v2
[
0
]
=
v1
[
1
]
*
vz
-
vy
*
v1
[
2
];
v2
[
1
]
=
v1
[
2
]
*
vx
-
v1
[
0
]
*
vz
;
v2
[
2
]
=
v1
[
0
]
*
vy
-
vx
*
v1
[
1
];
// 计算长度和宽度的平方
double
lw
=
v2
[
0
]
*
v2
[
0
]
+
v2
[
1
]
*
v2
[
1
]
+
v2
[
2
]
*
v2
[
2
];
// 如果当前长度和宽度的平方大于之前记录的,则更新记录
if
(
lw
>
leng_wid
)
{
leng_wid
=
lw
;
}
}
// 如果两个轮子的距离除以车辆长度小于轮胎宽度和长度的比值,则设置当前方向为零向量并返回0
if
((
two_dis
*
two_dis
/
leng_wid
)
<
p2l_ratio
)
{
curr_direct
.
setZero
();
return
0
;
}
// 对车辆四个轮子到地面的距离进行从大到小的排序
uint
disarrsize
=
disarr
.
size
();
for
(
uint
j
=
0
;
j
<
disarrsize
-
1
;
j
++
)
{
for
(
uint
k
=
j
+
1
;
k
<
disarrsize
;
k
++
)
{
if
(
disarr
[
j
]
<
disarr
[
k
])
// 如果第j个距离小于第k个距离,交换位置,使距离从大到小排序
{
leng_wid
=
disarr
[
j
];
disarr
[
j
]
=
disarr
[
k
];
disarr
[
k
]
=
leng_wid
;
}
}
}
// 判断倒数第二个距离值是否小于 1e-16,如果是,将 curr_direct 设为 0 向量,然后返回 0。
if
(
disarr
[
disarr
.
size
()
-
2
]
<
1e-16
)
{
curr_direct
.
setZero
();
return
0
;
}
// 判断激光雷达类型是否为 AVIA。
if
(
lidar_type
==
AVIA
)
{
// 计算 dismax_mid 和 dismid_min。
double
dismax_mid
=
disarr
[
0
]
/
disarr
[
disarrsize
/
2
];
double
dismid_min
=
disarr
[
disarrsize
/
2
]
/
disarr
[
disarrsize
-
2
];
// 如果 dismax_mid 大于等于 limit_maxmid 或者 dismid_min 大于等于 limit_midmin,将 curr_direct 设为 0 向量,然后返回 0。
if
(
dismax_mid
>=
limit_maxmid
||
dismid_min
>=
limit_midmin
)
{
curr_direct
.
setZero
();
return
0
;
}
}
else
// 如果激光雷达类型不是 AVIA。
{
// 计算 dismax_min。
double
dismax_min
=
disarr
[
0
]
/
disarr
[
disarrsize
-
2
];
// 如果 dismax_min 大于等于 limit_maxmin,将 curr_direct 设为 0 向量,然后返回 0。
if
(
dismax_min
>=
limit_maxmin
)
{
curr_direct
.
setZero
();
return
0
;
}
}
// 将 vx、vy、vz 组成向量 curr_direct。
curr_direct
<<
vx
,
vy
,
vz
;
// 将 curr_direct 归一化。
curr_direct
.
normalize
();
// 返回 1。
return
1
;
bool
Preprocess
::
edge_jump_judge
(
const
PointCloudXYZI
&
pl
,
vector
<
orgtype
>
&
types
,
uint
i
,
Surround
nor_dir
)
{
// 判断法向量的方向
if
(
nor_dir
==
0
)
{
//如果该点和前两个点中有任意一个点的激光雷达测量范围小于盲区,则返回false
if
(
types
[
i
-
1
].
range
<
blind
||
types
[
i
-
2
].
range
<
blind
)
{
return
false
;
}
}
else
if
(
nor_dir
==
1
)
{
//如果该点和后两个点中有任意一个点的激光雷达测量范围小于盲区,则返回false
if
(
types
[
i
+
1
].
range
<
blind
||
types
[
i
+
2
].
range
<
blind
)
{
return
false
;
}
}
// 获取朝向的索引值,用于计算距离
double
d1
=
types
[
i
nor_dir
-
1
].
dista
;
double
d2
=
types
[
i
3
*
nor_dir
-
2
].
dista
;
double
d
;
// 如果d1小于d2,则交换d1和d2的值
if
(
d1
<
d2
)
{
d
=
d1
;
d1
=
d2
;
d2
=
d
;
}
// 计算两点之间的距离
d1
=
sqrt
(
d1
);
d2
=
sqrt
(
d2
);
// 判断是否在边界内,如果在边界内则返回true,否则返回false
if
(
d1
>
edgea
*
d2
||
(
d1
-
d2
)
>
edgeb
)
{
return
false
;
}
return
true
;
imu222.cpp
0 → 100644
浏览文件 @
d4080bba
#include <cmath>
#include <math.h>
#include <deque>
#include <mutex>
#include <thread>
#include <fstream>
#include <csignal>
#include <ros/ros.h>
#include <so3_math.h>
#include <Eigen/Eigen>
#include <common_lib.h>
#include <pcl/common/io.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <condition_variable>
#include <nav_msgs/Odometry.h>
#include <pcl/common/transforms.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <tf/transform_broadcaster.h>
#include <eigen_conversions/eigen_msg.h>
#include <pcl_conversions/pcl_conversions.h>
#include <sensor_msgs/Imu.h>
#include <sensor_msgs/PointCloud2.h>
#include <geometry_msgs/Vector3.h>
#include "use-ikfom.hpp"
/// *************Preconfiguration
/**
* 定义 MAX_INI_COUNT 常量为 20
*/
#define MAX_INI_COUNT (20)
/**
* 比较两个 PointType 类型的对象的曲率值,按升序排列
* @param x PointType 类型的对象
* @param y PointType 类型的对象
* @return 返回比较结果,如果 x 的曲率小于 y 的曲率,返回 true,否则返回 false
*/
const
bool
time_list
(
PointType
&
x
,
PointType
&
y
)
{
return
(
x
.
curvature
<
y
.
curvature
);};
/// *************IMU Process and undistortion
class
ImuProcess
{
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
ImuProcess
();
// 构造函数
~
ImuProcess
();
// 析构函数
void
Reset
();
// 复位函数
void
Reset
(
double
start_timestamp
,
const
sensor_msgs
::
ImuConstPtr
&
lastimu
);
// 复位函数,带有起始时间戳和上一个IMU的指针参数
void
set_extrinsic
(
const
V3D
&
transl
,
const
M3D
&
rot
);
// 设置外参,包括平移和旋转矩阵
void
set_extrinsic
(
const
V3D
&
transl
);
// 设置外参,仅包括平移
void
set_extrinsic
(
const
MD
(
4
,
4
)
&
T
);
// 设置外参,包括齐次变换矩阵
void
set_gyr_cov
(
const
V3D
&
scaler
);
// 设置角速度测量噪声的方差
void
set_acc_cov
(
const
V3D
&
scaler
);
// 设置加速度测量噪声的方差
void
set_gyr_bias_cov
(
const
V3D
&
b_g
);
// 设置角速度Bias噪声的方差
void
set_acc_bias_cov
(
const
V3D
&
b_a
);
// 设置加速度Bias噪声的方差
Eigen
::
Matrix
<
double
,
12
,
12
>
Q
;
// 12x12的协方差矩阵
void
Process
(
const
MeasureGroup
&
meas
,
esekfom
::
esekf
<
state_ikfom
,
12
,
input_ikfom
>
&
kf_state
,
PointCloudXYZI
::
Ptr
pcl_un_
);
// IMU处理函数
ofstream
fout_imu
;
// 输出文件流
V3D
cov_acc
;
// 加速度噪声方差
V3D
cov_gyr
;
// 角速度噪声方差
V3D
cov_acc_scale
;
// 加速度scale噪声方差
V3D
cov_gyr_scale
;
// 角速度scale噪声方差
V3D
cov_bias_gyr
;
// 角速度Bias噪声方差
V3D
cov_bias_acc
;
// 加速度Bias噪声方差
double
first_lidar_time
;
// 第一个激光雷达时间戳
// 声明私有成员函数
private:
// IMU初始化函数
void
IMU_init
(
const
MeasureGroup
&
meas
,
esekfom
::
esekf
<
state_ikfom
,
12
,
input_ikfom
>
&
kf_state
,
int
&
N
);
// 去除点云畸变函数
void
UndistortPcl
(
const
MeasureGroup
&
meas
,
esekfom
::
esekf
<
state_ikfom
,
12
,
input_ikfom
>
&
kf_state
,
PointCloudXYZI
&
pcl_in_out
);
// 点云数据成员指针
PointCloudXYZI
::
Ptr
cur_pcl_un_
;
// 上一个IMU数据成员指针
sensor_msgs
::
ImuConstPtr
last_imu_
;
// IMU数据成员双端队列
deque
<
sensor_msgs
::
ImuConstPtr
>
v_imu_
;
// IMU位姿数据存储向量
vector
<
Pose6D
>
IMUpose
;
// 点云旋转矩阵存储向量
vector
<
M3D
>
v_rot_pcl_
;
// 相对于IMU坐标系的激光雷达旋转矩阵
M3D
Lidar_R_wrt_IMU
;
// 相对于IMU坐标系的激光雷达位置向量
V3D
Lidar_T_wrt_IMU
;
// IMU加速度均值向量
V3D
mean_acc
;
// IMU陀螺仪均值向量
V3D
mean_gyr
;
// 上一个时刻的角速度向量
V3D
angvel_last
;
// 上一个时刻的加速度向量
V3D
acc_s_last
;
// 初始时间戳
double
start_timestamp_
;
// 上一个激光雷达结束时间
double
last_lidar_end_time_
;
// 初始化迭代次数
int
init_iter_num
=
1
;
// 是否是第一帧数据
bool
b_first_frame_
=
true
;
// 是否需要IMU初始化
bool
imu_need_init_
=
true
;
};
ImuProcess
::
ImuProcess
()
//定义ImuProcess的构造函数
:
b_first_frame_
(
true
),
imu_need_init_
(
true
),
start_timestamp_
(
-
1
)
//成员变量初始化
{
init_iter_num
=
1
;
//初始化参数
Q
=
process_noise_cov
();
//初始化噪声矩阵Q
cov_acc
=
V3D
(
0.1
,
0.1
,
0.1
);
//初始化加速度计噪声协方差
cov_gyr
=
V3D
(
0.1
,
0.1
,
0.1
);
//初始化陀螺仪噪声协方差
cov_bias_gyr
=
V3D
(
0.0001
,
0.0001
,
0.0001
);
//初始化陀螺仪偏置噪声协方差
cov_bias_acc
=
V3D
(
0.0001
,
0.0001
,
0.0001
);
//初始化加速度计偏置噪声协方差
mean_acc
=
V3D
(
0
,
0
,
-
1.0
);
//初始化加速度计均值
mean_gyr
=
V3D
(
0
,
0
,
0
);
//初始化陀螺仪均值
angvel_last
=
Zero3d
;
//设置上一个角速度为零向量
Lidar_T_wrt_IMU
=
Zero3d
;
//设置Lidar_T_wrt_IMU为零向量
Lidar_R_wrt_IMU
=
Eye3d
;
//设置Lidar_R_wrt_IMU为单位矩阵
last_imu_
.
reset
(
new
sensor_msgs
::
Imu
());
//将last_imu_初始化为一个新的sensor_msgs::Imu指针
}
ImuProcess
::~
ImuProcess
()
{}
//析构函数,释放对象时调用
void
ImuProcess
::
Reset
()
{
// ROS_WARN("Reset ImuProcess"); //注释掉的代码,不会被执行,ROS_WARN是输出警告信息的函数
mean_acc
=
V3D
(
0
,
0
,
-
1.0
);
//将均值加速度重置为(0, 0, -1.0)
mean_gyr
=
V3D
(
0
,
0
,
0
);
//将均值陀螺仪重置为(0, 0, 0)
angvel_last
=
Zero3d
;
//将上一个角速度值重置为0
imu_need_init_
=
true
;
//需要初始化IMU
start_timestamp_
=
-
1
;
//起始时间戳为-1
init_iter_num
=
1
;
//初始化迭代次数为1
v_imu_
.
clear
();
//清空imu数据
IMUpose
.
clear
();
//清空imu位姿数据
last_imu_
.
reset
(
new
sensor_msgs
::
Imu
());
//将上一个IMU数据重置为新的sensor_msgs::Imu对象
cur_pcl_un_
.
reset
(
new
PointCloudXYZI
());
//将当前点云数据重置为新的PointCloudXYZI对象
}
void
ImuProcess
::
set_extrinsic
(
const
MD
(
4
,
4
)
&
T
)
{
// 设置激光雷达相对于IMU的平移向量
Lidar_T_wrt_IMU
=
T
.
block
<
3
,
1
>
(
0
,
3
);
// 设置激光雷达相对于IMU的旋转矩阵
Lidar_R_wrt_IMU
=
T
.
block
<
3
,
3
>
(
0
,
0
);
}
void
ImuProcess
::
set_extrinsic
(
const
V3D
&
transl
)
{
// 设置激光雷达相对于IMU的平移向量
Lidar_T_wrt_IMU
=
transl
;
// 设置激光雷达相对于IMU的旋转矩阵为单位矩阵
Lidar_R_wrt_IMU
.
setIdentity
();
}
void
ImuProcess
::
set_extrinsic
(
const
V3D
&
transl
,
const
M3D
&
rot
)
{
// 设置激光雷达相对于IMU的平移向量
Lidar_T_wrt_IMU
=
transl
;
// 设置激光雷达相对于IMU的旋转矩阵
Lidar_R_wrt_IMU
=
rot
;
}
void
ImuProcess
::
set_gyr_cov
(
const
V3D
&
scaler
)
{
// 设置陀螺仪的测量噪声方差
cov_gyr_scale
=
scaler
;
}
// 设置加速度计噪声协方差矩阵的比例因子
void
ImuProcess
::
set_acc_cov
(
const
V3D
&
scaler
)
{
cov_acc_scale
=
scaler
;
}
// 设置陀螺仪零偏噪声协方差矩阵
void
ImuProcess
::
set_gyr_bias_cov
(
const
V3D
&
b_g
)
{
cov_bias_gyr
=
b_g
;
}
// 设置加速度计零偏噪声协方差矩阵
void
ImuProcess
::
set_acc_bias_cov
(
const
V3D
&
b_a
)
{
cov_bias_acc
=
b_a
;
}
// IMU初始化函数
void
ImuProcess
::
IMU_init
(
const
MeasureGroup
&
meas
,
esekfom
::
esekf
<
state_ikfom
,
12
,
input_ikfom
>
&
kf_state
,
int
&
N
)
{
/** 1. initializing the gravity, gyro bias, acc and gyro covariance
** 2. normalize the acceleration measurenments to unit gravity **/
V3D
cur_acc
,
cur_gyr
;
if
(
b_first_frame_
)
// 如果是第一帧数据
{
// 进行初始化
Reset
();
// 重置一些变量
N
=
1
;
// 记录数据数量
b_first_frame_
=
false
;
// 标记为不是第一帧数据
const
auto
&
imu_acc
=
meas
.
imu
.
front
()
->
linear_acceleration
;
// 获取第一帧imu数据的加速度
const
auto
&
gyr_acc
=
meas
.
imu
.
front
()
->
angular_velocity
;
// 获取第一帧imu数据的角速度
mean_acc
<<
imu_acc
.
x
,
imu_acc
.
y
,
imu_acc
.
z
;
// 计算加速度均值
mean_gyr
<<
gyr_acc
.
x
,
gyr_acc
.
y
,
gyr_acc
.
z
;
// 计算角速度均值
first_lidar_time
=
meas
.
lidar_beg_time
;
// 记录第一帧激光雷达数据的时间戳
}
// 循环遍历测量中的每个IMU数据
for
(
const
auto
&
imu
:
meas
.
imu
)
{
// 从IMU数据中提取线性加速度和角速度
const
auto
&
imu_acc
=
imu
->
linear_acceleration
;
const
auto
&
gyr_acc
=
imu
->
angular_velocity
;
// 将当前加速度和角速度保存在Eigen向量中
cur_acc
<<
imu_acc
.
x
,
imu_acc
.
y
,
imu_acc
.
z
;
cur_gyr
<<
gyr_acc
.
x
,
gyr_acc
.
y
,
gyr_acc
.
z
;
// 计算平均加速度和角速度
mean_acc
=
(
cur_acc
-
mean_acc
)
/
N
;
mean_gyr
=
(
cur_gyr
-
mean_gyr
)
/
N
;
// 计算加速度和角速度的协方差矩阵
cov_acc
=
cov_acc
*
(
N
-
1.0
)
/
N
(
cur_acc
-
mean_acc
).
cwiseProduct
(
cur_acc
-
mean_acc
)
*
(
N
-
1.0
)
/
(
N
*
N
);
cov_gyr
=
cov_gyr
*
(
N
-
1.0
)
/
N
(
cur_gyr
-
mean_gyr
).
cwiseProduct
(
cur_gyr
-
mean_gyr
)
*
(
N
-
1.0
)
/
(
N
*
N
);
// 增加已处理的测量数量
N
++
;
}
// 从卡尔曼滤波器状态获取初始化状态
state_ikfom
init_state
=
kf_state
.
get_x
();
// 根据平均加速度计算重力向量并设置为初始化状态
init_state
.
grav
=
S2
(
-
mean_acc
/
mean_acc
.
norm
()
*
G_m_s2
);
// 获取当前IMU的方向
const
auto
&
orientation
=
meas
.
imu
.
front
()
->
orientation
;
// 如果orientation消息可用,则使用imu的orientation作为初始姿态
if
(
orientation
.
x
!=
0
||
orientation
.
y
!=
0
||
orientation
.
z
!=
0
||
orientation
.
w
!=
0
){
Eigen
::
Quaterniond
qua
(
orientation
.
w
,
orientation
.
x
,
orientation
.
y
,
orientation
.
z
);
init_state
.
rot
=
qua
;
}
}
// 初始化状态量
//state_inout.rot = Eye3d; // Exp(mean_acc.cross(V3D(0, 0, -1 / scale_gravity)));
// 上述语句是注释掉的,意为旋转状态初始化为单位矩阵,即不旋转,但后续也没有使用到,因此可以忽略
init_state
.
bg
=
mean_gyr
;
// 陀螺仪偏置初始化为零偏
init_state
.
offset_T_L_I
=
Lidar_T_wrt_IMU
;
// 激光雷达相对IMU的位置初始化
init_state
.
offset_R_L_I
=
Lidar_R_wrt_IMU
;
// 激光雷达相对IMU的旋转初始化
kf_state
.
change_x
(
init_state
);
// 更新KF的状态量
// 初始化协方差矩阵
esekfom
::
esekf
<
state_ikfom
,
12
,
input_ikfom
>::
cov
init_P
=
kf_state
.
get_P
();
// 获取KF的协方差矩阵
init_P
.
setIdentity
();
// 设置为对角线全为1的单位矩阵
init_P
(
6
,
6
)
=
init_P
(
7
,
7
)
=
init_P
(
8
,
8
)
=
0.00001
;
// 陀螺仪偏置协方差初始化为0.00001
init_P
(
9
,
9
)
=
init_P
(
10
,
10
)
=
init_P
(
11
,
11
)
=
0.00001
;
// 激光雷达位置偏差协方差初始化为0.00001
init_P
(
15
,
15
)
=
init_P
(
16
,
16
)
=
init_P
(
17
,
17
)
=
0.0001
;
// 速度偏差协方差初始化为0.0001
init_P
(
18
,
18
)
=
init_P
(
19
,
19
)
=
init_P
(
20
,
20
)
=
0.001
;
// 位置偏差协方差初始化为0.001
init_P
(
21
,
21
)
=
init_P
(
22
,
22
)
=
0.00001
;
// 重力加速度偏差协方差初始化为0.00001
kf_state
.
change_P
(
init_P
);
// 更新KF的协方差矩阵
last_imu_
=
meas
.
imu
.
back
();
// 记录最后一个IMU数据,用于后续计算时间间隔
}
// 通过对接收到的IMU和当前EKF状态信息来去畸变点云数据。
void
ImuProcess
::
UndistortPcl
(
const
MeasureGroup
&
meas
,
esekfom
::
esekf
<
state_ikfom
,
12
,
input_ikfom
>
&
kf_state
,
PointCloudXYZI
&
pcl_out
)
{
// 将上一帧IMU数据的尾部添加到当前帧IMU数据的头部。
auto
v_imu
=
meas
.
imu
;
v_imu
.
push_front
(
last_imu_
);
// 获取IMU和点云数据的时间戳
const
double
&
imu_beg_time
=
v_imu
.
front
()
->
header
.
stamp
.
toSec
();
const
double
&
imu_end_time
=
v_imu
.
back
()
->
header
.
stamp
.
toSec
();
const
double
&
pcl_beg_time
=
meas
.
lidar_beg_time
;
// 根据时间排序点云数据
pcl_out
=
*
(
meas
.
lidar
);
sort
(
pcl_out
.
points
.
begin
(),
pcl_out
.
points
.
end
(),
time_list
);
const
double
&
pcl_end_time
=
pcl_beg_time
+
pcl_out
.
points
.
back
().
curvature
/
double
(
1000
);
}
// cout<<"[ IMU Process ]: Process lidar from "<<pcl_beg_time<<" to "<<pcl_end_time<<", " \
// <<meas.imu.size()<<" imu msgs from "<<imu_beg_time<<" to "<<imu_end_time<<endl;
/*** Initialize IMU pose ***/
state_ikfom
imu_state
=
kf_state
.
get_x
();
IMUpose
.
clear
();
IMUpose
.
push_back
(
set_pose6d
(
0.0
,
acc_s_last
,
angvel_last
,
imu_state
.
vel
,
imu_state
.
pos
,
imu_state
.
rot
.
toRotationMatrix
()));
/*** forward propagation at each imu point ***/
V3D
angvel_avr
,
acc_avr
,
acc_imu
,
vel_imu
,
pos_imu
;
M3D
R_imu
;
double
dt
=
0
;
input_ikfom
in
;
for
(
auto
it_imu
=
v_imu
.
begin
();
it_imu
<
(
v_imu
.
end
()
-
1
);
it_imu
++
)
{
auto
&&
head
=
*
(
it_imu
);
auto
&&
tail
=
*
(
it_imu
+
1
);
if
(
tail
->
header
.
stamp
.
toSec
()
<
last_lidar_end_time_
)
continue
;
angvel_avr
<<
0.5
*
(
head
->
angular_velocity
.
x
+
tail
->
angular_velocity
.
x
),
0.5
*
(
head
->
angular_velocity
.
y
+
tail
->
angular_velocity
.
y
),
0.5
*
(
head
->
angular_velocity
.
z
+
tail
->
angular_velocity
.
z
);
acc_avr
<<
0.5
*
(
head
->
linear_acceleration
.
x
+
tail
->
linear_acceleration
.
x
),
0.5
*
(
head
->
linear_acceleration
.
y
+
tail
->
linear_acceleration
.
y
),
0.5
*
(
head
->
linear_acceleration
.
z
+
tail
->
linear_acceleration
.
z
);
fout_imu
<<
setw
(
10
)
<<
head
->
header
.
stamp
.
toSec
()
-
first_lidar_time
<<
" "
<<
angvel_avr
.
transpose
()
<<
" "
<<
acc_avr
.
transpose
()
<<
endl
;
acc_avr
=
acc_avr
*
G_m_s2
/
mean_acc
.
norm
();
// - state_inout.ba;
if
(
head
->
header
.
stamp
.
toSec
()
<
last_lidar_end_time_
)
{
dt
=
tail
->
header
.
stamp
.
toSec
()
-
last_lidar_end_time_
;
// dt = tail->header.stamp.toSec() - pcl_beg_time;
}
else
{
dt
=
tail
->
header
.
stamp
.
toSec
()
-
head
->
header
.
stamp
.
toSec
();
}
// 将平均角速度以及平均加速度设置为Kalman滤波器的输入
in
.
acc
=
acc_avr
;
in
.
gyro
=
angvel_avr
;
// 将协方差矩阵的对角线设置为陀螺仪和加速度计的方差,以及陀螺仪偏移和加速度计偏移的方差
Q
.
block
<
3
,
3
>
(
0
,
0
).
diagonal
()
=
cov_gyr
;
Q
.
block
<
3
,
3
>
(
3
,
3
).
diagonal
()
=
cov_acc
;
Q
.
block
<
3
,
3
>
(
6
,
6
).
diagonal
()
=
cov_bias_gyr
;
Q
.
block
<
3
,
3
>
(
9
,
9
).
diagonal
()
=
cov_bias_acc
;
// 使用Kalman滤波器的predict函数进行预测,传入时间步长dt,协方差矩阵Q以及输入in
kf_state
.
predict
(
dt
,
Q
,
in
);
/* save the poses at each IMU measurements */
// 获取当前时刻的KF状态量
imu_state
=
kf_state
.
get_x
();
// 计算当前时刻的角速度
angvel_last
=
angvel_avr
-
imu_state
.
bg
;
// 计算当前时刻的加速度
acc_s_last
=
imu_state
.
rot
*
(
acc_avr
-
imu_state
.
ba
);
// 将加速度调整到重力加速度方向
for
(
int
i
=
0
;
i
<
3
;
i
++
)
{
acc_s_last
[
i
]
=
imu_state
.
grav
[
i
];
}
// 计算时间偏移量、更新IMUpose向量
double
&&
offs_t
=
tail
->
header
.
stamp
.
toSec
()
-
pcl_beg_time
;
IMUpose
.
push_back
(
set_pose6d
(
offs_t
,
acc_s_last
,
angvel_last
,
imu_state
.
vel
,
imu_state
.
pos
,
imu_state
.
rot
.
toRotationMatrix
()));
/*** calculated the pos and attitude prediction at the frame-end ***/
// 确定note,基于pcl_end_time和imu_end_time的比较。note用于决定在预测步骤中加或减pcl_end_time和imu_end_time之间的时间差
double
note
=
pcl_end_time
>
imu_end_time
?
1.0
:
-
1.0
;
// 计算pcl_end_time和imu_end_time之间的时间差
dt
=
note
*
(
pcl_end_time
-
imu_end_time
);
// 使用时间差和输入噪声协方差矩阵预测卡尔曼滤波器的状态
kf_state
.
predict
(
dt
,
Q
,
in
);
// 更新imu_state为当前卡尔曼滤波器的状态
imu_state
=
kf_state
.
get_x
();
// 存储最后一个IMU测量值
last_imu_
=
meas
.
imu
.
back
();
// 存储点云的最后一个时间戳
last_lidar_end_time_
=
pcl_end_time
;
/*** 反向传播,使每个激光点失真 ***/
// 初始化迭代器到最后一个IMU位姿和点云中的最后一个点
auto
it_pcl
=
pcl_out
.
points
.
end
()
-
1
;
for
(
auto
it_kp
=
IMUpose
.
end
()
-
1
;
it_kp
!=
IMUpose
.
begin
();
it_kp
--
)
{
// 将head设置为it_kp之前的IMU位姿,将tail设置为it_kp
auto
head
=
it_kp
-
1
;
auto
tail
=
it_kp
;
// 从head IMU位姿中提取旋转矩阵
R_imu
<<
MAT_FROM_ARRAY
(
head
->
rot
);
// 从head IMU位姿中提取速度向量
vel_imu
<<
VEC_FROM_ARRAY
(
head
->
vel
);
// 从head IMU位姿中提取位置向量
pos_imu
<<
VEC_FROM_ARRAY
(
head
->
pos
);
// 从tail IMU位姿中提取加速度向量
acc_imu
<<
VEC_FROM_ARRAY
(
tail
->
acc
);
// 从tail IMU位姿中提取角速度向量
angvel_avr
<<
VEC_FROM_ARRAY
(
tail
->
gyr
);
// 遍历所有曲率大于head IMU位姿的偏移时间的激光点
for
(;
it_pcl
->
curvature
/
double
(
1000
)
>
head
->
offset_time
;
it_pcl
--
)
{
// 计算head IMU位姿和激光点之间的时间差
dt
=
it_pcl
->
curvature
/
double
(
1000
)
-
head
->
offset_time
;
/* Transform to the 'end' frame, using only the rotation
* Note: Compensation direction is INVERSE of Frame's moving direction
* So if we want to compensate a point at timestamp-i to the frame-e
* P_compensate = R_imu_e ^ T * (R_i * P_i + T_ei) where T_ei is represented in global frame */
M3D
R_i
(
R_imu
*
Exp
(
angvel_avr
,
dt
));
V3D
P_i
(
it_pcl
->
x
,
it_pcl
->
y
,
it_pcl
->
z
);
V3D
T_ei
(
pos_imu
+
vel_imu
*
dt
+
0.5
*
acc_imu
*
dt
*
dt
-
imu_state
.
pos
);
V3D
P_compensate
=
imu_state
.
offset_R_L_I
.
conjugate
()
*
(
imu_state
.
rot
.
conjugate
()
*
(
R_i
*
(
imu_state
.
offset_R_L_I
*
P_i
+
imu_state
.
offset_T_L_I
)
+
T_ei
)
-
imu_state
.
offset_T_L_I
);
// not accurate!
// save Undistorted points and their rotation
it_pcl
->
x
=
P_compensate
(
0
);
it_pcl
->
y
=
P_compensate
(
1
);
it_pcl
->
z
=
P_compensate
(
2
);
if
(
it_pcl
==
pcl_out
.
points
.
begin
())
break
;
}
}
}
void
ImuProcess
::
Process
(
const
MeasureGroup
&
meas
,
esekfom
::
esekf
<
state_ikfom
,
12
,
input_ikfom
>
&
kf_state
,
PointCloudXYZI
::
Ptr
cur_pcl_un_
)
{
double
t1
,
t2
,
t3
;
t1
=
omp_get_wtime
();
if
(
meas
.
imu
.
empty
())
{
return
;};
ROS_ASSERT
(
meas
.
lidar
!=
nullptr
);
if
(
imu_need_init_
)
{
/// The very first lidar frame
IMU_init
(
meas
,
kf_state
,
init_iter_num
);
imu_need_init_
=
true
;
last_imu_
=
meas
.
imu
.
back
();
state_ikfom
imu_state
=
kf_state
.
get_x
();
if
(
init_iter_num
>
MAX_INI_COUNT
)
{
cov_acc
*=
pow
(
G_m_s2
/
mean_acc
.
norm
(),
2
);
imu_need_init_
=
false
;
cov_acc
=
cov_acc_scale
;
cov_gyr
=
cov_gyr_scale
;
ROS_INFO
(
"IMU Initial Done"
);
// ROS_INFO("IMU Initial Done: Gravity: %.4f %.4f %.4f %.4f; state.bias_g: %.4f %.4f %.4f; acc covarience: %.8f %.8f %.8f; gry covarience: %.8f %.8f %.8f",\
// imu_state.grav[0], imu_state.grav[1], imu_state.grav[2], mean_acc.norm(), cov_bias_gyr[0], cov_bias_gyr[1], cov_bias_gyr[2], cov_acc[0], cov_acc[1], cov_acc[2], cov_gyr[0], cov_gyr[1], cov_gyr[2]);
fout_imu
.
open
(
DEBUG_FILE_DIR
(
"imu.txt"
),
ios
::
out
);
}
return
;
}
UndistortPcl
(
meas
,
kf_state
,
*
cur_pcl_un_
);
t2
=
omp_get_wtime
();
t3
=
omp_get_wtime
();
// cout<<"[ IMU Process ]: Time: "<<t3 - t1<<endl;
}
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录