mirror of
https://github.com/HViktorTsoi/FAST_LIO_LOCALIZATION.git
synced 2023-05-28 12:51:38 +08:00
151 lines
4.8 KiB
Python
151 lines
4.8 KiB
Python
# coding=utf8
|
|
# !/usr/bin/env python2
|
|
from __future__ import print_function, division, absolute_import
|
|
|
|
import copy
|
|
import thread
|
|
import time
|
|
|
|
import open3d as o3d
|
|
import rospy
|
|
import ros_numpy
|
|
from geometry_msgs.msg import PoseWithCovarianceStamped
|
|
from sensor_msgs.msg import PointCloud2
|
|
import numpy as np
|
|
import tf
|
|
import tf.transformations
|
|
import multiprocessing
|
|
|
|
global_map = o3d.geometry.PointCloud()
|
|
initialized = False
|
|
T_map_to_odom = np.eye(4)
|
|
cur_scan = o3d.geometry.PointCloud()
|
|
|
|
|
|
def msg_to_array(pc_msg):
|
|
pc_array = ros_numpy.numpify(pc_msg)
|
|
pc = np.zeros([len(pc_array), 3])
|
|
pc[:, 0] = pc_array['x']
|
|
pc[:, 1] = pc_array['y']
|
|
pc[:, 2] = pc_array['z']
|
|
return pc
|
|
|
|
|
|
def publish_tf():
|
|
br = tf.TransformBroadcaster()
|
|
while True:
|
|
rospy.sleep(0.01)
|
|
br.sendTransform(tf.transformations.translation_from_matrix(T_map_to_odom),
|
|
tf.transformations.quaternion_from_matrix(T_map_to_odom),
|
|
rospy.Time.now(),
|
|
'camera_init', 'map')
|
|
|
|
|
|
def global_localization(odometry_pose):
|
|
global global_map, cur_scan, T_map_to_odom
|
|
# 用icp配准
|
|
# print(global_map, cur_scan, T_map_to_odom)
|
|
rospy.loginfo('scan to map matching......')
|
|
|
|
# TODO 这里注意线程安全
|
|
# 估计法线
|
|
scan_tobe_mapped = copy.copy(cur_scan)
|
|
scan_tobe_mapped.estimate_normals(o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30))
|
|
scan_tobe_mapped = scan_tobe_mapped.voxel_down_sample(SCAN_VOXEL_SIZE)
|
|
|
|
tic = time.time()
|
|
# 粗配准
|
|
icp_coarse = o3d.registration.registration_icp(
|
|
scan_tobe_mapped.voxel_down_sample(SCAN_VOXEL_SIZE * 5), global_map.voxel_down_sample(MAP_VOXEL_SIZE * 5),
|
|
MAP_VOXEL_SIZE * 5, odometry_pose,
|
|
o3d.registration.TransformationEstimationPointToPoint())
|
|
# 配准
|
|
icp_fine = o3d.registration.registration_icp(
|
|
scan_tobe_mapped, global_map,
|
|
MAP_VOXEL_SIZE, icp_coarse.transformation,
|
|
o3d.registration.TransformationEstimationPointToPoint())
|
|
print(icp_fine)
|
|
|
|
toc = time.time()
|
|
rospy.loginfo('Time: {}'.format(toc - tic))
|
|
|
|
# 当全局定位成功时才更新map2odom
|
|
if icp_fine.fitness > 0.9:
|
|
T_map_to_odom = icp_fine.transformation
|
|
return True
|
|
else:
|
|
rospy.logwarn('Not match!!!!')
|
|
return False
|
|
|
|
|
|
def initialize_global_map(pc_msg):
|
|
global global_map
|
|
global_map.points = o3d.utility.Vector3dVector(msg_to_array(pc_msg))
|
|
global_map.estimate_normals(o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30))
|
|
global_map.voxel_down_sample(MAP_VOXEL_SIZE)
|
|
rospy.loginfo('Global map received.')
|
|
|
|
|
|
def cb_save_cur_scan(pc_msg):
|
|
global cur_scan
|
|
# 注意这里fastlio直接将scan转到odom系下了 不是lidar局部系
|
|
pc_msg.header.frame_id = 'camera_init'
|
|
pc_msg.header.stamp = rospy.Time().now()
|
|
pub_pc_in_map.publish(pc_msg)
|
|
|
|
# 转换为pcd
|
|
# 处理一下
|
|
pc_msg.fields = [pc_msg.fields[0], pc_msg.fields[1], pc_msg.fields[2],
|
|
pc_msg.fields[4], pc_msg.fields[5], pc_msg.fields[6],
|
|
pc_msg.fields[3], pc_msg.fields[7]]
|
|
pc = msg_to_array(pc_msg)
|
|
cur_scan.points = o3d.utility.Vector3dVector(pc[:, :3])
|
|
|
|
|
|
def thread_localization():
|
|
global T_map_to_odom
|
|
while True:
|
|
# 每隔一段时间进行全局定位
|
|
rospy.sleep(2)
|
|
# TODO 由于这里Fast lio发布的scan是已经转换到odom系下了 所以每次全局定位的初始解就是上一次的map2odom 不需要再拿odom了
|
|
global_localization(T_map_to_odom)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
MAP_VOXEL_SIZE = 0.4
|
|
SCAN_VOXEL_SIZE = 0.1
|
|
|
|
rospy.init_node('fast_lio_localization')
|
|
rospy.loginfo('Localization Node Inited...')
|
|
|
|
# 发布定位消息
|
|
thread.start_new_thread(publish_tf, ())
|
|
|
|
# publisher
|
|
pub_pc_in_map = rospy.Publisher('/cur_scan_in_map', PointCloud2, queue_size=1)
|
|
# rospy.Subscriber('/livox/lidar/pc2', PointCloud2, cb_save_cur_scan, queue_size=1)
|
|
rospy.Subscriber('/cloud_registered', PointCloud2, cb_save_cur_scan, queue_size=1)
|
|
|
|
# 初始化全局地图
|
|
rospy.loginfo('Waiting for global map')
|
|
initialize_global_map(rospy.wait_for_message('/map', PointCloud2))
|
|
|
|
# 初始化
|
|
while not initialized:
|
|
rospy.loginfo('Waiting for initial pose')
|
|
|
|
# 等待初始位姿
|
|
pose_msg = rospy.wait_for_message('/initialpose', PoseWithCovarianceStamped)
|
|
initial_pose = np.matmul(
|
|
tf.listener.xyz_to_mat44(pose_msg.pose.pose.position),
|
|
tf.listener.xyzw_to_mat44(pose_msg.pose.pose.orientation),
|
|
)
|
|
initialized = global_localization(initial_pose)
|
|
|
|
rospy.loginfo('Initialized successfully!!!!!!')
|
|
# 开始定期全局定位
|
|
thread.start_new_thread(thread_localization, ())
|
|
# multiprocessing.Process(target=thread_localization, args=()).start()
|
|
|
|
rospy.spin()
|