前言

览沃科技有限公司(Livox)成立于2016年。为了革新激光雷达行业,Livox致力于提供高性能、低成本的激光雷达传感器。通过降低使用门槛和生产成本,Livox将激光雷达技术集成到更多产品和应用之中,从而为自动驾驶、智慧城市、测绘、移动机器人等行业带来创新性改变。Livox产品已销往包括美国、加拿大、中国、日本和欧盟在内的 26 个国家和地区。

面向智能移动机器人市场,Livox 推出最新一代 3D 激光雷达 Mid-360,开启混合固态激光雷达 360° 立体感知新篇章。凭借小巧体积,Mid-360 的安装布置更加灵活。同时,Mid-360 充分考虑了移动机器人对导航、避障等升维感知的需求,兼容室内外场景,赋能移动机器人进入空间智能感知新时代。 但是这款产品官网一直购买不到, 本篇博客 在 gazebo 中 可实现对该激光雷达的仿真 。

传统激光雷达普遍采用机械扫描方式,扫描路径随时间重复。而Livox 激光雷达采用了独特的扫描⽅式,扫描路径不会重复。在非重复扫描方式中,视场中被激光照射到的区域面积会随时间增大,这意味着视场覆盖率随时间推移而显著提高,可减小视场内物体被漏检的概率,有助于探测视场中的更多细节。

下图中给出了一个直观的例子。(a)图由于采用了非重复扫描方式,随着时间的积累,视场覆盖率逐渐升高。而(b)图,由于扫描每次都是重复的,视场覆盖率几乎没有提升。

gazebo中自带的laser插件是上图中b的方式,并不能代表livox的这种雷达。

livox 的仿真功能包(livox_laser_simulation)中 提供了 多种半固体的雷达仿真模型,可以在gazebo中进行该雷达的仿真与其扫描方式(非重复式扫描)点云发布。

但是经过测试发现 livox_laser_simulation 雷达仿真功能包发布的是 ros PointCloud2 点云格式数据。

而现在针对livox雷达的激光slam算法用的是 livox CustomMsg 点云格式数据,例如 Fast-Lio2 、Faster-Lio 、Lio-livox等。所以想在gazebo中仿真这些算法,需要将 ros PointCloud2格式数据转为livox CustomMsg格式 。

点云格式

PointCloud2 点云格式

PointCloud2 是ros的一种点云格式

具体官方数据 http://docs.ros.org/en/jade/api/sensor_msgs/html/msg/PointCloud2.html

std_msgs/Header header
  uint32 seq
  time stamp
  string frame_id
uint32 height
uint32 width
sensor_msgs/PointField[] fields
  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
  uint32 offset
  uint8 datatype
  uint32 count
bool is_bigendian
uint32 point_step
uint32 row_step
uint8[] data
bool is_dense

一个仿真的点云消息长这样

header:  // 点云的头信息
  seq: 963 //
  stamp:  // 时间戳
    secs: 1541143772
    nsecs: 912011000
  frame_id: "/camera_init"
height: 1   // If the cloud is unordered, height is 1  如果cloud 是无序的 height 是 1
width: 852578  //点云的长度
fields:  //  sensor_msgs/PointField[] fields 
  - 
    name: "x"
    offset: 0
    datatype: 7     //     uint8 INT8    = 1
            //    uint8 UINT8   = 2
            //    uint8 INT16   = 3
            //    uint8 UINT16  = 4
            //    uint8 INT32   = 5
            //    uint8 UINT32  = 6
            //    uint8 FLOAT32 = 7
            //    uint8 FLOAT64 = 8
    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
is_bigendian: False
point_step: 32 // Length of a point in bytes 一个点占的比特数 
row_step: 27282496 // Length of a row in bytes 一行的长度占用的比特数
data: [ .......................................................... ] //  Actual point data, size is (row_step*height)
is_dense: True // 没有非法数据点

livox CustomMsg 点云格式

CustomMsg 是 livox雷达的 专有的点云格式,其格式如下:

Header header             # ROS standard message header
uint64 timebase          # The time of first point
uint32 point_num      # Total number of pointclouds
uint8 lidar_id               # Lidar device id number
uint8[3] rsvd                 # Reserved use
CustomPoint[] points    # Pointcloud data

上述自定义数据包中的自定义点云(CustomPoint)格式 :

uint32 offset_time      # offset time relative to the base time
float32 x               # X axis, unit:m
float32 y               # Y axis, unit:m
float32 z               # Z axis, unit:m
uint8 reflectivity      # reflectivity, 0~255
uint8 tag               # livox tag
uint8 line              # laser number in lidar
  • line
    最后一行line是激光雷达扫描的线数(livox avia为6线),从原始扫描点云可以看出,后期可以按线来对原始点处理、筛选
  • tag
    tag: 主要指示多回波信息及噪点信息。是一个二进制表达,标记信息的格式如下:bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0

    bit7 bit6 保留位

    bit5 bit4 回波序号 : 00: 第 0 个回波 01: 第 1 个回波 10: 第 2 个回波 11: 第 3 个回波 由于Livox Avia 采用同轴光路,即使外部无被测物体,其内部的光学系统也会产生一个回波,该回波记为第 0个回波。随后,若激光出射方向存在可被探测的物体,则最先返回系统的激光回波记为第 1 个回波,随后为第 2个回波,以此类推。如果被探测物体距离过近(例如 1.5 m),第 1 个回波将会融合到第 0 个回波里,该回波记为第 0 个回波。

    bit3 bit2 基于强度的点属性: 00:正常点 01:回波能量噪点置信度高 10:回波能量噪点置信度中 11:保留位
    基于回波能量强度判断采样点是否为噪点。通常情况下,激光光束受到类似灰尘、雨雾、雪等干扰产生的噪点的回波能量很小。目前按照回波能量强度大小将噪点置信度分为二档:01表示回波能量很弱,这类采样点有较高概率为噪点,例如灰尘点;10表示回波能量中等,该类采样点有中等概率为噪点,例如雨雾噪点。噪点置信度越低,说明该点是噪点的可能性越低。

    bit1 bit0 基于空间位置的点属性 : 00:正常点 01:空间噪点置信度高 10:空间噪点置信度中 11:空间噪点置信度低
    基于采样点的空间位置判断是否为噪点。例如,激光探测测距仪在测量前后两个距离十分相近的物体时,两个物体之间可能会产生拉丝状的噪点。目前按照不同的噪点置信度分为三档,噪点置信度越低,说明该点是噪点的可能性越低。

  • offset_time
    时间戳
    点云数据及 IMU 数据中都包含时间戳信息。

  • reflectivity
    目标反射率
    目标反射率:以 0 至 255 表示。其中 0 至 150 对应反射率介于 0 至 100% 的漫散射物体;而 151 至255 对应全反射物体。

  • x y z
    坐标信息:Livox Avia 的坐标信息可表示为直角坐标(x, y, z)或球坐标(r, θ, φ)。如果前方无被探测物体或者被探测物体超出量程范围(例如 600 m),在直角坐标系下,点云输出为(0,0,0);在球坐标系下,点云输出为(0,θ, φ)。

将 ros PointCloud2格式数据转为livox CustomMsg格式

为了使 livox_laser_simulation 功能包可以输出 CustomMsg 格式数据用于 fast-lio仿真,需要对其代码进行修改。主要是_livox_laser_simulation/src/livox_points_plugin.cpp_ 这个文件。

重新编写这个函数

void LivoxPointsPlugin::OnNewLaserScans()

前面的这些可以不变

    if (rayShape) 
    {
        std::vector<std::pair<int, AviaRotateInfo>> points_pair;
        InitializeRays(points_pair, rayShape);
        rayShape->Update();

        msgs::Set(laserMsg.mutable_time(), world->SimTime());
        msgs::LaserScan *scan = laserMsg.mutable_scan();
        InitializeScan(scan);

        SendRosTf(parentEntity->WorldPose(), world->Name(), raySensor->ParentName());

        auto rayCount = RayCount();
        auto verticalRayCount = VerticalRayCount();
        auto angle_min = AngleMin().Radian();
        auto angle_incre = AngleResolution();
        auto verticle_min = VerticalAngleMin().Radian();
        auto verticle_incre = VerticalAngleResolution();

声明要发布的 custom格式的livox 点云

        livox_ros_driver::CustomMsg pp_livox;//声明要发布的 custom格式的livox 点云

赋值点云帧头

        // 赋值点云帧头
        pp_livox.header.stamp = ros::Time::now();//时间
        pp_livox.header.frame_id = "livox";//坐标系
        int count = 0;

用 high_resolution_clock 记录 起始时间

        boost::chrono::high_resolution_clock::time_point start_time = boost::chrono::high_resolution_clock::now();

遍历每个点

        for (auto &pair : points_pair)
        {

获得点的距离

            auto range = rayShape->GetRange(pair.first);

获得点的强度

            auto intensity = rayShape->GetRetro(pair.first);

最大最小距离限制

            if (range >= RangeMax()) {
                range = 0;
            } else if (range <= RangeMin()) {
                range = 0;
            }

将极坐标转为x y z

            auto rotate_info = pair.second;
            // 将极坐标转为x y  z 
            ignition::math::Quaterniond ray;
            ray.Euler(ignition::math::Vector3d(0.0, rotate_info.zenith, rotate_info.azimuth));
            auto axis = ray * ignition::math::Vector3d(1.0, 0.0, 0.0);
            auto point = range * axis;

声明一个 Custom 点

livox_ros_driver::CustomPoint p;//声明一个 Custom 点

赋值坐标

            p.x = point.X();
            p.y = point.Y();
            p.z = point.Z();

赋值强度

 p.reflectivity = intensity;

计算当前点时间到起始时间差

            boost::chrono::high_resolution_clock::time_point end_time = boost::chrono::high_resolution_clock::now();// 当前点时间
            boost::chrono::nanoseconds elapsed_time = boost::chrono::duration_cast<boost::chrono::nanoseconds>(end_time - start_time);

赋值偏移时间

p.offset_time = elapsed_time.count();

将当前点存入点云中

pp_livox.points.push_back(p);
// 计点个数的计数器加1
count ++;
}//结束遍历每个点

赋值点云中点的个数

        pp_livox.point_num = count;

发布点云

        livox_pub.publish(pp_livox);
        ros::spinOnce();
    }

测试

将功能包进行编译,需要引入 livox_ros_driver 功能包

find_package(catkin REQUIRED COMPONENTS
  roscpp
  tf
  livox_ros_driver  # 新增
)

包含头文件

#include <livox_ros_driver/CustomMsg.h>

完成编译后启动仿真环境

产看当前话题名
可以看到这个 /livox/lidar 话题名

查看这个话题信息

rostopic info /livox/lidar

jk-jone@JKKC:~$ rostopic info /livox/lidar
Type: livox_ros_driver/CustomMsg
Publishers:
/gazebo (http://JKKC:38793/)
Subscribers: None

消息类型改成了 livox_ros_driver/CustomMsg