激光雷达在ROS2中的使用
一、ROS2中的激光雷达数据

1、激光雷达的话题名称
ROS2中的激光雷达的节点会在指定名称的话题里发布消息。话题名称在REP-138(激光雷达 LaserScan 的通用话题、参数和诊断信息规范。)推荐为“/scan”。
2、激光雷达的消息包格式
| Header header | header中的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 里对应什么 | 在我的后端里对应什么 |
|---|---|---|
| 拿到扫描数据 | 订阅 /scan | lidar_controller.latest_revolution() |
| 解析出距离 | 解析消息里的数组 | frame[‘pts’] 里就是 [角度, 距离, 质量] |
| 输出速度指令 | 发布 /cmd_vel(机器人节点拿到后,编写驱动电机程序,实现避障。) | motor_controller.drive(angle, speed)(drive函数直接编写驱动电机程序,实现避障。) |