ROS2+智能导航小车 2024年7月23日 · 30 分钟
项目四:激光雷达环境监控
结合激光雷达与摄像头,实现小车移动过程中的环境监控与拍照
在 ROS 2 中,使用激光雷达进行环境监控是一个常见且有效的应用。环境监控涉及到实时感知和分析周围环境的变化,以便做出相应的响应或决策。在自动驾驶汽车中,雷达环境检测可以实时监测周围环境,检测障碍物和其他车辆的移动,从而避免碰撞,提高行车安全性。 在工业自动化和仓库管理中,雷达检测可以防止机器人与人或物体发生碰撞,确保操作的安全性。与其他传感器(如摄像头、红外传感器)结合使用,雷达可以提供更加全面和精确的环境感知信息。
这节我们就结合雷达和摄像头来共同完成,做到移动监控拍照功能。
一、实现激光雷达环境监控
1.1 实现步骤:
- 构建一个新的功能包,包名叫做
lidar_monitor - 在软件包中新建一个节点,节点名为
lidar_monitor.py - 在节点中,申请订阅话题
/sacn,并设置回调函数为lidar_callback() - 构建回调函数
lidar_callback(),用来接收和处理雷达数据 - 显示雷达检测到的前方障碍物距离
- 如果存在前一次扫描数据
self.previous_scan,计算当前与前一次扫描数据的时间差time_diff。在时间差小于1秒的情况下,计算距离差distance_diff,检查是否有物体移动超过20厘米,记录移动方向和距离 - 如果雷达发布移动次数1s超过10次,则发布速度指令,控制机器人移动
- 调用相机拍照并保存图像
1.2 编写代码
构建一个新的功能包lidar_monitor
ros2 pkg create --build-type ament_python lidar_monitor
在lidar_monitor文件夹中新建一个文件
lidar_monitor.py
#!/usr/bin/env python3
# coding=utf-8
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import Twist
import numpy as np
import time
import os
from datetime import datetime
from pathlib import Path
import cv2 # OpenCV库
class LidarBehavior(Node):
def __init__(self):
super().__init__('lidar_monitor')
self.vel_pub = self.create_publisher(Twist, 'cmd_vel', 10)
self.lidar_sub = self.create_subscription(LaserScan, 'scan', self.lidar_callback, 10)
self.previous_scan = None
self.previous_time = None
self.angle_tolerance = np.deg2rad(10) # 角度容忍度设置为2度
self.movement_count = 0
self.capture_time = None
self.moving_to_position = False
self.capture_path = Path('/home/lll/capture')
self.ensure_capture_path()
def ensure_capture_path(self):
if not self.capture_path.exists():
self.capture_path.mkdir(parents=True)
self.get_logger().info(f"创建目录: {self.capture_path}")
def lidar_callback(self, msg):
if self.moving_to_position:
return # 如果小车正在移动到指定位置,则跳过处理
# 获取当前时间
current_time = time.time()
ranges = np.array(msg.ranges)
ranges = np.where(np.isnan(ranges), np.inf, ranges) # 将 NaN 值替换为无穷大
angle_min = msg.angle_min
angle_increment = msg.angle_increment
angles = np.arange(angle_min, angle_min + len(ranges) * angle_increment, angle_increment)
# 将角度范围转换为弧度
angle_min_rad = np.deg2rad(150)
angle_max_rad = np.deg2rad(210)
# 只取 150 到 210 度范围内的数据
front_indices = np.where((angles >= angle_min_rad) & (angles <= angle_max_rad))
front_distances = ranges[front_indices]
front_angles = angles[front_indices]
if self.previous_scan is not None and self.previous_time is not None:
time_diff = current_time - self.previous_time
if time_diff <= 1.0:
prev_distances = self.previous_scan[front_indices]
prev_angles = self.previous_angles[front_indices]
distance_diff = front_distances - prev_distances
# 检查是否有物体移动超过20厘米,并加上一个误差容忍度
distance_tolerance = 0.05 # 1厘米的误差容忍度
moved_indices = np.where(np.abs(distance_diff) > (0.2 + distance_tolerance))[0]
if len(moved_indices) > 0:
for index in moved_indices:
movement_distance = distance_diff[index]
movement_angle = front_angles[index]
previous_angle = prev_angles[index]
if np.abs(movement_angle - previous_angle) <= self.angle_tolerance:
movement_direction = "靠近" if movement_distance < 0 else "远离"
self.get_logger().info(f"检测到物体移动:方向 = {movement_direction}, 角度 = {np.rad2deg(movement_angle)} 度, 移动距离 = {np.abs(movement_distance)} 米")
self.movement_count += 1
# 更新前一次扫描数据和时间
self.previous_scan = ranges
self.previous_angles = angles
self.previous_time = current_time
# 检查是否需要移动小车到指定位置并拍照
if self.movement_count >= 10:
self.get_logger().info("检测到多次移动,移动小车到指定位置并调用相机拍照")
self.move_to_position_and_capture()
self.movement_count = 0 # 重置计数器
# 计算距离最近的目标位置
if len(front_distances) > 0 and not np.all(np.isinf(front_distances)):
min_distance_index = np.argmin(front_distances)
min_distance = front_distances[min_distance_index]
target_angle = front_angles[min_distance_index]
else:
min_distance = float('inf')
target_angle = 0.0
self.get_logger().info("目标距离 = %f 米, 目标角度 = %f 弧度" % (min_distance, target_angle))
# 根据目标距离和角度生成速度指令
vel_cmd = Twist()
target_distance = 0.5 # 目标距离
distance_tolerance = 0.05 # 允许的误差范围
if min_distance < target_distance - distance_tolerance:
vel_cmd.linear.x = -0.2 # 后退
elif min_distance > target_distance + distance_tolerance:
vel_cmd.linear.x = 0.2 # 前进
else:
vel_cmd.linear.x = 0.0 # 停止
# 设置角速度为目标角度的一部分,以便对准目标
# vel_cmd.angular.z = target_angle * 0.5
self.vel_pub.publish(vel_cmd)
def move_to_position_and_capture(self):
self.moving_to_position = True
self.get_logger().info("小车移动到指定位置")
# 在这里添加移动小车到指定位置的代码,例如通过发布特定的速度指令
# 这里假设小车移动需要3秒钟
time.sleep(3)
self.get_logger().info("小车已到达指定位置,调用相机拍照")
self.capture_image()
self.moving_to_position = False
def capture_image(self):
cap = cv2.VideoCapture(0) # 打开默认的相机
if not cap.isOpened():
self.get_logger().error("无法打开相机")
return
ret, frame = cap.read()
if ret:
image_name = self.capture_path / f"capture_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
cv2.imwrite(str(image_name), frame) # 保存图像
self.get_logger().info(f"调用相机拍照,图像保存路径: {image_name}")
else:
self.get_logger().error("无法获取相机帧")
cap.release()
def main(args=None):
rclpy.init(args=args)
node = LidarBehavior()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == "__main__":
main()
修改
package.xml
添加下面的内容
<exec_depend>rclpy</exec_depend>
<exec_depend>sensor_msgs</exec_depend>
<exec_depend>geometry_msgs</exec_depend>
修改setup.py
from setuptools import find_packages, setup
import os
from glob import glob
package_name = 'lidar_monitor'
setup(
name=package_name,
version='0.0.0',
packages=find_packages(exclude=['test']),
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
(os.path.join('share', package_name, 'lidar_monitor'), glob('lidar_monitor/*.py')),
(os.path.join('share', package_name, 'launch'), glob('launch/*.py')),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='lll',
maintainer_email='lll@todo.todo',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'lidar_monitor = lidar_monitor.lidar_monitor:main',
],
},
)
1.3 launch运行
还像上一节中一样将雷达启动节点写在一起就可以了
终端一:
cd ros2_car_ws
source install/setup.bash
ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888 -v6
终端二:
sudo chmod 666 /dev/video0 # 先给摄像头权限
colcon build
source install/setup.bash
ros2 launch lidar_monitor lidar_monitor.launch.py
1.4 拍照结果
最终会自动创建captrue文件夹用来放置图片
