SLAM

激光雷达在ROS2中的使用

一、ROS2中的激光雷达数据

1、激光雷达的话题名称

ROS2中的激光雷达的节点会在指定名称的话题里发布消息。话题名称在REP-138(激光雷达 LaserScan 的通用话题、参数和诊断信息规范。)推荐为“/scan”。

2、激光雷达的消息包格式

Header headerheader中的timestamp(时间戳)表示本次扫描中接收到第一束测距激光反射信号的时间。
header中的frame_id指明了激光雷达的基准坐标系。
float32 angle_min扫描的起始角度(弧度)
float32 angle_max扫描的终止角度(弧度)
float32 angle_increment相邻两次测距的旋转夹角(弧度)
float32 time_increment相邻两次测距的时间差(秒)
float32 scan_time两次扫描的起始时间差(秒)- 扫一圈要多久
float32 range_min有效测距范围的最小距离(米)
float32 range_max有效测距范围的最大距离(米)
float32[] ranges本次扫描的所有测距值(米)
float32[] intensities所有测距的返回信号强度

二、在Rviz2中查看激光雷达数据

Rviz2 = ROS Visualization tool 2,是一个图形化工具,可以直观地查看传感器的数据。

通过

source ~/ros2_ws/install/setup.bash
rviz2

启动图形界面后,我们可以根据需要来配置一些参数,比如“单击界面左下角的[Add]按钮,在弹出的对话框中选择[LaserScan]。在RViz2的左侧显示项目里展开[LaserScan]。将其中的雷达数据话题[Topic]设置为“/scan”,将点云尺寸[Size(m)]修改为“0.03”。”

参数配置完成后,可以进行当前配置文件的保存(“Sava Config As”),保存文件的后缀为rviz。

当下次启动Rviz2时,可以通过命令:

source ~/ros2_ws/install/setup.bash
rviz2 -d 路径名称

其中“-d”表示从文件中加载RViz2的界面配置。

通常,为了配合其他节点一同启动,也可以在Launch文件中,提前配置好Rviz启动指令等。

三、激光雷达数据获取

1、编写激光雷达数据获取程序

(1)创建软件包

(2)编写节点代码

注意:lidar_data.cpp在文件夹src下。

2、设置编译规则

节点源码的编译规则写在lidar_pkg的CMakeList.txt文件里。

3、修改软件包信息

4、编译软件包

hh 在工作环境的三个软件包都被编译了。

四、项目有关/scan架构

五、基于激光雷达的避障实现

避障程序原理。程序会订阅激光雷达数据的“/scan”话题,从中接收激光雷达节点发来的sensor_msgs::LaserScan类型消息包,并解析出雷达测距值。根据雷达测距值让机器人做出反应:前方没有障碍物就前进,前方遇到障碍物就转向。将根据测距值计算出来的速度值打包成geometry_msgs::Twist类型的消息包,发布到“/cmd_vel”话题中。机器人底盘节点会从“/cmd_vel”话题获取速度数值并执行,以此来实现机器人的避障行为。

1、编写激光雷达避障程序

#include <rclcpp/rclcpp.hpp>
#include <sensor_msgs/msg/laser_scan.hpp>
#include <geometry_msgs/msg/twist.hpp>

std::shared_ptr<rclcpp::Node> node;
// 发送速度指令的发布者
rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr cmd_vel_pub;

// 对机器人转向时间进行计数
int nCount = 0;

void LidarCallback(const sensor_msgs::msg::LaserScan::SharedPtr msg)
{
    int nNum = msg->ranges.size(); // 获取激光雷达扫描数据的数量
    int nMid = nNum / 2; // 计算中间索引
    float fMidRange = msg->ranges[nMid]; // 获取中间索引对应的距离值
    RCLCPP_INFO(node->get_logger(), "Mid Range: %f", fMidRange); // 打印中间距离值

    if (nCount > 0)
    {
        nCount--;
        return;
    }

    geometry_msgs::msg::Twist cmd_vel_msg;
    if (fMidRange < 1.5f) // 如果中间距离小于1.5米,表示前方有障碍物
    {
        cmd_vel_msg.angular.z = 0.3; // 设置角速度为0.5弧度/秒,向左转
        nCount = 100; // 设置计数器,表示需要转向的时间
    }
    else
    {
        cmd_vel_msg.linear.x = 0.1; // 设置线速度为0.1米/秒,向前移动
    }
    cmd_vel_pub->publish(cmd_vel_msg);
}

int main(int argc, char *argv[])
{
    rclcpp::init(argc, argv); // 初始化ROS2客户端库
    node = std::make_shared<rclcpp::Node>("lidar_behavior_node"); // 创建一个名为“lidar_behavior_node”的节点
    
    // 创建一个发布者,发布名为“/cmd_vel”的速度指令话题
    cmd_vel_pub = node->create_publisher<geometry_msgs::msg::Twist>("/cmd_vel", 10);

    auto subscription = node->create_subscription<sensor_msgs::msg::LaserScan>(
        "/scan", 10, LidarCallback); // 创建一个订阅者,订阅名为“/scan”的激光雷达扫描数据话题,并指定回调函数

    rclcpp::spin(node); // 进入循环,等待回调函数被触发
    rclcpp::shutdown(); // 关闭ROS2客户端库
    return 0; // 返回0表示程序正常结束
}

程序逻辑图如下:

2、设置编译规则

3、修改软件包信息

4、编译软件包

六、项目有关避障架构PLAN

核心:Pi后端不需要 /scan,它有的是原始数据。

避障要做的事在 ROS 里对应什么在我的后端里对应什么
拿到扫描数据订阅 /scanlidar_controller.latest_revolution()
解析出距离解析消息里的数组frame[‘pts’] 里就是 [角度, 距离, 质量]
输出速度指令发布 /cmd_vel(机器人节点拿到后,编写驱动电机程序,实现避障。)motor_controller.drive(angle, speed)(drive函数直接编写驱动电机程序,实现避障。)

发表评论